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 compoundmoresuffixes = 0; // allow more suffixes within compound words
52 checkcompounddup = 0; // forbid double words in compounds
53 checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution)
54 checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds
55 checkcompoundtriple = 0; // forbid compounds with triple letters
56 simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt)
57 forbiddenword = FORBIDDENWORD; // forbidden word signing flag
58 nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag
59 nongramsuggest = FLAG_NULL;
60 lang = NULL; // language
61 langnum = 0; // language code (see http://l10n.openoffice.org/languages.html)
62 needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes
63 cpdwordmax = -1; // default: unlimited wordcount in compound words
64 cpdmin = -1; // undefined
65 cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words
66 cpdvowels=NULL; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX)
67 cpdvowels_utf16=NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search)
68 cpdvowels_utf16_len=0; // vowels
69 pfxappnd=NULL; // previous prefix for counting the syllables of prefix BUG
70 sfxappnd=NULL; // previous suffix for counting a special syllables BUG
71 cpdsyllablenum=NULL; // syllable count incrementing flag
72 checknum=0; // checking numbers, and word with numbers
73 wordchars=NULL; // letters + spec. word characters
74 wordchars_utf16=NULL; // letters + spec. word characters
75 wordchars_utf16_len=0; // letters + spec. word characters
76 ignorechars=NULL; // letters + spec. word characters
77 ignorechars_utf16=NULL; // letters + spec. word characters
78 ignorechars_utf16_len=0; // letters + spec. word characters
79 version=NULL; // affix and dictionary file version string
80 havecontclass=0; // flags of possible continuing classes (double affix)
81 // LEMMA_PRESENT: not put root into the morphological output. Lemma presents
82 // in morhological description in dictionary file. It's often combined with PSEUDOROOT.
83 lemma_present = FLAG_NULL;
84 circumfix = FLAG_NULL;
85 onlyincompound = FLAG_NULL;
86 maxngramsugs = -1; // undefined
87 maxdiff = -1; // undefined
89 maxcpdsugs = -1; // undefined
97 substandard = FLAG_NULL;
103 for (int i=0; i < SETSIZE; i++) {
110 for (int j=0; j < CONTSIZE; j++) {
114 if (parse_file(affpath, key)) {
115 HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath);
118 if (cpdmin == -1) cpdmin = MINCPDLEN;
123 AffixMgr::~AffixMgr()
125 // pass through linked prefix entries and clean up
126 for (int i=0; i < SETSIZE ;i++) {
128 PfxEntry * ptr = pStart[i];
129 PfxEntry * nptr = NULL;
131 nptr = ptr->getNext();
138 // pass through linked suffix entries and clean up
139 for (int j=0; j < SETSIZE ; j++) {
141 SfxEntry * ptr = sStart[j];
142 SfxEntry * nptr = NULL;
144 nptr = ptr->getNext();
152 if (keystring) free(keystring);
154 if (trystring) free(trystring);
156 if (encoding) free(encoding);
159 for (int j=0; j < nummap; j++) {
160 for (int k=0; k < maptable[j].len; k++) {
161 if (maptable[j].set[k]) free(maptable[j].set[k]);
163 free(maptable[j].set);
164 maptable[j].set = NULL;
172 for (int j=0; j < numbreak; j++) {
173 if (breaktable[j]) free(breaktable[j]);
174 breaktable[j] = NULL;
181 for (int j=0; j < numrep; j++) {
182 free(reptable[j].pattern);
183 free(reptable[j].pattern2);
188 if (iconvtable) delete iconvtable;
189 if (oconvtable) delete oconvtable;
190 if (phone && phone->rules) {
191 for (int j=0; j < phone->num + 1; j++) {
192 free(phone->rules[j * 2]);
193 free(phone->rules[j * 2 + 1]);
201 for (int j=0; j < numdefcpd; j++) {
202 free(defcpdtable[j].def);
203 defcpdtable[j].def = NULL;
210 for (int j=0; j < numcheckcpd; j++) {
211 free(checkcpdtable[j].pattern);
212 free(checkcpdtable[j].pattern2);
213 free(checkcpdtable[j].pattern3);
214 checkcpdtable[j].pattern = NULL;
215 checkcpdtable[j].pattern2 = NULL;
216 checkcpdtable[j].pattern3 = NULL;
219 checkcpdtable = NULL;
222 FREE_FLAG(compoundflag);
223 FREE_FLAG(compoundbegin);
224 FREE_FLAG(compoundmiddle);
225 FREE_FLAG(compoundend);
226 FREE_FLAG(compoundpermitflag);
227 FREE_FLAG(compoundforbidflag);
228 FREE_FLAG(compoundroot);
229 FREE_FLAG(forbiddenword);
230 FREE_FLAG(nosuggest);
231 FREE_FLAG(nongramsuggest);
232 FREE_FLAG(needaffix);
233 FREE_FLAG(lemma_present);
234 FREE_FLAG(circumfix);
235 FREE_FLAG(onlyincompound);
241 if (cpdvowels) free(cpdvowels);
242 if (cpdvowels_utf16) free(cpdvowels_utf16);
243 if (cpdsyllablenum) free(cpdsyllablenum);
245 if (lang) free(lang);
246 if (wordchars) free(wordchars);
247 if (wordchars_utf16) free(wordchars_utf16);
248 if (ignorechars) free(ignorechars);
249 if (ignorechars_utf16) free(ignorechars_utf16);
250 if (version) free(version);
252 #ifdef MOZILLA_CLIENT
257 void AffixMgr::finishFileMgr(FileMgr *afflst)
261 // convert affix trees to sorted list
262 process_pfx_tree_to_list();
263 process_sfx_tree_to_list();
266 // read in aff file and build up prefix and suffix entry objects
267 int AffixMgr::parse_file(const char * affpath, const char * key)
269 char * line; // io buffers
270 char ft; // affix type
272 // checking flag duplication
273 char dupflags[CONTSIZE];
274 char dupflags_ini = 1;
276 // first line indicator for removing byte order mark
279 // open the affix file
280 FileMgr * afflst = new FileMgr(affpath, key);
282 HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath);
286 // step one is to parse the affix file building up the internal
287 // affix data structures
289 // read in each line ignoring any that do not
290 // start with a known line type indicator
291 while ((line = afflst->getline()) != NULL) {
294 /* remove byte order mark */
297 // Affix file begins with byte order mark: possible incompatibility with old Hunspell versions
298 if (strncmp(line,"\xEF\xBB\xBF",3) == 0) {
299 memmove(line, line+3, strlen(line+3)+1);
303 /* parse in the keyboard string */
304 if (strncmp(line,"KEY",3) == 0) {
305 if (parse_string(line, &keystring, afflst->getlinenum())) {
306 finishFileMgr(afflst);
311 /* parse in the try string */
312 if (strncmp(line,"TRY",3) == 0) {
313 if (parse_string(line, &trystring, afflst->getlinenum())) {
314 finishFileMgr(afflst);
319 /* parse in the name of the character set used by the .dict and .aff */
320 if (strncmp(line,"SET",3) == 0) {
321 if (parse_string(line, &encoding, afflst->getlinenum())) {
322 finishFileMgr(afflst);
325 if (strcmp(encoding, "UTF-8") == 0) {
327 #ifndef OPENOFFICEORG
328 #ifndef MOZILLA_CLIENT
329 if (initialize_utf_tbl()) return 1;
335 /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */
336 if (strncmp(line,"COMPLEXPREFIXES",15) == 0)
339 /* parse in the flag used by the controlled compound words */
340 if (strncmp(line,"COMPOUNDFLAG",12) == 0) {
341 if (parse_flag(line, &compoundflag, afflst)) {
342 finishFileMgr(afflst);
347 /* parse in the flag used by compound words */
348 if (strncmp(line,"COMPOUNDBEGIN",13) == 0) {
349 if (complexprefixes) {
350 if (parse_flag(line, &compoundend, afflst)) {
351 finishFileMgr(afflst);
355 if (parse_flag(line, &compoundbegin, afflst)) {
356 finishFileMgr(afflst);
362 /* parse in the flag used by compound words */
363 if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) {
364 if (parse_flag(line, &compoundmiddle, afflst)) {
365 finishFileMgr(afflst);
369 /* parse in the flag used by compound words */
370 if (strncmp(line,"COMPOUNDEND",11) == 0) {
371 if (complexprefixes) {
372 if (parse_flag(line, &compoundbegin, afflst)) {
373 finishFileMgr(afflst);
377 if (parse_flag(line, &compoundend, afflst)) {
378 finishFileMgr(afflst);
384 /* parse in the data used by compound_check() method */
385 if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) {
386 if (parse_num(line, &cpdwordmax, afflst)) {
387 finishFileMgr(afflst);
392 /* parse in the flag sign compounds in dictionary */
393 if (strncmp(line,"COMPOUNDROOT",12) == 0) {
394 if (parse_flag(line, &compoundroot, afflst)) {
395 finishFileMgr(afflst);
400 /* parse in the flag used by compound_check() method */
401 if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) {
402 if (parse_flag(line, &compoundpermitflag, afflst)) {
403 finishFileMgr(afflst);
408 /* parse in the flag used by compound_check() method */
409 if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) {
410 if (parse_flag(line, &compoundforbidflag, afflst)) {
411 finishFileMgr(afflst);
416 if (strncmp(line,"COMPOUNDMORESUFFIXES",20) == 0) {
417 compoundmoresuffixes = 1;
420 if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) {
421 checkcompounddup = 1;
424 if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) {
425 checkcompoundrep = 1;
428 if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) {
429 checkcompoundtriple = 1;
432 if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) {
433 simplifiedtriple = 1;
436 if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) {
437 checkcompoundcase = 1;
440 if (strncmp(line,"NOSUGGEST",9) == 0) {
441 if (parse_flag(line, &nosuggest, afflst)) {
442 finishFileMgr(afflst);
447 if (strncmp(line,"NONGRAMSUGGEST",14) == 0) {
448 if (parse_flag(line, &nongramsuggest, afflst)) {
449 finishFileMgr(afflst);
454 /* parse in the flag used by forbidden words */
455 if (strncmp(line,"FORBIDDENWORD",13) == 0) {
456 if (parse_flag(line, &forbiddenword, afflst)) {
457 finishFileMgr(afflst);
462 /* parse in the flag used by forbidden words */
463 if (strncmp(line,"LEMMA_PRESENT",13) == 0) {
464 if (parse_flag(line, &lemma_present, afflst)) {
465 finishFileMgr(afflst);
470 /* parse in the flag used by circumfixes */
471 if (strncmp(line,"CIRCUMFIX",9) == 0) {
472 if (parse_flag(line, &circumfix, afflst)) {
473 finishFileMgr(afflst);
478 /* parse in the flag used by fogemorphemes */
479 if (strncmp(line,"ONLYINCOMPOUND",14) == 0) {
480 if (parse_flag(line, &onlyincompound, afflst)) {
481 finishFileMgr(afflst);
486 /* parse in the flag used by `needaffixs' */
487 if (strncmp(line,"PSEUDOROOT",10) == 0) {
488 if (parse_flag(line, &needaffix, afflst)) {
489 finishFileMgr(afflst);
494 /* parse in the flag used by `needaffixs' */
495 if (strncmp(line,"NEEDAFFIX",9) == 0) {
496 if (parse_flag(line, &needaffix, afflst)) {
497 finishFileMgr(afflst);
502 /* parse in the minimal length for words in compounds */
503 if (strncmp(line,"COMPOUNDMIN",11) == 0) {
504 if (parse_num(line, &cpdmin, afflst)) {
505 finishFileMgr(afflst);
508 if (cpdmin < 1) cpdmin = 1;
511 /* parse in the max. words and syllables in compounds */
512 if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) {
513 if (parse_cpdsyllable(line, afflst)) {
514 finishFileMgr(afflst);
519 /* parse in the flag used by compound_check() method */
520 if (strncmp(line,"SYLLABLENUM",11) == 0) {
521 if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) {
522 finishFileMgr(afflst);
527 /* parse in the flag used by the controlled compound words */
528 if (strncmp(line,"CHECKNUM",8) == 0) {
532 /* parse in the extra word characters */
533 if (strncmp(line,"WORDCHARS",9) == 0) {
534 if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) {
535 finishFileMgr(afflst);
540 /* parse in the ignored characters (for example, Arabic optional diacretics charachters */
541 if (strncmp(line,"IGNORE",6) == 0) {
542 if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
543 finishFileMgr(afflst);
548 /* parse in the typical fault correcting table */
549 if (strncmp(line,"REP",3) == 0) {
550 if (parse_reptable(line, afflst)) {
551 finishFileMgr(afflst);
556 /* parse in the input conversion table */
557 if (strncmp(line,"ICONV",5) == 0) {
558 if (parse_convtable(line, afflst, &iconvtable, "ICONV")) {
559 finishFileMgr(afflst);
564 /* parse in the input conversion table */
565 if (strncmp(line,"OCONV",5) == 0) {
566 if (parse_convtable(line, afflst, &oconvtable, "OCONV")) {
567 finishFileMgr(afflst);
572 /* parse in the phonetic translation table */
573 if (strncmp(line,"PHONE",5) == 0) {
574 if (parse_phonetable(line, afflst)) {
575 finishFileMgr(afflst);
580 /* parse in the checkcompoundpattern table */
581 if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) {
582 if (parse_checkcpdtable(line, afflst)) {
583 finishFileMgr(afflst);
588 /* parse in the defcompound table */
589 if (strncmp(line,"COMPOUNDRULE",12) == 0) {
590 if (parse_defcpdtable(line, afflst)) {
591 finishFileMgr(afflst);
596 /* parse in the related character map table */
597 if (strncmp(line,"MAP",3) == 0) {
598 if (parse_maptable(line, afflst)) {
599 finishFileMgr(afflst);
604 /* parse in the word breakpoints table */
605 if (strncmp(line,"BREAK",5) == 0) {
606 if (parse_breaktable(line, afflst)) {
607 finishFileMgr(afflst);
612 /* parse in the language for language specific codes */
613 if (strncmp(line,"LANG",4) == 0) {
614 if (parse_string(line, &lang, afflst->getlinenum())) {
615 finishFileMgr(afflst);
618 langnum = get_lang_num(lang);
621 if (strncmp(line,"VERSION",7) == 0) {
622 for(line = line + 7; *line == ' ' || *line == '\t'; line++);
623 version = mystrdup(line);
626 if (strncmp(line,"MAXNGRAMSUGS",12) == 0) {
627 if (parse_num(line, &maxngramsugs, afflst)) {
628 finishFileMgr(afflst);
633 if (strncmp(line,"ONLYMAXDIFF", 11) == 0)
636 if (strncmp(line,"MAXDIFF",7) == 0) {
637 if (parse_num(line, &maxdiff, afflst)) {
638 finishFileMgr(afflst);
643 if (strncmp(line,"MAXCPDSUGS",10) == 0) {
644 if (parse_num(line, &maxcpdsugs, afflst)) {
645 finishFileMgr(afflst);
650 if (strncmp(line,"NOSPLITSUGS",11) == 0) {
654 if (strncmp(line,"FULLSTRIP",9) == 0) {
658 if (strncmp(line,"SUGSWITHDOTS",12) == 0) {
662 /* parse in the flag used by forbidden words */
663 if (strncmp(line,"KEEPCASE",8) == 0) {
664 if (parse_flag(line, &keepcase, afflst)) {
665 finishFileMgr(afflst);
670 /* parse in the flag used by `forceucase' */
671 if (strncmp(line,"FORCEUCASE",10) == 0) {
672 if (parse_flag(line, &forceucase, afflst)) {
673 finishFileMgr(afflst);
678 /* parse in the flag used by `warn' */
679 if (strncmp(line,"WARN",4) == 0) {
680 if (parse_flag(line, &warn, afflst)) {
681 finishFileMgr(afflst);
686 if (strncmp(line,"FORBIDWARN",10) == 0) {
690 /* parse in the flag used by the affix generator */
691 if (strncmp(line,"SUBSTANDARD",11) == 0) {
692 if (parse_flag(line, &substandard, afflst)) {
693 finishFileMgr(afflst);
698 if (strncmp(line,"CHECKSHARPS",11) == 0) {
702 /* parse this affix: P - prefix, S - suffix */
704 if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P';
705 if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S';
708 memset(dupflags, 0, sizeof(dupflags));
711 if (parse_affix(line, ft, afflst, dupflags)) {
712 finishFileMgr(afflst);
718 finishFileMgr(afflst);
719 // affix trees are sorted now
721 // now we can speed up performance greatly taking advantage of the
722 // relationship between the affixes and the idea of "subsets".
724 // View each prefix as a potential leading subset of another and view
725 // each suffix (reversed) as a potential trailing subset of another.
727 // To illustrate this relationship if we know the prefix "ab" is found in the
728 // word to examine, only prefixes that "ab" is a leading subset of need be examined.
729 // Furthermore is "ab" is not present then none of the prefixes that "ab" is
730 // is a subset need be examined.
731 // The same argument goes for suffix string that are reversed.
733 // Then to top this off why not examine the first char of the word to quickly
734 // limit the set of prefixes to examine (i.e. the prefixes to examine must
735 // be leading supersets of the first character of the word (if they exist)
737 // To take advantage of this "subset" relationship, we need to add two links
738 // from entry. One to take next if the current prefix is found (call it nexteq)
739 // and one to take next if the current prefix is not found (call it nextne).
741 // Since we have built ordered lists, all that remains is to properly initialize
742 // the nextne and nexteq pointers that relate them
747 /* get encoding for CHECKCOMPOUNDCASE */
749 char * enc = get_encoding();
750 csconv = get_current_cs(enc);
756 strcpy(expw, wordchars);
760 for (int i = 0; i <= 255; i++) {
761 if ( (csconv[i].cupper != csconv[i].clower) &&
762 (! strchr(expw, (char) i))) {
763 *(expw + strlen(expw) + 1) = '\0';
764 *(expw + strlen(expw)) = (char) i;
768 wordchars = mystrdup(expw);
771 // default BREAK definition
772 if (numbreak == -1) {
773 breaktable = (char **) malloc(sizeof(char *) * 3);
774 if (!breaktable) return 1;
775 breaktable[0] = mystrdup("-");
776 breaktable[1] = mystrdup("^-");
777 breaktable[2] = mystrdup("-$");
778 if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3;
784 // we want to be able to quickly access prefix information
785 // both by prefix flag, and sorted by prefix string itself
786 // so we need to set up two indexes
788 int AffixMgr::build_pfxtree(PfxEntry* pfxptr)
792 PfxEntry * ep = pfxptr;
794 // get the right starting points
795 const char * key = ep->getKey();
796 const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
798 // first index by flag which must exist
804 // handle the special case of null affix string
805 if (strlen(key) == 0) {
806 // always inset them at head of list at element 0
813 // now handle the normal case
817 unsigned char sp = *((const unsigned char *)key);
820 // handle the first insert
827 // otherwise use binary tree insertion so that a sorted
828 // list can easily be generated later
832 if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
833 ptr = ptr->getNextEQ();
839 ptr = ptr->getNextNE();
849 // we want to be able to quickly access suffix information
850 // both by suffix flag, and sorted by the reverse of the
851 // suffix string itself; so we need to set up two indexes
852 int AffixMgr::build_sfxtree(SfxEntry* sfxptr)
856 SfxEntry * ep = sfxptr;
858 /* get the right starting point */
859 const char * key = ep->getKey();
860 const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
862 // first index by flag which must exist
867 // next index by affix string
869 // handle the special case of null affix string
870 if (strlen(key) == 0) {
871 // always inset them at head of list at element 0
878 // now handle the normal case
882 unsigned char sp = *((const unsigned char *)key);
885 // handle the first insert
891 // otherwise use binary tree insertion so that a sorted
892 // list can easily be generated later
896 if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
897 ptr = ptr->getNextEQ();
903 ptr = ptr->getNextNE();
913 // convert from binary tree to sorted list
914 int AffixMgr::process_pfx_tree_to_list()
916 for (int i=1; i< SETSIZE; i++) {
917 pStart[i] = process_pfx_in_order(pStart[i],NULL);
923 PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr)
926 nptr = process_pfx_in_order(ptr->getNextNE(), nptr);
928 nptr = process_pfx_in_order(ptr->getNextEQ(), ptr);
934 // convert from binary tree to sorted list
935 int AffixMgr:: process_sfx_tree_to_list()
937 for (int i=1; i< SETSIZE; i++) {
938 sStart[i] = process_sfx_in_order(sStart[i],NULL);
943 SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr)
946 nptr = process_sfx_in_order(ptr->getNextNE(), nptr);
948 nptr = process_sfx_in_order(ptr->getNextEQ(), ptr);
954 // reinitialize the PfxEntry links NextEQ and NextNE to speed searching
955 // using the idea of leading subsets this time
956 int AffixMgr::process_pfx_order()
960 // loop through each prefix list starting point
961 for (int i=1; i < SETSIZE; i++) {
965 // look through the remainder of the list
966 // and find next entry with affix that
967 // the current one is not a subset of
968 // mark that as destination for NextNE
969 // use next in list that you are a subset
972 for (; ptr != NULL; ptr = ptr->getNext()) {
974 PfxEntry * nptr = ptr->getNext();
975 for (; nptr != NULL; nptr = nptr->getNext()) {
976 if (! isSubset( ptr->getKey() , nptr->getKey() )) break;
978 ptr->setNextNE(nptr);
979 ptr->setNextEQ(NULL);
980 if ((ptr->getNext()) && isSubset(ptr->getKey() , (ptr->getNext())->getKey()))
981 ptr->setNextEQ(ptr->getNext());
984 // now clean up by adding smart search termination strings:
985 // if you are already a superset of the previous prefix
986 // but not a subset of the next, search can end here
987 // so set NextNE properly
990 for (; ptr != NULL; ptr = ptr->getNext()) {
991 PfxEntry * nptr = ptr->getNext();
992 PfxEntry * mptr = NULL;
993 for (; nptr != NULL; nptr = nptr->getNext()) {
994 if (! isSubset(ptr->getKey(),nptr->getKey())) break;
997 if (mptr) mptr->setNextNE(NULL);
1003 // initialize the SfxEntry links NextEQ and NextNE to speed searching
1004 // using the idea of leading subsets this time
1005 int AffixMgr::process_sfx_order()
1009 // loop through each prefix list starting point
1010 for (int i=1; i < SETSIZE; i++) {
1014 // look through the remainder of the list
1015 // and find next entry with affix that
1016 // the current one is not a subset of
1017 // mark that as destination for NextNE
1018 // use next in list that you are a subset
1021 for (; ptr != NULL; ptr = ptr->getNext()) {
1022 SfxEntry * nptr = ptr->getNext();
1023 for (; nptr != NULL; nptr = nptr->getNext()) {
1024 if (! isSubset(ptr->getKey(),nptr->getKey())) break;
1026 ptr->setNextNE(nptr);
1027 ptr->setNextEQ(NULL);
1028 if ((ptr->getNext()) && isSubset(ptr->getKey(),(ptr->getNext())->getKey()))
1029 ptr->setNextEQ(ptr->getNext());
1033 // now clean up by adding smart search termination strings:
1034 // if you are already a superset of the previous suffix
1035 // but not a subset of the next, search can end here
1036 // so set NextNE properly
1039 for (; ptr != NULL; ptr = ptr->getNext()) {
1040 SfxEntry * nptr = ptr->getNext();
1041 SfxEntry * mptr = NULL;
1042 for (; nptr != NULL; nptr = nptr->getNext()) {
1043 if (! isSubset(ptr->getKey(),nptr->getKey())) break;
1046 if (mptr) mptr->setNextNE(NULL);
1052 // add flags to the result for dictionary debugging
1053 void AffixMgr::debugflag(char * result, unsigned short flag) {
1054 char * st = encode_flag(flag);
1055 mystrcat(result, " ", MAXLNLEN);
1056 mystrcat(result, MORPH_FLAG, MAXLNLEN);
1058 mystrcat(result, st, MAXLNLEN);
1063 // calculate the character length of the condition
1064 int AffixMgr::condlen(char * st)
1072 } else if (*st == ']') group = false;
1073 else if (!group && (!utf8 ||
1074 (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++;
1079 int AffixMgr::encodeit(affentry &entry, char * cs)
1081 if (strcmp(cs,".") != 0) {
1082 entry.numconds = (char) condlen(cs);
1083 strncpy(entry.c.conds, cs, MAXCONDLEN);
1084 // long condition (end of conds padded by strncpy)
1085 if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) {
1086 entry.opts += aeLONGCOND;
1087 entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1);
1088 if (!entry.c.l.conds2) return 1;
1092 entry.c.conds[0] = '\0';
1097 // return 1 if s1 is a leading subset of s2 (dots are for infixes)
1098 inline int AffixMgr::isSubset(const char * s1, const char * s2)
1100 while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) {
1104 return (*s1 == '\0');
1108 // check word for prefixes
1109 struct hentry * AffixMgr::prefix_check(const char * word, int len, char in_compound,
1110 const FLAG needflag)
1112 struct hentry * rv= NULL;
1118 // first handle the special case of 0 length prefixes
1119 PfxEntry * pe = pStart[0];
1123 ((in_compound != IN_CPD_NOT) || !(pe->getCont() &&
1124 (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) &&
1125 // permit prefixes in compounds
1126 ((in_compound != IN_CPD_END) || (pe->getCont() &&
1127 (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen()))))
1130 rv = pe->checkword(word, len, in_compound, needflag);
1132 pfx=pe; // BUG: pfx not stateless
1139 // now handle the general case
1140 unsigned char sp = *((const unsigned char *)word);
1141 PfxEntry * pptr = pStart[sp];
1144 if (isSubset(pptr->getKey(),word)) {
1147 ((in_compound != IN_CPD_NOT) || !(pptr->getCont() &&
1148 (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) &&
1149 // permit prefixes in compounds
1150 ((in_compound != IN_CPD_END) || (pptr->getCont() &&
1151 (TESTAFF(pptr->getCont(), compoundpermitflag, pptr->getContLen()))))
1154 rv = pptr->checkword(word, len, in_compound, needflag);
1156 pfx=pptr; // BUG: pfx not stateless
1160 pptr = pptr->getNextEQ();
1162 pptr = pptr->getNextNE();
1169 // check word for prefixes
1170 struct hentry * AffixMgr::prefix_check_twosfx(const char * word, int len,
1171 char in_compound, const FLAG needflag)
1173 struct hentry * rv= NULL;
1178 // first handle the special case of 0 length prefixes
1179 PfxEntry * pe = pStart[0];
1182 rv = pe->check_twosfx(word, len, in_compound, needflag);
1187 // now handle the general case
1188 unsigned char sp = *((const unsigned char *)word);
1189 PfxEntry * pptr = pStart[sp];
1192 if (isSubset(pptr->getKey(),word)) {
1193 rv = pptr->check_twosfx(word, len, in_compound, needflag);
1198 pptr = pptr->getNextEQ();
1200 pptr = pptr->getNextNE();
1207 // check word for prefixes
1208 char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound,
1209 const FLAG needflag)
1213 char result[MAXLNLEN];
1219 // first handle the special case of 0 length prefixes
1220 PfxEntry * pe = pStart[0];
1222 st = pe->check_morph(word,len,in_compound, needflag);
1224 mystrcat(result, st, MAXLNLEN);
1227 // if (rv) return rv;
1231 // now handle the general case
1232 unsigned char sp = *((const unsigned char *)word);
1233 PfxEntry * pptr = pStart[sp];
1236 if (isSubset(pptr->getKey(),word)) {
1237 st = pptr->check_morph(word,len,in_compound, needflag);
1240 if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() &&
1241 (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) {
1242 mystrcat(result, st, MAXLNLEN);
1247 pptr = pptr->getNextEQ();
1249 pptr = pptr->getNextNE();
1253 if (*result) return mystrdup(result);
1258 // check word for prefixes
1259 char * AffixMgr::prefix_check_twosfx_morph(const char * word, int len,
1260 char in_compound, const FLAG needflag)
1264 char result[MAXLNLEN];
1270 // first handle the special case of 0 length prefixes
1271 PfxEntry * pe = pStart[0];
1273 st = pe->check_twosfx_morph(word,len,in_compound, needflag);
1275 mystrcat(result, st, MAXLNLEN);
1281 // now handle the general case
1282 unsigned char sp = *((const unsigned char *)word);
1283 PfxEntry * pptr = pStart[sp];
1286 if (isSubset(pptr->getKey(),word)) {
1287 st = pptr->check_twosfx_morph(word, len, in_compound, needflag);
1289 mystrcat(result, st, MAXLNLEN);
1293 pptr = pptr->getNextEQ();
1295 pptr = pptr->getNextNE();
1299 if (*result) return mystrdup(result);
1303 // Is word a non compound with a REP substitution (see checkcompoundrep)?
1304 int AffixMgr::cpdrep_check(const char * word, int wl)
1306 char candidate[MAXLNLEN];
1310 if ((wl < 2) || !numrep) return 0;
1312 for (int i=0; i < numrep; i++ ) {
1314 lenr = strlen(reptable[i].pattern2);
1315 lenp = strlen(reptable[i].pattern);
1316 // search every occurence of the pattern in the word
1317 while ((r=strstr(r, reptable[i].pattern)) != NULL) {
1318 strcpy(candidate, word);
1319 if (r-word + lenr + strlen(r+lenp) >= MAXLNLEN) break;
1320 strcpy(candidate+(r-word),reptable[i].pattern2);
1321 strcpy(candidate+(r-word)+lenr, r+lenp);
1322 if (candidate_check(candidate,strlen(candidate))) return 1;
1323 r++; // search for the next letter
1329 // forbid compoundings when there are special patterns at word bound
1330 int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2, const char /*affixed*/)
1333 for (int i = 0; i < numcheckcpd; i++) {
1334 if (isSubset(checkcpdtable[i].pattern2, word + pos) &&
1335 (!r1 || !checkcpdtable[i].cond ||
1336 (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) &&
1337 (!r2 || !checkcpdtable[i].cond2 ||
1338 (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) &&
1339 // zero length pattern => only TESTAFF
1340 // zero pattern (0/flag) => unmodified stem (zero affixes allowed)
1341 (!*(checkcpdtable[i].pattern) || (
1342 (*(checkcpdtable[i].pattern)=='0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) ||
1343 (*(checkcpdtable[i].pattern)!='0' && ((len = strlen(checkcpdtable[i].pattern)) != 0) &&
1344 strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) {
1351 // forbid compounding with neighbouring upper and lower case characters at word bounds
1352 int AffixMgr::cpdcase_check(const char * word, int pos)
1357 u8_u16(&u, 1, word + pos);
1358 for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--);
1360 unsigned short a = (u.h << 8) + u.l;
1361 unsigned short b = (w.h << 8) + w.l;
1362 if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) &&
1363 (a != '-') && (b != '-')) return 1;
1365 unsigned char a = *(word + pos - 1);
1366 unsigned char b = *(word + pos);
1367 if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1;
1372 // check compound patterns
1373 int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all)
1375 signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking
1376 signed short btwp[MAXWORDLEN]; // word positions for metacharacters
1377 int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions
1392 (*words)[wnum] = rv;
1394 // has the last word COMPOUNDRULE flag?
1395 if (rv->alen == 0) {
1396 (*words)[wnum] = NULL;
1397 if (w) *words = NULL;
1401 for (i = 0; i < numdefcpd; i++) {
1402 for (j = 0; j < defcpdtable[i].len; j++) {
1403 if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' &&
1404 TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) {
1411 (*words)[wnum] = NULL;
1412 if (w) *words = NULL;
1416 for (i = 0; i < numdefcpd; i++) {
1417 signed short pp = 0; // pattern position
1418 signed short wp = 0; // "words" position
1423 while ((pp < defcpdtable[i].len) && (wp <= wnum)) {
1424 if (((pp+1) < defcpdtable[i].len) &&
1425 ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) {
1426 int wend = (defcpdtable[i].def[pp+1] == '?') ? wp : wnum;
1431 while (wp <= wend) {
1432 if (!(*words)[wp]->alen ||
1433 !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp-2], (*words)[wp]->alen)) {
1439 if (wp <= wnum) ok2 = 0;
1440 btnum[bt] = wp - btwp[bt];
1441 if (btnum[bt] > 0) bt++;
1445 if (!(*words)[wp] || !(*words)[wp]->alen ||
1446 !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], (*words)[wp]->alen)) {
1452 if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0;
1457 while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) &&
1458 ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2;
1459 if (defcpdtable[i].len <= r) return 1;
1466 wp = btwp[bt - 1] + (signed short) btnum[bt - 1];
1467 } while ((btnum[bt - 1] < 0) && --bt);
1470 if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1;
1472 // check zero ending
1473 while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) &&
1474 ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2;
1475 if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1;
1477 (*words)[wnum] = NULL;
1478 if (w) *words = NULL;
1482 inline int AffixMgr::candidate_check(const char * word, int len)
1484 struct hentry * rv=NULL;
1489 // rv = prefix_check(word,len,1);
1490 // if (rv) return 1;
1492 rv = affix_check(word,len);
1497 // calculate number of syllable for compound-checking
1498 short AffixMgr::get_syllable(const char * word, int wlen)
1500 if (cpdmaxsyllable==0) return 0;
1505 for (int i=0; i<wlen; i++) {
1506 if (strchr(cpdvowels, word[i])) num++;
1508 } else if (cpdvowels_utf16) {
1509 w_char w[MAXWORDUTF8LEN];
1510 int i = u8_u16(w, MAXWORDUTF8LEN, word);
1511 for (; i > 0; i--) {
1512 if (flag_bsearch((unsigned short *) cpdvowels_utf16,
1513 ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++;
1519 void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) {
1522 for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) {
1523 for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++);
1525 for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) {
1526 for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--);
1530 *cmax = len - cpdmin + 1;
1535 // check if compound word is correctly spelled
1536 // hu_mov_rule = spec. Hungarian rule (XXX)
1537 struct hentry * AffixMgr::compound_check(const char * word, int len,
1538 short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL,
1539 char hu_mov_rule = 0, char is_sug = 0, int * info = NULL)
1542 short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
1543 struct hentry * rv = NULL;
1544 struct hentry * rv_first;
1545 struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
1546 char st [MAXWORDUTF8LEN + 4];
1556 int checkedstriple = 0;
1559 hentry ** oldwords = words;
1563 setcminmax(&cmin, &cmax, word, len);
1567 for (i = cmin; i < cmax; i++) {
1568 // go to end of the UTF-8 character
1570 for (; (st[i] & 0xc0) == 0x80; i++);
1571 if (i >= cmax) return NULL;
1575 onlycpdrule = (words) ? 1 : 0;
1577 do { // onlycpdrule loop
1579 oldnumsyllable = numsyllable;
1580 oldwordnum = wordnum;
1584 do { // simplified checkcompoundpattern loop
1587 for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 ||
1588 strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++);
1590 if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop
1591 strcpy(st + i, checkcpdtable[scpd-1].pattern);
1593 i += strlen(checkcpdtable[scpd-1].pattern);
1594 strcpy(st + i, checkcpdtable[scpd-1].pattern2);
1595 strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3));
1598 len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3);
1601 setcminmax(&cmin, &cmax, st, len);
1603 cmax = len - cpdmin + 1;
1615 rv = lookup(st); // perhaps without prefix
1617 // search homonym with compound flag
1618 while ((rv) && !hu_mov_rule &&
1619 ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
1620 !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1621 (compoundbegin && !wordnum && !onlycpdrule &&
1622 TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
1623 (compoundmiddle && wordnum && !words && !onlycpdrule &&
1624 TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
1625 (numdefcpd && onlycpdrule &&
1626 ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
1627 (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))) ||
1628 (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL &&
1629 !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)))
1631 rv = rv->next_homonym;
1634 if (rv) affixed = 0;
1637 if (onlycpdrule) break;
1639 !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
1640 if (((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
1641 FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
1642 (compoundmoresuffixes && (rv = suffix_check_twosfx(st, i, 0, NULL, compoundflag)))) && !hu_mov_rule &&
1644 ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag,
1645 sfx->getContLen())) || (compoundend &&
1646 TESTAFF(sfx->getCont(), compoundend,
1647 sfx->getContLen())))) {
1653 (((wordnum == 0) && compoundbegin &&
1654 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
1655 (compoundmoresuffixes && (rv = suffix_check_twosfx(st, i, 0, NULL, compoundbegin))) || // twofold suffixes + compound
1656 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
1657 ((wordnum > 0) && compoundmiddle &&
1658 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
1659 (compoundmoresuffixes && (rv = suffix_check_twosfx(st, i, 0, NULL, compoundmiddle))) || // twofold suffixes + compound
1660 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
1661 ) checked_prefix = 1;
1662 // else check forbiddenwords and needaffix
1663 } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1664 TESTAFF(rv->astr, needaffix, rv->alen) ||
1665 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1666 (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen))
1673 // check non_compound flag in suffix and prefix
1674 if ((rv) && !hu_mov_rule &&
1675 ((pfx && pfx->getCont() &&
1676 TESTAFF(pfx->getCont(), compoundforbidflag,
1677 pfx->getContLen())) ||
1678 (sfx && sfx->getCont() &&
1679 TESTAFF(sfx->getCont(), compoundforbidflag,
1680 sfx->getContLen())))) {
1684 // check compoundend flag in suffix and prefix
1685 if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
1686 ((pfx && pfx->getCont() &&
1687 TESTAFF(pfx->getCont(), compoundend,
1688 pfx->getContLen())) ||
1689 (sfx && sfx->getCont() &&
1690 TESTAFF(sfx->getCont(), compoundend,
1691 sfx->getContLen())))) {
1695 // check compoundmiddle flag in suffix and prefix
1696 if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
1697 ((pfx && pfx->getCont() &&
1698 TESTAFF(pfx->getCont(), compoundmiddle,
1699 pfx->getContLen())) ||
1700 (sfx && sfx->getCont() &&
1701 TESTAFF(sfx->getCont(), compoundmiddle,
1702 sfx->getContLen())))) {
1706 // check forbiddenwords
1707 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1708 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1709 (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
1713 // increment word number, if the second root has a compoundroot flag
1714 if ((rv) && compoundroot &&
1715 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1719 // first word is acceptable in compound words?
1721 ( checked_prefix || (words && words[wnum]) ||
1722 (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1723 ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
1724 ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))// ||
1727 // LANG_hu section: spec. Hungarian rule
1728 || ((langnum == LANG_hu) && hu_mov_rule && (
1729 TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes
1730 TESTAFF(rv->astr, 'G', rv->alen) ||
1731 TESTAFF(rv->astr, 'H', rv->alen)
1734 // END of LANG_hu section
1737 // test CHECKCOMPOUNDPATTERN conditions
1738 scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL ||
1739 TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)
1741 && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters
1742 (word[i-1]==word[i]) && (
1743 ((i>1) && (word[i-1]==word[i-2])) ||
1744 ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
1748 checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i)
1751 // LANG_hu section: spec. Hungarian rule
1752 || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
1753 (sfx && sfx->getCont() && ( // XXX hardwired Hungarian dic. codes
1754 TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
1755 TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
1759 ) { // first word is ok condition
1761 // LANG_hu section: spec. Hungarian rule
1762 if (langnum == LANG_hu) {
1763 // calculate syllable number of the word
1764 numsyllable += get_syllable(st, i);
1765 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
1766 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
1768 // END of LANG_hu section
1774 do { // striple loop
1776 // check simplifiedtriple
1777 if (simplifiedtriple) {
1780 i--; // check "fahrt" instead of "ahrt" in "Schiffahrt"
1781 } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1;
1784 rv = lookup((st+i)); // perhaps without prefix
1786 // search homonym with compound flag
1787 while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
1788 !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1789 (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
1790 (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) ||
1791 (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL &&
1792 !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
1794 rv = rv->next_homonym;
1798 if (rv && forceucase && (rv) &&
1799 (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL;
1801 if (rv && words && words[wnum + 1]) return rv_first;
1803 oldnumsyllable2 = numsyllable;
1804 oldwordnum2 = wordnum;
1807 // LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code
1808 if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
1811 // END of LANG_hu section
1813 // increment word number, if the second root has a compoundroot flag
1814 if ((rv) && (compoundroot) &&
1815 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1819 // check forbiddenwords
1820 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1821 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1822 (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
1824 // second word is acceptable, as a root?
1825 // hungarian conventions: compounding is acceptable,
1826 // when compound forms consist of 2 words, or if more,
1827 // then the syllable number of root words must be 6, or lesser.
1830 (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1831 (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
1834 ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
1835 ((cpdmaxsyllable!=0) &&
1836 (numsyllable + get_syllable(HENTRY_WORD(rv), rv->clen)<=cpdmaxsyllable))
1839 // test CHECKCOMPOUNDPATTERN
1840 !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv, 0)
1843 (!checkcompounddup || (rv != rv_first))
1845 // test CHECKCOMPOUNDPATTERN conditions
1846 && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
1847 TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
1850 // forbid compound word, if it is a non compound word with typical fault
1851 if (checkcompoundrep && cpdrep_check(word,len)) return NULL;
1855 numsyllable = oldnumsyllable2;
1856 wordnum = oldwordnum2;
1858 // perhaps second word has prefix or/and suffix
1860 sfxflag = FLAG_NULL;
1861 rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL;
1862 if (!rv && compoundend && !onlycpdrule) {
1865 rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END);
1868 if (!rv && numdefcpd && words) {
1869 rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
1870 if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first;
1874 // test CHECKCOMPOUNDPATTERN conditions (allowed forms)
1875 if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
1876 TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL;
1878 // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds)
1879 if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL;
1881 // check non_compound flag in suffix and prefix
1883 ((pfx && pfx->getCont() &&
1884 TESTAFF(pfx->getCont(), compoundforbidflag,
1885 pfx->getContLen())) ||
1886 (sfx && sfx->getCont() &&
1887 TESTAFF(sfx->getCont(), compoundforbidflag,
1888 sfx->getContLen())))) {
1893 if (rv && forceucase && (rv) &&
1894 (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL;
1896 // check forbiddenwords
1897 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1898 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1899 (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
1901 // pfxappnd = prefix of word+i, or NULL
1902 // calculate syllable number of prefix.
1903 // hungarian convention: when syllable number of prefix is more,
1904 // than 1, the prefix+word counts as two words.
1906 if (langnum == LANG_hu) {
1907 // calculate syllable number of the word
1908 numsyllable += get_syllable(word + i, strlen(word + i));
1910 // - affix syllable num.
1911 // XXX only second suffix (inflections, not derivations)
1913 char * tmp = myrevstrdup(sfxappnd);
1914 numsyllable -= get_syllable(tmp, strlen(tmp));
1918 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
1919 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
1921 // increment syllable num, if last word has a SYLLABLENUM flag
1922 // and the suffix is beginning `s'
1924 if (cpdsyllablenum) {
1926 case 'c': { numsyllable+=2; break; }
1927 case 'J': { numsyllable += 1; break; }
1928 case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
1933 // increment word number, if the second word has a compoundroot flag
1934 if ((rv) && (compoundroot) &&
1935 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1939 // second word is acceptable, as a word with prefix or/and suffix?
1940 // hungarian conventions: compounding is acceptable,
1941 // when compound forms consist 2 word, otherwise
1942 // the syllable number of root words is 6, or lesser.
1945 ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) ||
1946 ((cpdmaxsyllable != 0) &&
1947 (numsyllable <= cpdmaxsyllable))
1950 (!checkcompounddup || (rv != rv_first))
1952 // forbid compound word, if it is a non compound word with typical fault
1953 if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
1957 numsyllable = oldnumsyllable2;
1958 wordnum = oldwordnum2;
1960 // perhaps second word is a compound word (recursive call)
1961 if (wordnum < maxwordnum) {
1962 rv = compound_check((st+i),strlen(st+i), wordnum+1,
1963 numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info);
1965 if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) ||
1966 (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL;
1971 // forbid compound word, if it is a non compound word with typical fault
1972 if (checkcompoundrep || forbiddenword) {
1973 struct hentry * rv2 = NULL;
1975 if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
1978 if (strncmp(rv->word, word + i, rv->blen) == 0) {
1979 char r = *(st + i + rv->blen);
1980 *(st + i + rv->blen) = '\0';
1982 if (checkcompoundrep && cpdrep_check(st, i + rv->blen)) {
1983 *(st + i + rv->blen) = r;
1987 if (forbiddenword) {
1989 if (!rv2) rv2 = affix_check(word, len);
1990 if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) &&
1991 (strncmp(rv2->word, st, i + rv->blen) == 0)) {
1995 *(st + i + rv->blen) = r;
2000 } while (striple && !checkedstriple); // end of striple loop
2002 if (checkedstriple) {
2008 } // first word is ok condition
2020 } while (!onlycpdrule && simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop
2023 wordnum = oldwordnum;
2024 numsyllable = oldnumsyllable;
2028 strcpy(st, word); // XXX add more optim.
2032 } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
2039 // check if compound word is correctly spelled
2040 // hu_mov_rule = spec. Hungarian rule (XXX)
2041 int AffixMgr::compound_check_morph(const char * word, int len,
2042 short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words,
2043 char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL)
2046 short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
2049 struct hentry * rv = NULL;
2050 struct hentry * rv_first;
2051 struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
2052 char st [MAXWORDUTF8LEN + 4];
2056 char presult[MAXLNLEN];
2063 hentry ** oldwords = words;
2065 setcminmax(&cmin, &cmax, word, len);
2069 for (i = cmin; i < cmax; i++) {
2070 oldnumsyllable = numsyllable;
2071 oldwordnum = wordnum;
2074 // go to end of the UTF-8 character
2076 for (; (st[i] & 0xc0) == 0x80; i++);
2077 if (i >= cmax) return 0;
2081 onlycpdrule = (words) ? 1 : 0;
2083 do { // onlycpdrule loop
2085 oldnumsyllable = numsyllable;
2086 oldwordnum = wordnum;
2098 if (partresult) mystrcat(presult, partresult, MAXLNLEN);
2100 rv = lookup(st); // perhaps without prefix
2102 // search homonym with compound flag
2103 while ((rv) && !hu_mov_rule &&
2104 ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
2105 !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2106 (compoundbegin && !wordnum && !onlycpdrule &&
2107 TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
2108 (compoundmiddle && wordnum && !words && !onlycpdrule &&
2109 TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
2110 (numdefcpd && onlycpdrule &&
2111 ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
2112 (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))
2114 rv = rv->next_homonym;
2117 if (rv) affixed = 0;
2120 sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st);
2121 if (!HENTRY_FIND(rv, MORPH_STEM)) {
2122 sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st);
2124 // store the pointer of the hash entry
2125 // sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv);
2126 if (HENTRY_DATA(rv)) {
2127 sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv));
2132 if (onlycpdrule && strlen(*result) > MAXLNLEN/10) break;
2134 !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
2135 if (((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
2136 FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
2137 (compoundmoresuffixes && (rv = suffix_check_twosfx(st, i, 0, NULL, compoundflag)))) && !hu_mov_rule &&
2139 ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag,
2140 sfx->getContLen())) || (compoundend &&
2141 TESTAFF(sfx->getCont(), compoundend,
2142 sfx->getContLen())))) {
2148 (((wordnum == 0) && compoundbegin &&
2149 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
2150 (compoundmoresuffixes && (rv = suffix_check_twosfx(st, i, 0, NULL, compoundbegin))) || // twofold suffix+compound
2151 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
2152 ((wordnum > 0) && compoundmiddle &&
2153 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
2154 (compoundmoresuffixes && (rv = suffix_check_twosfx(st, i, 0, NULL, compoundmiddle))) || // twofold suffix+compound
2155 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
2157 // char * p = prefix_check_morph(st, i, 0, compound);
2159 if (compoundflag) p = affix_check_morph(st, i, compoundflag);
2160 if (!p || (*p == '\0')) {
2163 if ((wordnum == 0) && compoundbegin) {
2164 p = affix_check_morph(st, i, compoundbegin);
2165 } else if ((wordnum > 0) && compoundmiddle) {
2166 p = affix_check_morph(st, i, compoundmiddle);
2169 if (p && (*p != '\0')) {
2170 sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD,
2171 MORPH_PART, st, line_uniq_app(&p, MSEP_REC));
2176 // else check forbiddenwords
2177 } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
2178 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
2179 TESTAFF(rv->astr, needaffix, rv->alen))) {
2184 // check non_compound flag in suffix and prefix
2185 if ((rv) && !hu_mov_rule &&
2186 ((pfx && pfx->getCont() &&
2187 TESTAFF(pfx->getCont(), compoundforbidflag,
2188 pfx->getContLen())) ||
2189 (sfx && sfx->getCont() &&
2190 TESTAFF(sfx->getCont(), compoundforbidflag,
2191 sfx->getContLen())))) {
2195 // check compoundend flag in suffix and prefix
2196 if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
2197 ((pfx && pfx->getCont() &&
2198 TESTAFF(pfx->getCont(), compoundend,
2199 pfx->getContLen())) ||
2200 (sfx && sfx->getCont() &&
2201 TESTAFF(sfx->getCont(), compoundend,
2202 sfx->getContLen())))) {
2206 // check compoundmiddle flag in suffix and prefix
2207 if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
2208 ((pfx && pfx->getCont() &&
2209 TESTAFF(pfx->getCont(), compoundmiddle,
2210 pfx->getContLen())) ||
2211 (sfx && sfx->getCont() &&
2212 TESTAFF(sfx->getCont(), compoundmiddle,
2213 sfx->getContLen())))) {
2217 // check forbiddenwords
2218 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen)
2219 || TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) continue;
2221 // increment word number, if the second root has a compoundroot flag
2222 if ((rv) && (compoundroot) &&
2223 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2227 // first word is acceptable in compound words?
2229 ( checked_prefix || (words && words[wnum]) ||
2230 (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2231 ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
2232 ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))
2233 // LANG_hu section: spec. Hungarian rule
2234 || ((langnum == LANG_hu) && // hu_mov_rule
2236 TESTAFF(rv->astr, 'F', rv->alen) ||
2237 TESTAFF(rv->astr, 'G', rv->alen) ||
2238 TESTAFF(rv->astr, 'H', rv->alen)
2241 // END of LANG_hu section
2243 && ! (( checkcompoundtriple && !words && // test triple letters
2244 (word[i-1]==word[i]) && (
2245 ((i>1) && (word[i-1]==word[i-2])) ||
2246 ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
2250 // test CHECKCOMPOUNDPATTERN
2251 numcheckcpd && !words && cpdpat_check(word, i, rv, NULL, affixed)
2254 checkcompoundcase && !words && cpdcase_check(word, i)
2257 // LANG_hu section: spec. Hungarian rule
2258 || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
2259 (sfx && sfx->getCont() && (
2260 TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
2261 TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
2265 // END of LANG_hu section
2268 // LANG_hu section: spec. Hungarian rule
2269 if (langnum == LANG_hu) {
2270 // calculate syllable number of the word
2271 numsyllable += get_syllable(st, i);
2273 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
2274 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
2276 // END of LANG_hu section
2280 rv = lookup((word+i)); // perhaps without prefix
2282 // search homonym with compound flag
2283 while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
2284 !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2285 (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
2286 (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) {
2287 rv = rv->next_homonym;
2290 if (rv && words && words[wnum + 1]) {
2291 mystrcat(*result, presult, MAXLNLEN);
2292 mystrcat(*result, " ", MAXLNLEN);
2293 mystrcat(*result, MORPH_PART, MAXLNLEN);
2294 mystrcat(*result, word+i, MAXLNLEN);
2295 if (complexprefixes && HENTRY_DATA(rv)) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2296 if (!HENTRY_FIND(rv, MORPH_STEM)) {
2297 mystrcat(*result, " ", MAXLNLEN);
2298 mystrcat(*result, MORPH_STEM, MAXLNLEN);
2299 mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
2301 // store the pointer of the hash entry
2302 // sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
2303 if (!complexprefixes && HENTRY_DATA(rv)) {
2304 mystrcat(*result, " ", MAXLNLEN);
2305 mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2307 mystrcat(*result, "\n", MAXLNLEN);
2312 oldnumsyllable2 = numsyllable;
2313 oldwordnum2 = wordnum;
2315 // LANG_hu section: spec. Hungarian rule
2316 if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
2319 // END of LANG_hu section
2320 // increment word number, if the second root has a compoundroot flag
2321 if ((rv) && (compoundroot) &&
2322 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2326 // check forbiddenwords
2327 if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
2328 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) {
2333 // second word is acceptable, as a root?
2334 // hungarian conventions: compounding is acceptable,
2335 // when compound forms consist of 2 words, or if more,
2336 // then the syllable number of root words must be 6, or lesser.
2338 (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2339 (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
2342 ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
2343 ((cpdmaxsyllable!=0) &&
2344 (numsyllable+get_syllable(HENTRY_WORD(rv),rv->blen)<=cpdmaxsyllable))
2347 (!checkcompounddup || (rv != rv_first))
2351 // bad compound word
2352 mystrcat(*result, presult, MAXLNLEN);
2353 mystrcat(*result, " ", MAXLNLEN);
2354 mystrcat(*result, MORPH_PART, MAXLNLEN);
2355 mystrcat(*result, word+i, MAXLNLEN);
2357 if (HENTRY_DATA(rv)) {
2358 if (complexprefixes) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2359 if (! HENTRY_FIND(rv, MORPH_STEM)) {
2360 mystrcat(*result, " ", MAXLNLEN);
2361 mystrcat(*result, MORPH_STEM, MAXLNLEN);
2362 mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
2364 // store the pointer of the hash entry
2365 // sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
2366 if (!complexprefixes) {
2367 mystrcat(*result, " ", MAXLNLEN);
2368 mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2371 mystrcat(*result, "\n", MAXLNLEN);
2375 numsyllable = oldnumsyllable2 ;
2376 wordnum = oldwordnum2;
2378 // perhaps second word has prefix or/and suffix
2380 sfxflag = FLAG_NULL;
2382 if (compoundflag && !onlycpdrule) rv = affix_check((word+i),strlen(word+i), compoundflag); else rv = NULL;
2384 if (!rv && compoundend && !onlycpdrule) {
2387 rv = affix_check((word+i),strlen(word+i), compoundend);
2390 if (!rv && numdefcpd && words) {
2391 rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
2392 if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
2394 if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
2395 if ((!m || *m == '\0') && compoundend) {
2397 m = affix_check_morph((word+i),strlen(word+i), compoundend);
2399 mystrcat(*result, presult, MAXLNLEN);
2400 if (m || (*m != '\0')) {
2401 sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
2402 MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
2405 mystrcat(*result, "\n", MAXLNLEN);
2410 // check non_compound flag in suffix and prefix
2412 ((pfx && pfx->getCont() &&
2413 TESTAFF(pfx->getCont(), compoundforbidflag,
2414 pfx->getContLen())) ||
2415 (sfx && sfx->getCont() &&
2416 TESTAFF(sfx->getCont(), compoundforbidflag,
2417 sfx->getContLen())))) {
2421 // check forbiddenwords
2422 if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen) ||
2423 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))
2424 && (! TESTAFF(rv->astr, needaffix, rv->alen))) {
2429 if (langnum == LANG_hu) {
2430 // calculate syllable number of the word
2431 numsyllable += get_syllable(word + i, strlen(word + i));
2433 // - affix syllable num.
2434 // XXX only second suffix (inflections, not derivations)
2436 char * tmp = myrevstrdup(sfxappnd);
2437 numsyllable -= get_syllable(tmp, strlen(tmp));
2441 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
2442 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
2444 // increment syllable num, if last word has a SYLLABLENUM flag
2445 // and the suffix is beginning `s'
2447 if (cpdsyllablenum) {
2449 case 'c': { numsyllable+=2; break; }
2450 case 'J': { numsyllable += 1; break; }
2451 case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
2456 // increment word number, if the second word has a compoundroot flag
2457 if ((rv) && (compoundroot) &&
2458 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2461 // second word is acceptable, as a word with prefix or/and suffix?
2462 // hungarian conventions: compounding is acceptable,
2463 // when compound forms consist 2 word, otherwise
2464 // the syllable number of root words is 6, or lesser.
2467 ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) ||
2468 ((cpdmaxsyllable!=0) &&
2469 (numsyllable <= cpdmaxsyllable))
2472 (!checkcompounddup || (rv != rv_first))
2475 if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
2476 if ((!m || *m == '\0') && compoundend) {
2478 m = affix_check_morph((word+i),strlen(word+i), compoundend);
2480 mystrcat(*result, presult, MAXLNLEN);
2481 if (m && (*m != '\0')) {
2482 sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
2483 MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
2486 sprintf(*result + strlen(*result), "%c", MSEP_REC);
2490 numsyllable = oldnumsyllable2;
2491 wordnum = oldwordnum2;
2493 // perhaps second word is a compound word (recursive call)
2494 if ((wordnum < maxwordnum) && (ok == 0)) {
2495 compound_check_morph((word+i),strlen(word+i), wordnum+1,
2496 numsyllable, maxwordnum, wnum + 1, words, 0, result, presult);
2502 wordnum = oldwordnum;
2503 numsyllable = oldnumsyllable;
2505 } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
2511 // return 1 if s1 (reversed) is a leading subset of end of s2
2512 /* inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
2514 while ((len > 0) && *s1 && (*s1 == *end_of_s2)) {
2519 return (*s1 == '\0');
2523 inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
2525 while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) {
2530 return (*s1 == '\0');
2533 // check word for suffixes
2535 struct hentry * AffixMgr::suffix_check (const char * word, int len,
2536 int sfxopts, PfxEntry * ppfx, char ** wlst, int maxSug, int * ns,
2537 const FLAG cclass, const FLAG needflag, char in_compound)
2539 struct hentry * rv = NULL;
2540 PfxEntry* ep = ppfx;
2542 // first handle the special case of 0 length suffixes
2543 SfxEntry * se = sStart[0];
2546 if (!cclass || se->getCont()) {
2547 // suffixes are not allowed in beginning of compounds
2548 if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2549 // except when signed with compoundpermitflag flag
2550 (se->getCont() && compoundpermitflag &&
2551 TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
2552 // no circumfix flag in prefix and suffix
2553 ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2554 circumfix, ep->getContLen())) &&
2555 (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
2556 // circumfix flag in prefix AND suffix
2557 ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2558 circumfix, ep->getContLen())) &&
2559 (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) &&
2562 !(se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) &&
2563 // needaffix on prefix or first suffix
2565 !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
2566 (ppfx && !((ep->getCont()) &&
2567 TESTAFF(ep->getCont(), needaffix,
2570 rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass,
2571 needflag, (in_compound ? 0 : onlyincompound));
2573 sfx=se; // BUG: sfx not stateless
2581 // now handle the general case
2582 if (len == 0) return NULL; // FULLSTRIP
2583 unsigned char sp= *((const unsigned char *)(word + len - 1));
2584 SfxEntry * sptr = sStart[sp];
2587 if (isRevSubset(sptr->getKey(), word + len - 1, len)
2589 // suffixes are not allowed in beginning of compounds
2590 if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2591 // except when signed with compoundpermitflag flag
2592 (sptr->getCont() && compoundpermitflag &&
2593 TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
2594 // no circumfix flag in prefix and suffix
2595 ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2596 circumfix, ep->getContLen())) &&
2597 (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
2598 // circumfix flag in prefix AND suffix
2599 ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2600 circumfix, ep->getContLen())) &&
2601 (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) &&
2604 !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
2605 // needaffix on prefix or first suffix
2607 !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
2608 (ppfx && !((ep->getCont()) &&
2609 TESTAFF(ep->getCont(), needaffix,
2612 ) if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) {
2613 rv = sptr->checkword(word,len, sfxopts, ppfx, wlst,
2614 maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound));
2616 sfx=sptr; // BUG: sfx not stateless
2617 sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2618 if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2622 sptr = sptr->getNextEQ();
2624 sptr = sptr->getNextNE();
2631 // check word for two-level suffixes
2633 struct hentry * AffixMgr::suffix_check_twosfx(const char * word, int len,
2634 int sfxopts, PfxEntry * ppfx, const FLAG needflag)
2636 struct hentry * rv = NULL;
2638 // first handle the special case of 0 length suffixes
2639 SfxEntry * se = sStart[0];
2641 if (contclasses[se->getFlag()])
2643 rv = se->check_twosfx(word,len, sfxopts, ppfx, needflag);
2649 // now handle the general case
2650 if (len == 0) return NULL; // FULLSTRIP
2651 unsigned char sp = *((const unsigned char *)(word + len - 1));
2652 SfxEntry * sptr = sStart[sp];
2655 if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
2656 if (contclasses[sptr->getFlag()])
2658 rv = sptr->check_twosfx(word,len, sfxopts, ppfx, needflag);
2660 sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2661 if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2665 sptr = sptr->getNextEQ();
2667 sptr = sptr->getNextNE();
2674 char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len,
2675 int sfxopts, PfxEntry * ppfx, const FLAG needflag)
2677 char result[MAXLNLEN];
2678 char result2[MAXLNLEN];
2679 char result3[MAXLNLEN];
2687 // first handle the special case of 0 length suffixes
2688 SfxEntry * se = sStart[0];
2690 if (contclasses[se->getFlag()])
2692 st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
2695 if (ppfx->getMorph()) {
2696 mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2697 mystrcat(result, " ", MAXLNLEN);
2698 } else debugflag(result, ppfx->getFlag());
2700 mystrcat(result, st, MAXLNLEN);
2702 if (se->getMorph()) {
2703 mystrcat(result, " ", MAXLNLEN);
2704 mystrcat(result, se->getMorph(), MAXLNLEN);
2705 } else debugflag(result, se->getFlag());
2706 mystrcat(result, "\n", MAXLNLEN);
2712 // now handle the general case
2713 if (len == 0) return NULL; // FULLSTRIP
2714 unsigned char sp = *((const unsigned char *)(word + len - 1));
2715 SfxEntry * sptr = sStart[sp];
2718 if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
2719 if (contclasses[sptr->getFlag()])
2721 st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
2723 sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2724 if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2725 strcpy(result2, st);
2730 if (sptr->getMorph()) {
2731 mystrcat(result3, " ", MAXLNLEN);
2732 mystrcat(result3, sptr->getMorph(), MAXLNLEN);
2733 } else debugflag(result3, sptr->getFlag());
2734 strlinecat(result2, result3);
2735 mystrcat(result2, "\n", MAXLNLEN);
2736 mystrcat(result, result2, MAXLNLEN);
2739 sptr = sptr->getNextEQ();
2741 sptr = sptr->getNextNE();
2744 if (*result) return mystrdup(result);
2748 char * AffixMgr::suffix_check_morph(const char * word, int len,
2749 int sfxopts, PfxEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound)
2751 char result[MAXLNLEN];
2753 struct hentry * rv = NULL;
2757 PfxEntry* ep = ppfx;
2759 // first handle the special case of 0 length suffixes
2760 SfxEntry * se = sStart[0];
2762 if (!cclass || se->getCont()) {
2763 // suffixes are not allowed in beginning of compounds
2764 if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2765 // except when signed with compoundpermitflag flag
2766 (se->getCont() && compoundpermitflag &&
2767 TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
2768 // no circumfix flag in prefix and suffix
2769 ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2770 circumfix, ep->getContLen())) &&
2771 (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
2772 // circumfix flag in prefix AND suffix
2773 ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2774 circumfix, ep->getContLen())) &&
2775 (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) &&
2778 !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) &&
2779 // needaffix on prefix or first suffix
2781 !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
2782 (ppfx && !((ep->getCont()) &&
2783 TESTAFF(ep->getCont(), needaffix,
2787 rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
2790 if (ppfx->getMorph()) {
2791 mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2792 mystrcat(result, " ", MAXLNLEN);
2793 } else debugflag(result, ppfx->getFlag());
2795 if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2796 if (! HENTRY_FIND(rv, MORPH_STEM)) {
2797 mystrcat(result, " ", MAXLNLEN);
2798 mystrcat(result, MORPH_STEM, MAXLNLEN);
2799 mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
2801 // store the pointer of the hash entry
2802 // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
2804 if (!complexprefixes && HENTRY_DATA(rv)) {
2805 mystrcat(result, " ", MAXLNLEN);
2806 mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2808 if (se->getMorph()) {
2809 mystrcat(result, " ", MAXLNLEN);
2810 mystrcat(result, se->getMorph(), MAXLNLEN);
2811 } else debugflag(result, se->getFlag());
2812 mystrcat(result, "\n", MAXLNLEN);
2813 rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
2819 // now handle the general case
2820 if (len == 0) return NULL; // FULLSTRIP
2821 unsigned char sp = *((const unsigned char *)(word + len - 1));
2822 SfxEntry * sptr = sStart[sp];
2825 if (isRevSubset(sptr->getKey(), word + len - 1, len)
2827 // suffixes are not allowed in beginning of compounds
2828 if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2829 // except when signed with compoundpermitflag flag
2830 (sptr->getCont() && compoundpermitflag &&
2831 TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
2832 // no circumfix flag in prefix and suffix
2833 ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2834 circumfix, ep->getContLen())) &&
2835 (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
2836 // circumfix flag in prefix AND suffix
2837 ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2838 circumfix, ep->getContLen())) &&
2839 (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) &&
2842 !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
2843 // needaffix on first suffix
2844 (cclass || !(sptr->getCont() &&
2845 TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())))
2846 )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
2849 if (ppfx->getMorph()) {
2850 mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2851 mystrcat(result, " ", MAXLNLEN);
2852 } else debugflag(result, ppfx->getFlag());
2854 if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2855 if (! HENTRY_FIND(rv, MORPH_STEM)) {
2856 mystrcat(result, " ", MAXLNLEN);
2857 mystrcat(result, MORPH_STEM, MAXLNLEN);
2858 mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
2860 // store the pointer of the hash entry
2861 // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
2863 if (!complexprefixes && HENTRY_DATA(rv)) {
2864 mystrcat(result, " ", MAXLNLEN);
2865 mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2868 if (sptr->getMorph()) {
2869 mystrcat(result, " ", MAXLNLEN);
2870 mystrcat(result, sptr->getMorph(), MAXLNLEN);
2871 } else debugflag(result, sptr->getFlag());
2872 mystrcat(result, "\n", MAXLNLEN);
2873 rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
2875 sptr = sptr->getNextEQ();
2877 sptr = sptr->getNextNE();
2881 if (*result) return mystrdup(result);
2885 // check if word with affixes is correctly spelled
2886 struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound)
2888 struct hentry * rv= NULL;
2890 // check all prefixes (also crossed with suffixes if allowed)
2891 rv = prefix_check(word, len, in_compound, needflag);
2894 // if still not found check all suffixes
2895 rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound);
2897 if (havecontclass) {
2902 // if still not found check all two-level suffixes
2903 rv = suffix_check_twosfx(word, len, 0, NULL, needflag);
2906 // if still not found check all two-level suffixes
2907 rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag);
2913 // check if word with affixes is correctly spelled
2914 char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound)
2916 char result[MAXLNLEN];
2921 // check all prefixes (also crossed with suffixes if allowed)
2922 st = prefix_check_morph(word, len, in_compound);
2924 mystrcat(result, st, MAXLNLEN);
2928 // if still not found check all suffixes
2929 st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound);
2931 mystrcat(result, st, MAXLNLEN);
2935 if (havecontclass) {
2938 // if still not found check all two-level suffixes
2939 st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag);
2941 mystrcat(result, st, MAXLNLEN);
2945 // if still not found check all two-level suffixes
2946 st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag);
2948 mystrcat(result, st, MAXLNLEN);
2953 return mystrdup(result);
2956 char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap,
2957 unsigned short al, char * morph, char * targetmorph, int level)
2961 char * stemmorphcatpos;
2962 char mymorph[MAXLNLEN];
2964 if (!morph) return NULL;
2966 // check substandard flag
2967 if (TESTAFF(ap, substandard, al)) return NULL;
2969 if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts);
2971 // int targetcount = get_sfxcount(targetmorph);
2973 // use input suffix fields, if exist
2974 if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) {
2975 stemmorph = mymorph;
2976 strcpy(stemmorph, morph);
2977 mystrcat(stemmorph, " ", MAXLNLEN);
2978 stemmorphcatpos = stemmorph + strlen(stemmorph);
2981 stemmorphcatpos = NULL;
2984 for (int i = 0; i < al; i++) {
2985 const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
2986 SfxEntry * sptr = sFlag[c];
2988 if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) ||
2989 // don't generate forms with substandard affixes
2990 !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) {
2992 if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph());
2993 else stemmorph = (char *) sptr->getMorph();
2995 int cmp = morphcmp(stemmorph, targetmorph);
2998 char * newword = sptr->add(ts, wl);
3000 hentry * check = pHMgr->lookup(newword); // XXX extra dic
3001 if (!check || !check->astr ||
3002 !(TESTAFF(check->astr, forbiddenword, check->alen) ||
3003 TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) {
3010 // recursive call for secondary suffixes
3011 if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) &&
3012 // (get_sfxcount(stemmorph) < targetcount) &&
3013 !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) {
3014 char * newword = sptr->add(ts, wl);
3016 char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(),
3017 sptr->getContLen(), stemmorph, targetmorph, 1);
3028 sptr = sptr->getFlgNxt();
3035 int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts,
3036 int wl, const unsigned short * ap, unsigned short al, char * bad, int badl,
3040 // first add root word to list
3041 if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) ||
3042 (onlyincompound && TESTAFF(ap, onlyincompound, al))))) {
3043 wlst[nh].word = mystrdup(ts);
3044 if (!wlst[nh].word) return 0;
3045 wlst[nh].allow = (1 == 0);
3046 wlst[nh].orig = NULL;
3048 // add special phonetic version
3049 if (phon && (nh < maxn)) {
3050 wlst[nh].word = mystrdup(phon);
3051 if (!wlst[nh].word) return nh - 1;
3052 wlst[nh].allow = (1 == 0);
3053 wlst[nh].orig = mystrdup(ts);
3054 if (!wlst[nh].orig) return nh - 1;
3060 for (int i = 0; i < al; i++) {
3061 const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
3062 SfxEntry * sptr = sFlag[c];
3064 if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) &&
3065 (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) &&
3066 // check needaffix flag
3067 !(sptr->getCont() && ((needaffix &&
3068 TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
3070 TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) ||
3072 TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))
3074 char * newword = sptr->add(ts, wl);
3077 wlst[nh].word = newword;
3078 wlst[nh].allow = sptr->allowCross();
3079 wlst[nh].orig = NULL;
3081 // add special phonetic version
3082 if (phon && (nh < maxn)) {
3083 char st[MAXWORDUTF8LEN];
3085 strcat(st, sptr->getKey());
3086 reverseword(st + strlen(phon));
3087 wlst[nh].word = mystrdup(st);
3088 if (!wlst[nh].word) return nh - 1;
3089 wlst[nh].allow = (1 == 0);
3090 wlst[nh].orig = mystrdup(newword);
3091 if (!wlst[nh].orig) return nh - 1;
3099 sptr = sptr->getFlgNxt();
3105 // handle cross products of prefixes and suffixes
3106 for (int j=1;j<n ;j++)
3107 if (wlst[j].allow) {
3108 for (int k = 0; k < al; k++) {
3109 const unsigned char c = (unsigned char) (ap[k] & 0x00FF);
3110 PfxEntry * cptr = pFlag[c];
3112 if ((cptr->getFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) &&
3113 (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) {
3114 int l1 = strlen(wlst[j].word);
3115 char * newword = cptr->add(wlst[j].word, l1);
3118 wlst[nh].word = newword;
3119 wlst[nh].allow = cptr->allowCross();
3120 wlst[nh].orig = NULL;
3127 cptr = cptr->getFlgNxt();
3133 // now handle pure prefixes
3134 for (int m = 0; m < al; m ++) {
3135 const unsigned char c = (unsigned char) (ap[m] & 0x00FF);
3136 PfxEntry * ptr = pFlag[c];
3138 if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) &&
3139 (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) &&
3140 // check needaffix flag
3141 !(ptr->getCont() && ((needaffix &&
3142 TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) ||
3144 TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) ||
3146 TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen()))))
3148 char * newword = ptr->add(ts, wl);
3151 wlst[nh].word = newword;
3152 wlst[nh].allow = ptr->allowCross();
3153 wlst[nh].orig = NULL;
3160 ptr = ptr->getFlgNxt();
3167 // return length of replacing table
3168 int AffixMgr::get_numrep() const
3173 // return replacing table
3174 struct replentry * AffixMgr::get_reptable() const
3176 if (! reptable ) return NULL;
3180 // return iconv table
3181 RepList * AffixMgr::get_iconvtable() const
3183 if (! iconvtable ) return NULL;
3187 // return oconv table
3188 RepList * AffixMgr::get_oconvtable() const
3190 if (! oconvtable ) return NULL;
3194 // return replacing table
3195 struct phonetable * AffixMgr::get_phonetable() const
3197 if (! phone ) return NULL;
3201 // return length of character map table
3202 int AffixMgr::get_nummap() const
3207 // return character map table
3208 struct mapentry * AffixMgr::get_maptable() const
3210 if (! maptable ) return NULL;
3214 // return length of word break table
3215 int AffixMgr::get_numbreak() const
3220 // return character map table
3221 char ** AffixMgr::get_breaktable() const
3223 if (! breaktable ) return NULL;
3227 // return text encoding of dictionary
3228 char * AffixMgr::get_encoding()
3230 if (! encoding ) encoding = mystrdup(SPELL_ENCODING);
3231 return mystrdup(encoding);
3234 // return text encoding of dictionary
3235 int AffixMgr::get_langnum() const
3240 // return double prefix option
3241 int AffixMgr::get_complexprefixes() const
3243 return complexprefixes;
3246 // return FULLSTRIP option
3247 int AffixMgr::get_fullstrip() const
3252 FLAG AffixMgr::get_keepcase() const
3257 FLAG AffixMgr::get_forceucase() const
3262 FLAG AffixMgr::get_warn() const
3267 int AffixMgr::get_forbidwarn() const
3272 int AffixMgr::get_checksharps() const
3277 char * AffixMgr::encode_flag(unsigned short aflag) const
3279 return pHMgr->encode_flag(aflag);
3283 // return the preferred ignore string for suggestions
3284 char * AffixMgr::get_ignore() const
3286 if (!ignorechars) return NULL;
3290 // return the preferred ignore string for suggestions
3291 unsigned short * AffixMgr::get_ignore_utf16(int * len) const
3293 *len = ignorechars_utf16_len;
3294 return ignorechars_utf16;
3297 // return the keyboard string for suggestions
3298 char * AffixMgr::get_key_string()
3300 if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING);
3301 return mystrdup(keystring);
3304 // return the preferred try string for suggestions
3305 char * AffixMgr::get_try_string() const
3307 if (! trystring ) return NULL;
3308 return mystrdup(trystring);
3311 // return the preferred try string for suggestions
3312 const char * AffixMgr::get_wordchars() const
3317 unsigned short * AffixMgr::get_wordchars_utf16(int * len) const
3319 *len = wordchars_utf16_len;
3320 return wordchars_utf16;
3323 // is there compounding?
3324 int AffixMgr::get_compound() const
3326 return compoundflag || compoundbegin || numdefcpd;
3329 // return the compound words control flag
3330 FLAG AffixMgr::get_compoundflag() const
3332 return compoundflag;
3335 // return the forbidden words control flag
3336 FLAG AffixMgr::get_forbiddenword() const
3338 return forbiddenword;
3341 // return the forbidden words control flag
3342 FLAG AffixMgr::get_nosuggest() const
3347 // return the forbidden words control flag
3348 FLAG AffixMgr::get_nongramsuggest() const
3350 return nongramsuggest;
3353 // return the forbidden words flag modify flag
3354 FLAG AffixMgr::get_needaffix() const
3359 // return the onlyincompound flag
3360 FLAG AffixMgr::get_onlyincompound() const
3362 return onlyincompound;
3365 // return the compound word signal flag
3366 FLAG AffixMgr::get_compoundroot() const
3368 return compoundroot;
3371 // return the compound begin signal flag
3372 FLAG AffixMgr::get_compoundbegin() const
3374 return compoundbegin;
3377 // return the value of checknum
3378 int AffixMgr::get_checknum() const
3383 // return the value of prefix
3384 const char * AffixMgr::get_prefix() const
3386 if (pfx) return pfx->getKey();
3390 // return the value of suffix
3391 const char * AffixMgr::get_suffix() const
3396 // return the value of suffix
3397 const char * AffixMgr::get_version() const
3402 // return lemma_present flag
3403 FLAG AffixMgr::get_lemma_present() const
3405 return lemma_present;
3408 // utility method to look up root words in hash table
3409 struct hentry * AffixMgr::lookup(const char * word)
3412 struct hentry * he = NULL;
3413 for (i = 0; i < *maxdic && !he; i++) {
3414 he = (alldic[i])->lookup(word);
3419 // return the value of suffix
3420 int AffixMgr::have_contclass() const
3422 return havecontclass;
3426 int AffixMgr::get_utf8() const
3431 int AffixMgr::get_maxngramsugs(void) const
3433 return maxngramsugs;
3436 int AffixMgr::get_maxcpdsugs(void) const
3441 int AffixMgr::get_maxdiff(void) const
3446 int AffixMgr::get_onlymaxdiff(void) const
3451 // return nosplitsugs
3452 int AffixMgr::get_nosplitsugs(void) const
3457 // return sugswithdots
3458 int AffixMgr::get_sugswithdots(void) const
3460 return sugswithdots;
3464 int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) {
3466 if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) {
3467 HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
3470 if (parse_string(line, &s, af->getlinenum())) return 1;
3471 *out = pHMgr->decode_flag(s);
3477 int AffixMgr::parse_num(char * line, int * out, FileMgr * af) {
3480 HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
3483 if (parse_string(line, &s, af->getlinenum())) return 1;
3489 /* parse in the max syllablecount of compound words and */
3490 int AffixMgr::parse_cpdsyllable(char * line, FileMgr * af)
3496 w_char w[MAXWORDLEN];
3497 piece = mystrsep(&tp, 0);
3499 if (*piece != '\0') {
3501 case 0: { np++; break; }
3502 case 1: { cpdmaxsyllable = atoi(piece); np++; break; }
3505 cpdvowels = mystrdup(piece);
3507 int n = u8_u16(w, MAXWORDLEN, piece);
3509 flag_qsort((unsigned short *) w, 0, n);
3510 cpdvowels_utf16 = (w_char *) malloc(n * sizeof(w_char));
3511 if (!cpdvowels_utf16) return 1;
3512 memcpy(cpdvowels_utf16, w, n * sizeof(w_char));
3514 cpdvowels_utf16_len = n;
3523 piece = mystrsep(&tp, 0);
3526 HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum());
3529 if (np == 2) cpdvowels = mystrdup("aeiouAEIOU");
3533 /* parse in the typical fault correcting table */
3534 int AffixMgr::parse_reptable(char * line, FileMgr * af)
3537 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3544 piece = mystrsep(&tp, 0);
3546 if (*piece != '\0') {
3548 case 0: { np++; break; }
3550 numrep = atoi(piece);
3552 HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
3555 reptable = (replentry *) malloc(numrep * sizeof(struct replentry));
3556 if (!reptable) return 1;
3564 piece = mystrsep(&tp, 0);
3567 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3571 /* now parse the numrep lines to read in the remainder of the table */
3573 for (int j=0; j < numrep; j++) {
3574 if ((nl = af->getline()) == NULL) return 1;
3578 reptable[j].pattern = NULL;
3579 reptable[j].pattern2 = NULL;
3580 piece = mystrsep(&tp, 0);
3582 if (*piece != '\0') {
3585 if (strncmp(piece,"REP",3) != 0) {
3586 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3593 if (*piece == '^') reptable[j].start = true; else reptable[j].start = false;
3594 reptable[j].pattern = mystrrep(mystrdup(piece + int(reptable[j].start)),"_"," ");
3595 int lr = strlen(reptable[j].pattern) - 1;
3596 if (reptable[j].pattern[lr] == '$') {
3597 reptable[j].end = true;
3598 reptable[j].pattern[lr] = '\0';
3599 } else reptable[j].end = false;
3602 case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; }
3607 piece = mystrsep(&tp, 0);
3609 if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) {
3610 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3618 /* parse in the typical fault correcting table */
3619 int AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword)
3622 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3630 piece = mystrsep(&tp, 0);
3632 if (*piece != '\0') {
3634 case 0: { np++; break; }
3636 numrl = atoi(piece);
3638 HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
3641 *rl = new RepList(numrl);
3650 piece = mystrsep(&tp, 0);
3653 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3657 /* now parse the num lines to read in the remainder of the table */
3659 for (int j=0; j < numrl; j++) {
3660 if (!(nl = af->getline())) return 1;
3664 char * pattern = NULL;
3665 char * pattern2 = NULL;
3666 piece = mystrsep(&tp, 0);
3668 if (*piece != '\0') {
3671 if (strncmp(piece, keyword, strlen(keyword)) != 0) {
3672 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3679 case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; }
3681 pattern2 = mystrrep(mystrdup(piece),"_"," ");
3688 piece = mystrsep(&tp, 0);
3690 if (!pattern || !pattern2) {
3695 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3698 (*rl)->add(pattern, pattern2);
3704 /* parse in the typical fault correcting table */
3705 int AffixMgr::parse_phonetable(char * line, FileMgr * af)
3708 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3715 piece = mystrsep(&tp, 0);
3717 if (*piece != '\0') {
3719 case 0: { np++; break; }
3721 phone = (phonetable *) malloc(sizeof(struct phonetable));
3722 if (!phone) return 1;
3723 phone->num = atoi(piece);
3724 phone->rules = NULL;
3725 phone->utf8 = (char) utf8;
3726 if (phone->num < 1) {
3727 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3730 phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *));
3731 if (!phone->rules) {
3743 piece = mystrsep(&tp, 0);
3746 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3750 /* now parse the phone->num lines to read in the remainder of the table */
3752 for (int j=0; j < phone->num; j++) {
3753 if (!(nl = af->getline())) return 1;
3757 phone->rules[j * 2] = NULL;
3758 phone->rules[j * 2 + 1] = NULL;
3759 piece = mystrsep(&tp, 0);
3761 if (*piece != '\0') {
3764 if (strncmp(piece,"PHONE",5) != 0) {
3765 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3771 case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; }
3772 case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; }
3777 piece = mystrsep(&tp, 0);
3779 if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) {
3780 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3785 phone->rules[phone->num * 2] = mystrdup("");
3786 phone->rules[phone->num * 2 + 1] = mystrdup("");
3787 init_phonet_hash(*phone);
3791 /* parse in the checkcompoundpattern table */
3792 int AffixMgr::parse_checkcpdtable(char * line, FileMgr * af)
3794 if (numcheckcpd != 0) {
3795 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3802 piece = mystrsep(&tp, 0);
3804 if (*piece != '\0') {
3806 case 0: { np++; break; }
3808 numcheckcpd = atoi(piece);
3809 if (numcheckcpd < 1) {
3810 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3813 checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry));
3814 if (!checkcpdtable) return 1;
3822 piece = mystrsep(&tp, 0);
3825 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3829 /* now parse the numcheckcpd lines to read in the remainder of the table */
3831 for (int j=0; j < numcheckcpd; j++) {
3832 if (!(nl = af->getline())) return 1;
3836 checkcpdtable[j].pattern = NULL;
3837 checkcpdtable[j].pattern2 = NULL;
3838 checkcpdtable[j].pattern3 = NULL;
3839 checkcpdtable[j].cond = FLAG_NULL;
3840 checkcpdtable[j].cond2 = FLAG_NULL;
3841 piece = mystrsep(&tp, 0);
3843 if (*piece != '\0') {
3846 if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) {
3847 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3854 checkcpdtable[j].pattern = mystrdup(piece);
3855 char * p = strchr(checkcpdtable[j].pattern, '/');
3858 checkcpdtable[j].cond = pHMgr->decode_flag(p + 1);
3862 checkcpdtable[j].pattern2 = mystrdup(piece);
3863 char * p = strchr(checkcpdtable[j].pattern2, '/');
3866 checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1);
3870 case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; }
3875 piece = mystrsep(&tp, 0);
3877 if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) {
3878 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3886 /* parse in the compound rule table */
3887 int AffixMgr::parse_defcpdtable(char * line, FileMgr * af)
3889 if (numdefcpd != 0) {
3890 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3897 piece = mystrsep(&tp, 0);
3899 if (*piece != '\0') {
3901 case 0: { np++; break; }
3903 numdefcpd = atoi(piece);
3904 if (numdefcpd < 1) {
3905 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3908 defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry));
3909 if (!defcpdtable) return 1;
3917 piece = mystrsep(&tp, 0);
3920 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3924 /* now parse the numdefcpd lines to read in the remainder of the table */
3926 for (int j=0; j < numdefcpd; j++) {
3927 if (!(nl = af->getline())) return 1;
3931 defcpdtable[j].def = NULL;
3932 piece = mystrsep(&tp, 0);
3934 if (*piece != '\0') {
3937 if (strncmp(piece, "COMPOUNDRULE", 12) != 0) {
3938 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3944 case 1: { // handle parenthesized flags
3945 if (strchr(piece, '(')) {
3946 defcpdtable[j].def = (FLAG *) malloc(strlen(piece) * sizeof(FLAG));
3947 defcpdtable[j].len = 0;
3951 char * par = piece + 1;
3952 while (*par != '(' && *par != ')' && *par != '\0') par++;
3953 if (*par == '\0') end = 1; else *par = '\0';
3954 if (*piece == '(') piece++;
3955 if (*piece == '*' || *piece == '?') {
3956 defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece;
3957 } else if (*piece != '\0') {
3958 int l = pHMgr->decode_flags(&conv, piece, af);
3959 for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k];
3965 defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af);
3973 piece = mystrsep(&tp, 0);
3975 if (!defcpdtable[j].len) {
3976 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3985 /* parse in the character map table */
3986 int AffixMgr::parse_maptable(char * line, FileMgr * af)
3989 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3996 piece = mystrsep(&tp, 0);
3998 if (*piece != '\0') {
4000 case 0: { np++; break; }
4002 nummap = atoi(piece);
4004 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
4007 maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry));
4008 if (!maptable) return 1;
4016 piece = mystrsep(&tp, 0);
4019 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
4023 /* now parse the nummap lines to read in the remainder of the table */
4025 for (int j=0; j < nummap; j++) {
4026 if (!(nl = af->getline())) return 1;
4030 maptable[j].set = NULL;
4031 maptable[j].len = 0;
4032 piece = mystrsep(&tp, 0);
4034 if (*piece != '\0') {
4037 if (strncmp(piece,"MAP",3) != 0) {
4038 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4046 maptable[j].len = strlen(piece);
4047 maptable[j].set = (char **) malloc(maptable[j].len * sizeof(char*));
4048 if (!maptable[j].set) return 1;
4049 for (int k = 0; k < maptable[j].len; k++) {
4052 if (piece[k] == '(') {
4053 char * parpos = strchr(piece + k, ')');
4054 if (parpos != NULL) {
4056 chl = (int)(parpos - piece) - k - 1;
4060 if (utf8 && (piece[k] & 0xc0) == 0xc0) {
4061 for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++);
4066 maptable[j].set[setn] = (char *) malloc(chl + 1);
4067 if (!maptable[j].set[setn]) return 1;
4068 strncpy(maptable[j].set[setn], piece + chb, chl);
4069 maptable[j].set[setn][chl] = '\0';
4072 maptable[j].len = setn;
4078 piece = mystrsep(&tp, 0);
4080 if (!maptable[j].set || !maptable[j].len) {
4081 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4089 /* parse in the word breakpoint table */
4090 int AffixMgr::parse_breaktable(char * line, FileMgr * af)
4092 if (numbreak > -1) {
4093 HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
4100 piece = mystrsep(&tp, 0);
4102 if (*piece != '\0') {
4104 case 0: { np++; break; }
4106 numbreak = atoi(piece);
4108 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
4111 if (numbreak == 0) return 0;
4112 breaktable = (char **) malloc(numbreak * sizeof(char *));
4113 if (!breaktable) return 1;
4121 piece = mystrsep(&tp, 0);
4124 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
4128 /* now parse the numbreak lines to read in the remainder of the table */
4130 for (int j=0; j < numbreak; j++) {
4131 if (!(nl = af->getline())) return 1;
4135 piece = mystrsep(&tp, 0);
4137 if (*piece != '\0') {
4140 if (strncmp(piece,"BREAK",5) != 0) {
4141 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4148 breaktable[j] = mystrdup(piece);
4155 piece = mystrsep(&tp, 0);
4158 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4166 void AffixMgr::reverse_condition(char * piece) {
4168 for (char * k = piece + strlen(piece) - 1; k >= piece; k--) {
4171 if (neg) *(k+1) = '['; else *k = ']';
4176 if (neg) *(k+1) = '^';
4181 if (*(k+1) == ']') neg = 1; else *(k+1) = *k;
4185 if (neg) *(k+1) = *k;
4191 int AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags)
4193 int numents = 0; // number of affentry structures to parse
4195 unsigned short aflag = 0; // affix char identifier
4198 std::vector<affentry> affentries;
4205 // checking lines with bad syntax
4207 int basefieldnum = 0;
4210 // split affix header line into pieces
4214 piece = mystrsep(&tp, 0);
4216 if (*piece != '\0') {
4218 // piece 1 - is type of affix
4219 case 0: { np++; break; }
4221 // piece 2 - is affix char
4224 aflag = pHMgr->decode_flag(piece);
4225 if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
4226 ((at == 'P') && (dupflags[aflag] & dupPFX))) {
4227 HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n",
4229 // return 1; XXX permissive mode for bad dictionaries
4231 dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX);
4234 // piece 3 - is cross product indicator
4235 case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; }
4237 // piece 4 - is number of affentries
4240 numents = atoi(piece);
4242 char * err = pHMgr->encode_flag(aflag);
4244 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
4250 affentries.resize(numents);
4251 affentries[0].opts = ff;
4252 if (utf8) affentries[0].opts += aeUTF8;
4253 if (pHMgr->is_aliasf()) affentries[0].opts += aeALIASF;
4254 if (pHMgr->is_aliasm()) affentries[0].opts += aeALIASM;
4255 affentries[0].aflag = aflag;
4262 piece = mystrsep(&tp, 0);
4264 // check to make sure we parsed enough pieces
4266 char * err = pHMgr->encode_flag(aflag);
4268 HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
4274 // now parse numents affentries for this affix
4275 std::vector<affentry>::iterator start = affentries.begin();
4276 std::vector<affentry>::iterator end = affentries.end();
4277 for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
4278 if ((nl = af->getline()) == NULL) return 1;
4284 // split line into pieces
4285 piece = mystrsep(&tp, 0);
4287 if (*piece != '\0') {
4289 // piece 1 - is type
4292 if (entry != start) entry->opts = start->opts &
4293 (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM);
4297 // piece 2 - is affix char
4300 if (pHMgr->decode_flag(piece) != aflag) {
4301 char * err = pHMgr->encode_flag(aflag);
4303 HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
4304 af->getlinenum(), err);
4310 if (entry != start) entry->aflag = start->aflag;
4314 // piece 3 - is string to strip or 0 for null
4317 if (complexprefixes) {
4318 if (utf8) reverseword_utf(piece); else reverseword(piece);
4320 entry->strip = mystrdup(piece);
4321 entry->stripl = (unsigned char) strlen(entry->strip);
4322 if (strcmp(entry->strip,"0") == 0) {
4324 entry->strip=mystrdup("");
4330 // piece 4 - is affix string or 0 for null
4333 entry->morphcode = NULL;
4334 entry->contclass = NULL;
4335 entry->contclasslen = 0;
4337 dash = strchr(piece, '/');
4343 remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
4345 remove_ignored_chars(piece,ignorechars);
4349 if (complexprefixes) {
4350 if (utf8) reverseword_utf(piece); else reverseword(piece);
4352 entry->appnd = mystrdup(piece);
4354 if (pHMgr->is_aliasf()) {
4355 int index = atoi(dash + 1);
4356 entry->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(entry->contclass), af);
4357 if (!entry->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1);
4359 entry->contclasslen = (unsigned short) pHMgr->decode_flags(&(entry->contclass), dash + 1, af);
4360 flag_qsort(entry->contclass, 0, entry->contclasslen);
4365 for (unsigned short _i = 0; _i < entry->contclasslen; _i++) {
4366 contclasses[(entry->contclass)[_i]] = 1;
4371 remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
4373 remove_ignored_chars(piece,ignorechars);
4377 if (complexprefixes) {
4378 if (utf8) reverseword_utf(piece); else reverseword(piece);
4380 entry->appnd = mystrdup(piece);
4383 entry->appndl = (unsigned char) strlen(entry->appnd);
4384 if (strcmp(entry->appnd,"0") == 0) {
4386 entry->appnd=mystrdup("");
4392 // piece 5 - is the conditions descriptions
4395 if (complexprefixes) {
4396 if (utf8) reverseword_utf(piece); else reverseword(piece);
4397 reverse_condition(piece);
4399 if (entry->stripl && (strcmp(piece, ".") != 0) &&
4400 redundant_condition(at, entry->strip, entry->stripl, piece, af->getlinenum()))
4404 reverse_condition(piece);
4406 if (encodeit(*entry, piece)) return 1;
4412 if (pHMgr->is_aliasm()) {
4413 int index = atoi(piece);
4414 entry->morphcode = pHMgr->get_aliasm(index);
4416 if (complexprefixes) { // XXX - fix me for morph. gen.
4417 if (utf8) reverseword_utf(piece); else reverseword(piece);
4419 // add the remaining of the line
4422 tp = tp + strlen(tp);
4424 entry->morphcode = mystrdup(piece);
4425 if (!entry->morphcode) return 1;
4433 piece = mystrsep(&tp, 0);
4435 // check to make sure we parsed enough pieces
4437 char * err = pHMgr->encode_flag(aflag);
4439 HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
4440 af->getlinenum(), err);
4447 // detect unnecessary fields, excepting comments
4449 int fieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
4450 if (fieldnum != basefieldnum)
4451 HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum());
4453 basefieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
4458 // now create SfxEntry or PfxEntry objects and use links to
4459 // build an ordered (sorted by affix string) list
4460 for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
4462 PfxEntry * pfxptr = new PfxEntry(this,&(*entry));
4463 build_pfxtree(pfxptr);
4465 SfxEntry * sfxptr = new SfxEntry(this,&(*entry));
4466 build_sfxtree(sfxptr);
4472 int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) {
4473 int condl = strlen(cond);
4478 if (ft == 'P') { // prefix
4479 if (strncmp(strip, cond, condl) == 0) return 1;
4482 for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) {
4483 if (cond[j] != '[') {
4484 if (cond[j] != strip[i]) {
4485 HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4489 neg = (cond[j+1] == '^') ? 1 : 0;
4493 if (strip[i] == cond[j]) in = 1;
4494 } while ((j < (condl - 1)) && (cond[j] != ']'));
4495 if (j == (condl - 1) && (cond[j] != ']')) {
4496 HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum, cond);
4499 if ((!neg && !in) || (neg && in)) {
4500 HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4505 if (j >= condl) return 1;
4508 if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1;
4511 for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) {
4512 if (cond[j] != ']') {
4513 if (cond[j] != strip[i]) {
4514 HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4521 if (strip[i] == cond[j]) in = 1;
4522 } while ((j > 0) && (cond[j] != '['));
4523 if ((j == 0) && (cond[j] != '[')) {
4524 HUNSPELL_WARNING(stderr, "error: line: %d: missing ] in condition:\n%s\n", linenum, cond);
4527 neg = (cond[j+1] == '^') ? 1 : 0;
4528 if ((!neg && !in) || (neg && in)) {
4529 HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4534 if (j < 0) return 1;