Tizen 2.1 base
[external/enchant.git] / src / myspell / affixmgr.cxx
1 #include "license.hunspell"
2 #include "license.myspell"
3
4 #include <stdlib.h>
5 #include <string.h>
6 #include <stdio.h>
7 #include <ctype.h>
8
9 #include <vector>
10
11 #include "affixmgr.hxx"
12 #include "affentry.hxx"
13 #include "langnum.hxx"
14
15 #include "csutil.hxx"
16
17 AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key) 
18 {
19   // register hash manager and load affix data from aff file
20   pHMgr = ptr[0];
21   alldic = ptr;
22   maxdic = md;
23   keystring = NULL;
24   trystring = NULL;
25   encoding=NULL;
26   csconv=NULL;
27   utf8 = 0;
28   complexprefixes = 0;
29   maptable = NULL;
30   nummap = 0;
31   breaktable = NULL;
32   numbreak = -1;
33   reptable = NULL;
34   numrep = 0;
35   iconvtable = NULL;
36   oconvtable = NULL;
37   checkcpdtable = NULL;
38   // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN)
39   simplifiedcpd = 0;
40   numcheckcpd = 0;
41   defcpdtable = NULL;
42   numdefcpd = 0;
43   phone = NULL;
44   compoundflag = FLAG_NULL; // permits word in compound forms
45   compoundbegin = FLAG_NULL; // may be first word in compound forms
46   compoundmiddle = FLAG_NULL; // may be middle word in compound forms
47   compoundend = FLAG_NULL; // may be last word in compound forms
48   compoundroot = FLAG_NULL; // compound word signing flag
49   compoundpermitflag = FLAG_NULL; // compound permitting flag for suffixed word
50   compoundforbidflag = FLAG_NULL; // compound fordidden flag for suffixed word
51   checkcompounddup = 0; // forbid double words in compounds
52   checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution)
53   checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds
54   checkcompoundtriple = 0; // forbid compounds with triple letters
55   simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt)
56   forbiddenword = FORBIDDENWORD; // forbidden word signing flag
57   nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag
58   nongramsuggest = FLAG_NULL;
59   lang = NULL; // language
60   langnum = 0; // language code (see http://l10n.openoffice.org/languages.html)
61   needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes
62   cpdwordmax = -1; // default: unlimited wordcount in compound words
63   cpdmin = -1;  // undefined
64   cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words
65   cpdvowels=NULL; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX)
66   cpdvowels_utf16=NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search)
67   cpdvowels_utf16_len=0; // vowels
68   pfxappnd=NULL; // previous prefix for counting the syllables of prefix BUG
69   sfxappnd=NULL; // previous suffix for counting a special syllables BUG
70   cpdsyllablenum=NULL; // syllable count incrementing flag
71   checknum=0; // checking numbers, and word with numbers
72   wordchars=NULL; // letters + spec. word characters
73   wordchars_utf16=NULL; // letters + spec. word characters
74   wordchars_utf16_len=0; // letters + spec. word characters
75   ignorechars=NULL; // letters + spec. word characters
76   ignorechars_utf16=NULL; // letters + spec. word characters
77   ignorechars_utf16_len=0; // letters + spec. word characters
78   version=NULL; // affix and dictionary file version string
79   havecontclass=0; // flags of possible continuing classes (double affix)
80   // LEMMA_PRESENT: not put root into the morphological output. Lemma presents
81   // in morhological description in dictionary file. It's often combined with PSEUDOROOT.
82   lemma_present = FLAG_NULL; 
83   circumfix = FLAG_NULL; 
84   onlyincompound = FLAG_NULL; 
85   maxngramsugs = -1; // undefined
86   maxdiff = -1; // undefined
87   onlymaxdiff = 0;
88   maxcpdsugs = -1; // undefined
89   nosplitsugs = 0;
90   sugswithdots = 0;
91   keepcase = 0;
92   forceucase = 0;
93   warn = 0;
94   forbidwarn = 0;
95   checksharps = 0;
96   substandard = FLAG_NULL;
97   fullstrip = 0;
98
99   sfx = NULL;
100   pfx = NULL;
101
102   for (int i=0; i < SETSIZE; i++) {
103      pStart[i] = NULL;
104      sStart[i] = NULL;
105      pFlag[i] = NULL;
106      sFlag[i] = NULL;
107   }
108
109   for (int j=0; j < CONTSIZE; j++) {
110     contclasses[j] = 0;
111   }
112
113   if (parse_file(affpath, key)) {
114      HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath);
115   }
116   
117   if (cpdmin == -1) cpdmin = MINCPDLEN;
118
119 }
120
121
122 AffixMgr::~AffixMgr() 
123 {
124   // pass through linked prefix entries and clean up
125   for (int i=0; i < SETSIZE ;i++) {
126        pFlag[i] = NULL;
127        PfxEntry * ptr = pStart[i];
128        PfxEntry * nptr = NULL;
129        while (ptr) {
130             nptr = ptr->getNext();
131             delete(ptr);
132             ptr = nptr;
133             nptr = NULL;
134        }  
135   }
136
137   // pass through linked suffix entries and clean up
138   for (int j=0; j < SETSIZE ; j++) {
139        sFlag[j] = NULL;
140        SfxEntry * ptr = sStart[j];
141        SfxEntry * nptr = NULL;
142        while (ptr) {
143             nptr = ptr->getNext();
144             delete(ptr);
145             ptr = nptr;
146             nptr = NULL;
147        }
148        sStart[j] = NULL;
149   }
150
151   if (keystring) free(keystring);
152   keystring=NULL;
153   if (trystring) free(trystring);
154   trystring=NULL;
155   if (encoding) free(encoding);
156   encoding=NULL;
157   if (maptable) {  
158      for (int j=0; j < nummap; j++) {
159         for (int k=0; k < maptable[j].len; k++) {
160            if (maptable[j].set[k]) free(maptable[j].set[k]);
161         }
162         free(maptable[j].set);
163         maptable[j].set = NULL;
164         maptable[j].len = 0;
165      }
166      free(maptable);  
167      maptable = NULL;
168   }
169   nummap = 0;
170   if (breaktable) {
171      for (int j=0; j < numbreak; j++) {
172         if (breaktable[j]) free(breaktable[j]);
173         breaktable[j] = NULL;
174      }
175      free(breaktable);  
176      breaktable = NULL;
177   }
178   numbreak = 0;
179   if (reptable) {
180      for (int j=0; j < numrep; j++) {
181         free(reptable[j].pattern);
182         free(reptable[j].pattern2);
183      }
184      free(reptable);  
185      reptable = NULL;
186   }
187   if (iconvtable) delete iconvtable;
188   if (oconvtable) delete oconvtable;
189   if (phone && phone->rules) {
190      for (int j=0; j < phone->num + 1; j++) {
191         free(phone->rules[j * 2]);
192         free(phone->rules[j * 2 + 1]);
193      }
194      free(phone->rules);
195      free(phone);  
196      phone = NULL;
197   }
198
199   if (defcpdtable) {  
200      for (int j=0; j < numdefcpd; j++) {
201         free(defcpdtable[j].def);
202         defcpdtable[j].def = NULL;
203      }
204      free(defcpdtable);  
205      defcpdtable = NULL;
206   }
207   numrep = 0;
208   if (checkcpdtable) {  
209      for (int j=0; j < numcheckcpd; j++) {
210         free(checkcpdtable[j].pattern);
211         free(checkcpdtable[j].pattern2);
212         free(checkcpdtable[j].pattern3);
213         checkcpdtable[j].pattern = NULL;
214         checkcpdtable[j].pattern2 = NULL;
215         checkcpdtable[j].pattern3 = NULL;
216      }
217      free(checkcpdtable);  
218      checkcpdtable = NULL;
219   }
220   numcheckcpd = 0;
221   FREE_FLAG(compoundflag);
222   FREE_FLAG(compoundbegin);
223   FREE_FLAG(compoundmiddle);
224   FREE_FLAG(compoundend);
225   FREE_FLAG(compoundpermitflag);
226   FREE_FLAG(compoundforbidflag);
227   FREE_FLAG(compoundroot);
228   FREE_FLAG(forbiddenword);
229   FREE_FLAG(nosuggest);
230   FREE_FLAG(nongramsuggest);
231   FREE_FLAG(needaffix);
232   FREE_FLAG(lemma_present);
233   FREE_FLAG(circumfix);
234   FREE_FLAG(onlyincompound);
235   
236   cpdwordmax = 0;
237   pHMgr = NULL;
238   cpdmin = 0;
239   cpdmaxsyllable = 0;
240   if (cpdvowels) free(cpdvowels);
241   if (cpdvowels_utf16) free(cpdvowels_utf16);
242   if (cpdsyllablenum) free(cpdsyllablenum);
243   free_utf_tbl();
244   if (lang) free(lang);
245   if (wordchars) free(wordchars);
246   if (wordchars_utf16) free(wordchars_utf16);
247   if (ignorechars) free(ignorechars);
248   if (ignorechars_utf16) free(ignorechars_utf16);
249   if (version) free(version);
250   checknum=0;
251 #ifdef MOZILLA_CLIENT
252   delete [] csconv;
253 #endif
254 }
255
256
257 // read in aff file and build up prefix and suffix entry objects 
258 int  AffixMgr::parse_file(const char * affpath, const char * key)
259 {
260   char * line; // io buffers
261   char ft;     // affix type
262   
263   // checking flag duplication
264   char dupflags[CONTSIZE];
265   char dupflags_ini = 1;
266
267   // first line indicator for removing byte order mark
268   int firstline = 1;
269   
270   // open the affix file
271   FileMgr * afflst = new FileMgr(affpath, key);
272   if (!afflst) {
273     HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath);
274     return 1;
275   }
276
277   // step one is to parse the affix file building up the internal
278   // affix data structures
279
280     // read in each line ignoring any that do not
281     // start with a known line type indicator
282     while ((line = afflst->getline())) {
283        mychomp(line);
284
285        /* remove byte order mark */
286        if (firstline) {
287          firstline = 0;
288          // Affix file begins with byte order mark: possible incompatibility with old Hunspell versions
289          if (strncmp(line,"\xEF\xBB\xBF",3) == 0) {
290             memmove(line, line+3, strlen(line+3)+1);
291          }
292        }
293
294        /* parse in the keyboard string */
295        if (strncmp(line,"KEY",3) == 0) {
296           if (parse_string(line, &keystring, afflst->getlinenum())) {
297              delete afflst;
298              return 1;
299           }
300        }
301
302        /* parse in the try string */
303        if (strncmp(line,"TRY",3) == 0) {
304           if (parse_string(line, &trystring, afflst->getlinenum())) {
305              delete afflst;
306              return 1;
307           }
308        }
309
310        /* parse in the name of the character set used by the .dict and .aff */
311        if (strncmp(line,"SET",3) == 0) {
312           if (parse_string(line, &encoding, afflst->getlinenum())) {
313              delete afflst;
314              return 1;
315           }
316           if (strcmp(encoding, "UTF-8") == 0) {
317              utf8 = 1;
318 #ifndef OPENOFFICEORG
319 #ifndef MOZILLA_CLIENT
320              if (initialize_utf_tbl()) return 1;
321 #endif
322 #endif
323           }
324        }
325
326        /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */
327        if (strncmp(line,"COMPLEXPREFIXES",15) == 0)
328                    complexprefixes = 1;
329
330        /* parse in the flag used by the controlled compound words */
331        if (strncmp(line,"COMPOUNDFLAG",12) == 0) {
332           if (parse_flag(line, &compoundflag, afflst)) {
333              delete afflst;
334              return 1;
335           }
336        }
337
338        /* parse in the flag used by compound words */
339        if (strncmp(line,"COMPOUNDBEGIN",13) == 0) {
340           if (complexprefixes) {
341             if (parse_flag(line, &compoundend, afflst)) {
342               delete afflst;
343               return 1;
344             }
345           } else {
346             if (parse_flag(line, &compoundbegin, afflst)) {
347               delete afflst;
348               return 1;
349             }
350           }
351        }
352
353        /* parse in the flag used by compound words */
354        if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) {
355           if (parse_flag(line, &compoundmiddle, afflst)) {
356              delete afflst;
357              return 1;
358           }
359        }
360        /* parse in the flag used by compound words */
361        if (strncmp(line,"COMPOUNDEND",11) == 0) {
362           if (complexprefixes) {
363             if (parse_flag(line, &compoundbegin, afflst)) {
364               delete afflst;
365               return 1;
366             }
367           } else {
368             if (parse_flag(line, &compoundend, afflst)) {
369               delete afflst;
370               return 1;
371             }
372           }
373        }
374
375        /* parse in the data used by compound_check() method */
376        if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) {
377           if (parse_num(line, &cpdwordmax, afflst)) {
378              delete afflst;
379              return 1;
380           }
381        }
382
383        /* parse in the flag sign compounds in dictionary */
384        if (strncmp(line,"COMPOUNDROOT",12) == 0) {
385           if (parse_flag(line, &compoundroot, afflst)) {
386              delete afflst;
387              return 1;
388           }
389        }
390
391        /* parse in the flag used by compound_check() method */
392        if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) {
393           if (parse_flag(line, &compoundpermitflag, afflst)) {
394              delete afflst;
395              return 1;
396           }
397        }
398
399        /* parse in the flag used by compound_check() method */
400        if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) {
401           if (parse_flag(line, &compoundforbidflag, afflst)) {
402              delete afflst;
403              return 1;
404           }
405        }
406
407        if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) {
408                    checkcompounddup = 1;
409        }
410
411        if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) {
412                    checkcompoundrep = 1;
413        }
414
415        if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) {
416                    checkcompoundtriple = 1;
417        }
418
419        if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) {
420                    simplifiedtriple = 1;
421        }
422
423        if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) {
424                    checkcompoundcase = 1;
425        }
426
427        if (strncmp(line,"NOSUGGEST",9) == 0) {
428           if (parse_flag(line, &nosuggest, afflst)) {
429              delete afflst;
430              return 1;
431           }
432        }
433
434        if (strncmp(line,"NONGRAMSUGGEST",14) == 0) {
435           if (parse_flag(line, &nongramsuggest, afflst)) {
436              delete afflst;
437              return 1;
438           }
439        }
440
441        /* parse in the flag used by forbidden words */
442        if (strncmp(line,"FORBIDDENWORD",13) == 0) {
443           if (parse_flag(line, &forbiddenword, afflst)) {
444              delete afflst;
445              return 1;
446           }
447        }
448
449        /* parse in the flag used by forbidden words */
450        if (strncmp(line,"LEMMA_PRESENT",13) == 0) {
451           if (parse_flag(line, &lemma_present, afflst)) {
452              delete afflst;
453              return 1;
454           }
455        }
456
457        /* parse in the flag used by circumfixes */
458        if (strncmp(line,"CIRCUMFIX",9) == 0) {
459           if (parse_flag(line, &circumfix, afflst)) {
460              delete afflst;
461              return 1;
462           }
463        }
464
465        /* parse in the flag used by fogemorphemes */
466        if (strncmp(line,"ONLYINCOMPOUND",14) == 0) {
467           if (parse_flag(line, &onlyincompound, afflst)) {
468              delete afflst;
469              return 1;
470           }
471        }
472
473        /* parse in the flag used by `needaffixs' */
474        if (strncmp(line,"PSEUDOROOT",10) == 0) {
475           if (parse_flag(line, &needaffix, afflst)) {
476              delete afflst;
477              return 1;
478           }
479        }
480
481        /* parse in the flag used by `needaffixs' */
482        if (strncmp(line,"NEEDAFFIX",9) == 0) {
483           if (parse_flag(line, &needaffix, afflst)) {
484              delete afflst;
485              return 1;
486           }
487        }
488
489        /* parse in the minimal length for words in compounds */
490        if (strncmp(line,"COMPOUNDMIN",11) == 0) {
491           if (parse_num(line, &cpdmin, afflst)) {
492              delete afflst;
493              return 1;
494           }
495           if (cpdmin < 1) cpdmin = 1;
496        }
497
498        /* parse in the max. words and syllables in compounds */
499        if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) {
500           if (parse_cpdsyllable(line, afflst)) {
501              delete afflst;
502              return 1;
503           }
504        }
505
506        /* parse in the flag used by compound_check() method */
507        if (strncmp(line,"SYLLABLENUM",11) == 0) {
508           if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) {
509              delete afflst;
510              return 1;
511           }
512        }
513
514        /* parse in the flag used by the controlled compound words */
515        if (strncmp(line,"CHECKNUM",8) == 0) {
516            checknum=1;
517        }
518
519        /* parse in the extra word characters */
520        if (strncmp(line,"WORDCHARS",9) == 0) {
521           if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) {
522              delete afflst;
523              return 1;
524           }
525        }
526
527        /* parse in the ignored characters (for example, Arabic optional diacretics charachters */
528        if (strncmp(line,"IGNORE",6) == 0) {
529           if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
530              delete afflst;
531              return 1;
532           }
533        }
534
535        /* parse in the typical fault correcting table */
536        if (strncmp(line,"REP",3) == 0) {
537           if (parse_reptable(line, afflst)) {
538              delete afflst;
539              return 1;
540           }
541        }
542
543        /* parse in the input conversion table */
544        if (strncmp(line,"ICONV",5) == 0) {
545           if (parse_convtable(line, afflst, &iconvtable, "ICONV")) {
546              delete afflst;
547              return 1;
548           }
549        }
550
551        /* parse in the input conversion table */
552        if (strncmp(line,"OCONV",5) == 0) {
553           if (parse_convtable(line, afflst, &oconvtable, "OCONV")) {
554              delete afflst;
555              return 1;
556           }
557        }
558
559        /* parse in the phonetic translation table */
560        if (strncmp(line,"PHONE",5) == 0) {
561           if (parse_phonetable(line, afflst)) {
562              delete afflst;
563              return 1;
564           }
565        }
566
567        /* parse in the checkcompoundpattern table */
568        if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) {
569           if (parse_checkcpdtable(line, afflst)) {
570              delete afflst;
571              return 1;
572           }
573        }
574
575        /* parse in the defcompound table */
576        if (strncmp(line,"COMPOUNDRULE",12) == 0) {
577           if (parse_defcpdtable(line, afflst)) {
578              delete afflst;
579              return 1;
580           }
581        }
582
583        /* parse in the related character map table */
584        if (strncmp(line,"MAP",3) == 0) {
585           if (parse_maptable(line, afflst)) {
586              delete afflst;
587              return 1;
588           }
589        }
590
591        /* parse in the word breakpoints table */
592        if (strncmp(line,"BREAK",5) == 0) {
593           if (parse_breaktable(line, afflst)) {
594              delete afflst;
595              return 1;
596           }
597        }
598
599        /* parse in the language for language specific codes */
600        if (strncmp(line,"LANG",4) == 0) {
601           if (parse_string(line, &lang, afflst->getlinenum())) {
602              delete afflst;
603              return 1;
604           }
605           langnum = get_lang_num(lang);
606        }
607
608        if (strncmp(line,"VERSION",7) == 0) {
609           for(line = line + 7; *line == ' ' || *line == '\t'; line++);
610           version = mystrdup(line);
611        }
612
613        if (strncmp(line,"MAXNGRAMSUGS",12) == 0) {
614           if (parse_num(line, &maxngramsugs, afflst)) {
615              delete afflst;
616              return 1;
617           }
618        }
619
620        if (strncmp(line,"ONLYMAXDIFF", 11) == 0)
621                    onlymaxdiff = 1;
622
623        if (strncmp(line,"MAXDIFF",7) == 0) {
624           if (parse_num(line, &maxdiff, afflst)) {
625              delete afflst;
626              return 1;
627           }
628        }
629
630        if (strncmp(line,"MAXCPDSUGS",10) == 0) {
631           if (parse_num(line, &maxcpdsugs, afflst)) {
632              delete afflst;
633              return 1;
634           }
635        }
636
637        if (strncmp(line,"NOSPLITSUGS",11) == 0) {
638                    nosplitsugs=1;
639        }
640
641        if (strncmp(line,"FULLSTRIP",9) == 0) {
642                    fullstrip=1;
643        }
644
645        if (strncmp(line,"SUGSWITHDOTS",12) == 0) {
646                    sugswithdots=1;
647        }
648
649        /* parse in the flag used by forbidden words */
650        if (strncmp(line,"KEEPCASE",8) == 0) {
651           if (parse_flag(line, &keepcase, afflst)) {
652              delete afflst;
653              return 1;
654           }
655        }
656
657        /* parse in the flag used by `forceucase' */
658        if (strncmp(line,"FORCEUCASE",10) == 0) {
659           if (parse_flag(line, &forceucase, afflst)) {
660              delete afflst;
661              return 1;
662           }
663        }
664
665        /* parse in the flag used by `warn' */
666        if (strncmp(line,"WARN",4) == 0) {
667           if (parse_flag(line, &warn, afflst)) {
668              delete afflst;
669              return 1;
670           }
671        }
672
673        if (strncmp(line,"FORBIDWARN",10) == 0) {
674                    forbidwarn=1;
675        }
676
677        /* parse in the flag used by the affix generator */
678        if (strncmp(line,"SUBSTANDARD",11) == 0) {
679           if (parse_flag(line, &substandard, afflst)) {
680              delete afflst;
681              return 1;
682           }
683        }
684
685        if (strncmp(line,"CHECKSHARPS",11) == 0) {
686                    checksharps=1;
687        }
688
689        /* parse this affix: P - prefix, S - suffix */
690        ft = ' ';
691        if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P';
692        if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S';
693        if (ft != ' ') {
694           if (dupflags_ini) {
695             memset(dupflags, 0, sizeof(dupflags));
696             dupflags_ini = 0;
697           }
698           if (parse_affix(line, ft, afflst, dupflags)) {
699              delete afflst;
700              process_pfx_tree_to_list();
701              process_sfx_tree_to_list();
702              return 1;
703           }
704        }
705
706     }
707     delete afflst;
708
709     // convert affix trees to sorted list
710     process_pfx_tree_to_list();
711     process_sfx_tree_to_list();
712
713     // now we can speed up performance greatly taking advantage of the 
714     // relationship between the affixes and the idea of "subsets".
715
716     // View each prefix as a potential leading subset of another and view
717     // each suffix (reversed) as a potential trailing subset of another.
718
719     // To illustrate this relationship if we know the prefix "ab" is found in the
720     // word to examine, only prefixes that "ab" is a leading subset of need be examined.
721     // Furthermore is "ab" is not present then none of the prefixes that "ab" is
722     // is a subset need be examined.
723     // The same argument goes for suffix string that are reversed.
724
725     // Then to top this off why not examine the first char of the word to quickly
726     // limit the set of prefixes to examine (i.e. the prefixes to examine must 
727     // be leading supersets of the first character of the word (if they exist)
728  
729     // To take advantage of this "subset" relationship, we need to add two links
730     // from entry.  One to take next if the current prefix is found (call it nexteq)
731     // and one to take next if the current prefix is not found (call it nextne).
732
733     // Since we have built ordered lists, all that remains is to properly initialize 
734     // the nextne and nexteq pointers that relate them
735
736     process_pfx_order();
737     process_sfx_order();
738
739     /* get encoding for CHECKCOMPOUNDCASE */
740     if (!utf8) {
741     char * enc = get_encoding();
742     csconv = get_current_cs(enc);
743     free(enc);
744     enc = NULL;
745
746     char expw[MAXLNLEN];
747     if (wordchars) {
748         strcpy(expw, wordchars);
749         free(wordchars);
750     } else *expw = '\0';
751
752     for (int i = 0; i <= 255; i++) {
753         if ( (csconv[i].cupper != csconv[i].clower) &&
754             (! strchr(expw, (char) i))) {
755                 *(expw + strlen(expw) + 1) = '\0';
756                 *(expw + strlen(expw)) = (char) i;
757         }
758     }
759
760     wordchars = mystrdup(expw);
761     }
762
763     // default BREAK definition
764     if (numbreak == -1) {
765         breaktable = (char **) malloc(sizeof(char *) * 3);
766         if (!breaktable) return 1;
767         breaktable[0] = mystrdup("-");
768         breaktable[1] = mystrdup("^-");
769         breaktable[2] = mystrdup("-$");
770         if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3;
771     }
772     return 0;
773 }
774
775
776 // we want to be able to quickly access prefix information
777 // both by prefix flag, and sorted by prefix string itself 
778 // so we need to set up two indexes
779
780 int AffixMgr::build_pfxtree(PfxEntry* pfxptr)
781 {
782   PfxEntry * ptr;
783   PfxEntry * pptr;
784   PfxEntry * ep = pfxptr;
785
786   // get the right starting points
787   const char * key = ep->getKey();
788   const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
789
790   // first index by flag which must exist
791   ptr = pFlag[flg];
792   ep->setFlgNxt(ptr);
793   pFlag[flg] = ep;
794
795
796   // handle the special case of null affix string
797   if (strlen(key) == 0) {
798     // always inset them at head of list at element 0
799      ptr = pStart[0];
800      ep->setNext(ptr);
801      pStart[0] = ep;
802      return 0;
803   }
804
805   // now handle the normal case
806   ep->setNextEQ(NULL);
807   ep->setNextNE(NULL);
808
809   unsigned char sp = *((const unsigned char *)key);
810   ptr = pStart[sp];
811   
812   // handle the first insert 
813   if (!ptr) {
814      pStart[sp] = ep;
815      return 0;
816   }
817
818
819   // otherwise use binary tree insertion so that a sorted
820   // list can easily be generated later
821   pptr = NULL;
822   for (;;) {
823     pptr = ptr;
824     if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
825        ptr = ptr->getNextEQ();
826        if (!ptr) {
827           pptr->setNextEQ(ep);
828           break;
829        }
830     } else {
831        ptr = ptr->getNextNE();
832        if (!ptr) {
833           pptr->setNextNE(ep);
834           break;
835        }
836     }
837   }
838   return 0;
839 }
840
841 // we want to be able to quickly access suffix information
842 // both by suffix flag, and sorted by the reverse of the
843 // suffix string itself; so we need to set up two indexes
844 int AffixMgr::build_sfxtree(SfxEntry* sfxptr)
845 {
846   SfxEntry * ptr;
847   SfxEntry * pptr;
848   SfxEntry * ep = sfxptr;
849
850   /* get the right starting point */
851   const char * key = ep->getKey();
852   const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF);
853
854   // first index by flag which must exist
855   ptr = sFlag[flg];
856   ep->setFlgNxt(ptr);
857   sFlag[flg] = ep;
858
859   // next index by affix string
860
861   // handle the special case of null affix string
862   if (strlen(key) == 0) {
863     // always inset them at head of list at element 0
864      ptr = sStart[0];
865      ep->setNext(ptr);
866      sStart[0] = ep;
867      return 0;
868   }
869
870   // now handle the normal case
871   ep->setNextEQ(NULL);
872   ep->setNextNE(NULL);
873
874   unsigned char sp = *((const unsigned char *)key);
875   ptr = sStart[sp];
876   
877   // handle the first insert 
878   if (!ptr) {
879      sStart[sp] = ep;
880      return 0;
881   }
882
883   // otherwise use binary tree insertion so that a sorted
884   // list can easily be generated later
885   pptr = NULL;
886   for (;;) {
887     pptr = ptr;
888     if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) {
889        ptr = ptr->getNextEQ();
890        if (!ptr) {
891           pptr->setNextEQ(ep);
892           break;
893        }
894     } else {
895        ptr = ptr->getNextNE();
896        if (!ptr) {
897           pptr->setNextNE(ep);
898           break;
899        }
900     }
901   }
902   return 0;
903 }
904
905 // convert from binary tree to sorted list
906 int AffixMgr::process_pfx_tree_to_list()
907 {
908   for (int i=1; i< SETSIZE; i++) {
909     pStart[i] = process_pfx_in_order(pStart[i],NULL);
910   }
911   return 0;
912 }
913
914
915 PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr)
916 {
917   if (ptr) {
918     nptr = process_pfx_in_order(ptr->getNextNE(), nptr);
919     ptr->setNext(nptr);
920     nptr = process_pfx_in_order(ptr->getNextEQ(), ptr);
921   }
922   return nptr;
923 }
924
925
926 // convert from binary tree to sorted list
927 int AffixMgr:: process_sfx_tree_to_list()
928 {
929   for (int i=1; i< SETSIZE; i++) {
930     sStart[i] = process_sfx_in_order(sStart[i],NULL);
931   }
932   return 0;
933 }
934
935 SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr)
936 {
937   if (ptr) {
938     nptr = process_sfx_in_order(ptr->getNextNE(), nptr);
939     ptr->setNext(nptr);
940     nptr = process_sfx_in_order(ptr->getNextEQ(), ptr);
941   }
942   return nptr;
943 }
944
945
946 // reinitialize the PfxEntry links NextEQ and NextNE to speed searching
947 // using the idea of leading subsets this time
948 int AffixMgr::process_pfx_order()
949 {
950     PfxEntry* ptr;
951
952     // loop through each prefix list starting point
953     for (int i=1; i < SETSIZE; i++) {
954
955          ptr = pStart[i];
956
957          // look through the remainder of the list
958          //  and find next entry with affix that 
959          // the current one is not a subset of
960          // mark that as destination for NextNE
961          // use next in list that you are a subset
962          // of as NextEQ
963
964          for (; ptr != NULL; ptr = ptr->getNext()) {
965
966              PfxEntry * nptr = ptr->getNext();
967              for (; nptr != NULL; nptr = nptr->getNext()) {
968                  if (! isSubset( ptr->getKey() , nptr->getKey() )) break;
969              }
970              ptr->setNextNE(nptr);
971              ptr->setNextEQ(NULL);
972              if ((ptr->getNext()) && isSubset(ptr->getKey() , (ptr->getNext())->getKey())) 
973                  ptr->setNextEQ(ptr->getNext());
974          }
975
976          // now clean up by adding smart search termination strings:
977          // if you are already a superset of the previous prefix
978          // but not a subset of the next, search can end here
979          // so set NextNE properly
980
981          ptr = pStart[i];
982          for (; ptr != NULL; ptr = ptr->getNext()) {
983              PfxEntry * nptr = ptr->getNext();
984              PfxEntry * mptr = NULL;
985              for (; nptr != NULL; nptr = nptr->getNext()) {
986                  if (! isSubset(ptr->getKey(),nptr->getKey())) break;
987                  mptr = nptr;
988              }
989              if (mptr) mptr->setNextNE(NULL);
990          }
991     }
992     return 0;
993 }
994
995 // initialize the SfxEntry links NextEQ and NextNE to speed searching
996 // using the idea of leading subsets this time
997 int AffixMgr::process_sfx_order()
998 {
999     SfxEntry* ptr;
1000
1001     // loop through each prefix list starting point
1002     for (int i=1; i < SETSIZE; i++) {
1003
1004          ptr = sStart[i];
1005
1006          // look through the remainder of the list
1007          //  and find next entry with affix that 
1008          // the current one is not a subset of
1009          // mark that as destination for NextNE
1010          // use next in list that you are a subset
1011          // of as NextEQ
1012
1013          for (; ptr != NULL; ptr = ptr->getNext()) {
1014              SfxEntry * nptr = ptr->getNext();
1015              for (; nptr != NULL; nptr = nptr->getNext()) {
1016                  if (! isSubset(ptr->getKey(),nptr->getKey())) break;
1017              }
1018              ptr->setNextNE(nptr);
1019              ptr->setNextEQ(NULL);
1020              if ((ptr->getNext()) && isSubset(ptr->getKey(),(ptr->getNext())->getKey())) 
1021                  ptr->setNextEQ(ptr->getNext());
1022          }
1023
1024
1025          // now clean up by adding smart search termination strings:
1026          // if you are already a superset of the previous suffix
1027          // but not a subset of the next, search can end here
1028          // so set NextNE properly
1029
1030          ptr = sStart[i];
1031          for (; ptr != NULL; ptr = ptr->getNext()) {
1032              SfxEntry * nptr = ptr->getNext();
1033              SfxEntry * mptr = NULL;
1034              for (; nptr != NULL; nptr = nptr->getNext()) {
1035                  if (! isSubset(ptr->getKey(),nptr->getKey())) break;
1036                  mptr = nptr;
1037              }
1038              if (mptr) mptr->setNextNE(NULL);
1039          }
1040     }
1041     return 0;
1042 }
1043
1044 // add flags to the result for dictionary debugging
1045 void AffixMgr::debugflag(char * result, unsigned short flag) {
1046     char * st = encode_flag(flag);
1047     mystrcat(result, " ", MAXLNLEN);
1048     mystrcat(result, MORPH_FLAG, MAXLNLEN);
1049     if (st) {
1050         mystrcat(result, st, MAXLNLEN);
1051         free(st);
1052     }
1053 }
1054
1055 // calculate the character length of the condition
1056 int AffixMgr::condlen(char * st)
1057 {
1058   int l = 0;
1059   bool group = false;
1060   for(; *st; st++) {
1061     if (*st == '[') {
1062         group = true;
1063         l++;
1064     } else if (*st == ']') group = false;
1065     else if (!group && (!utf8 ||
1066         (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++;
1067   }
1068   return l;
1069 }
1070
1071 int AffixMgr::encodeit(affentry &entry, char * cs)
1072 {
1073   if (strcmp(cs,".") != 0) {
1074     entry.numconds = (char) condlen(cs);
1075     strncpy(entry.c.conds, cs, MAXCONDLEN);
1076     // long condition (end of conds padded by strncpy)
1077     if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) {
1078       entry.opts += aeLONGCOND;
1079       entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1);
1080       if (!entry.c.l.conds2) return 1;
1081     }
1082   } else {
1083     entry.numconds = 0;
1084     entry.c.conds[0] = '\0';
1085   }
1086   return 0;
1087 }
1088
1089 // return 1 if s1 is a leading subset of s2 (dots are for infixes)
1090 inline int AffixMgr::isSubset(const char * s1, const char * s2)
1091  {
1092     while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) {
1093         s1++;
1094         s2++;
1095     }
1096     return (*s1 == '\0');
1097  }
1098
1099
1100 // check word for prefixes
1101 struct hentry * AffixMgr::prefix_check(const char * word, int len, char in_compound,
1102     const FLAG needflag)
1103 {
1104     struct hentry * rv= NULL;
1105
1106     pfx = NULL;
1107     pfxappnd = NULL;
1108     sfxappnd = NULL;
1109     
1110     // first handle the special case of 0 length prefixes
1111     PfxEntry * pe = pStart[0];
1112     while (pe) {
1113         if (
1114             // fogemorpheme
1115               ((in_compound != IN_CPD_NOT) || !(pe->getCont() &&
1116                   (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) &&
1117             // permit prefixes in compounds
1118               ((in_compound != IN_CPD_END) || (pe->getCont() &&
1119                   (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen()))))
1120               ) {
1121                     // check prefix
1122                     rv = pe->checkword(word, len, in_compound, needflag);
1123                     if (rv) {
1124                         pfx=pe; // BUG: pfx not stateless
1125                         return rv;
1126                     }
1127              }
1128        pe = pe->getNext();
1129     }
1130   
1131     // now handle the general case
1132     unsigned char sp = *((const unsigned char *)word);
1133     PfxEntry * pptr = pStart[sp];
1134
1135     while (pptr) {
1136         if (isSubset(pptr->getKey(),word)) {
1137              if (
1138             // fogemorpheme
1139               ((in_compound != IN_CPD_NOT) || !(pptr->getCont() &&
1140                   (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) &&
1141             // permit prefixes in compounds
1142               ((in_compound != IN_CPD_END) || (pptr->getCont() &&
1143                   (TESTAFF(pptr->getCont(), compoundpermitflag, pptr->getContLen()))))
1144               ) {
1145             // check prefix
1146                   rv = pptr->checkword(word, len, in_compound, needflag);
1147                   if (rv) {
1148                     pfx=pptr; // BUG: pfx not stateless
1149                     return rv;
1150                   }
1151              }
1152              pptr = pptr->getNextEQ();
1153         } else {
1154              pptr = pptr->getNextNE();
1155         }
1156     }
1157     
1158     return NULL;
1159 }
1160
1161 // check word for prefixes
1162 struct hentry * AffixMgr::prefix_check_twosfx(const char * word, int len,
1163     char in_compound, const FLAG needflag)
1164 {
1165     struct hentry * rv= NULL;
1166
1167     pfx = NULL;
1168     sfxappnd = NULL;
1169     
1170     // first handle the special case of 0 length prefixes
1171     PfxEntry * pe = pStart[0];
1172     
1173     while (pe) {
1174         rv = pe->check_twosfx(word, len, in_compound, needflag);
1175         if (rv) return rv;
1176         pe = pe->getNext();
1177     }
1178   
1179     // now handle the general case
1180     unsigned char sp = *((const unsigned char *)word);
1181     PfxEntry * pptr = pStart[sp];
1182
1183     while (pptr) {
1184         if (isSubset(pptr->getKey(),word)) {
1185             rv = pptr->check_twosfx(word, len, in_compound, needflag);
1186             if (rv) {
1187                 pfx = pptr;
1188                 return rv;
1189             }
1190             pptr = pptr->getNextEQ();
1191         } else {
1192              pptr = pptr->getNextNE();
1193         }
1194     }
1195     
1196     return NULL;
1197 }
1198
1199 // check word for prefixes
1200 char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound,
1201     const FLAG needflag)
1202 {
1203     char * st;
1204
1205     char result[MAXLNLEN];
1206     result[0] = '\0';
1207
1208     pfx = NULL;
1209     sfxappnd = NULL;
1210     
1211     // first handle the special case of 0 length prefixes
1212     PfxEntry * pe = pStart[0];
1213     while (pe) {
1214        st = pe->check_morph(word,len,in_compound, needflag);
1215        if (st) {
1216             mystrcat(result, st, MAXLNLEN);
1217             free(st);
1218        }
1219        // if (rv) return rv;
1220        pe = pe->getNext();
1221     }
1222   
1223     // now handle the general case
1224     unsigned char sp = *((const unsigned char *)word);
1225     PfxEntry * pptr = pStart[sp];
1226
1227     while (pptr) {
1228         if (isSubset(pptr->getKey(),word)) {
1229             st = pptr->check_morph(word,len,in_compound, needflag);
1230             if (st) {
1231               // fogemorpheme
1232               if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() && 
1233                         (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) {
1234                     mystrcat(result, st, MAXLNLEN);
1235                     pfx = pptr;
1236                 }
1237                 free(st);
1238             }
1239             pptr = pptr->getNextEQ();
1240         } else {
1241             pptr = pptr->getNextNE();
1242         }
1243     }
1244     
1245     if (*result) return mystrdup(result);
1246     return NULL;
1247 }
1248
1249
1250 // check word for prefixes
1251 char * AffixMgr::prefix_check_twosfx_morph(const char * word, int len,
1252     char in_compound, const FLAG needflag)
1253 {
1254     char * st;
1255
1256     char result[MAXLNLEN];
1257     result[0] = '\0';
1258
1259     pfx = NULL;
1260     sfxappnd = NULL;
1261     
1262     // first handle the special case of 0 length prefixes
1263     PfxEntry * pe = pStart[0];
1264     while (pe) {
1265         st = pe->check_twosfx_morph(word,len,in_compound, needflag);
1266         if (st) {
1267             mystrcat(result, st, MAXLNLEN);
1268             free(st);
1269         }
1270         pe = pe->getNext();
1271     }
1272   
1273     // now handle the general case
1274     unsigned char sp = *((const unsigned char *)word);
1275     PfxEntry * pptr = pStart[sp];
1276
1277     while (pptr) {
1278         if (isSubset(pptr->getKey(),word)) {
1279             st = pptr->check_twosfx_morph(word, len, in_compound, needflag);
1280             if (st) {
1281                 mystrcat(result, st, MAXLNLEN);
1282                 free(st);
1283                 pfx = pptr;
1284             }
1285             pptr = pptr->getNextEQ();
1286         } else {
1287             pptr = pptr->getNextNE();
1288         }
1289     }
1290     
1291     if (*result) return mystrdup(result);
1292     return NULL;
1293 }
1294
1295 // Is word a non compound with a REP substitution (see checkcompoundrep)?
1296 int AffixMgr::cpdrep_check(const char * word, int wl)
1297 {
1298   char candidate[MAXLNLEN];
1299   const char * r;
1300   int lenr, lenp;
1301
1302   if ((wl < 2) || !numrep) return 0;
1303
1304   for (int i=0; i < numrep; i++ ) {
1305       r = word;
1306       lenr = strlen(reptable[i].pattern2);
1307       lenp = strlen(reptable[i].pattern);
1308       // search every occurence of the pattern in the word
1309       while ((r=strstr(r, reptable[i].pattern)) != NULL) {
1310           strcpy(candidate, word);
1311           if (r-word + lenr + strlen(r+lenp) >= MAXLNLEN) break;
1312           strcpy(candidate+(r-word),reptable[i].pattern2);
1313           strcpy(candidate+(r-word)+lenr, r+lenp);
1314           if (candidate_check(candidate,strlen(candidate))) return 1;
1315           r++; // search for the next letter
1316       }
1317    }
1318    return 0;
1319 }
1320
1321 // forbid compoundings when there are special patterns at word bound
1322 int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2, const char affixed)
1323 {
1324   int len;
1325   for (int i = 0; i < numcheckcpd; i++) {
1326       if (isSubset(checkcpdtable[i].pattern2, word + pos) &&
1327         (!r1 || !checkcpdtable[i].cond ||
1328           (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) &&
1329         (!r2 || !checkcpdtable[i].cond2 ||
1330           (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) &&
1331         // zero length pattern => only TESTAFF
1332         // zero pattern (0/flag) => unmodified stem (zero affixes allowed)
1333         (!*(checkcpdtable[i].pattern) || (
1334             (*(checkcpdtable[i].pattern)=='0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) ||
1335             (*(checkcpdtable[i].pattern)!='0' && (len = strlen(checkcpdtable[i].pattern)) &&
1336                 strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) {
1337             return 1;
1338         }
1339   }
1340   return 0;
1341 }
1342
1343 // forbid compounding with neighbouring upper and lower case characters at word bounds
1344 int AffixMgr::cpdcase_check(const char * word, int pos)
1345 {
1346   if (utf8) {
1347       w_char u, w;
1348       const char * p;
1349       u8_u16(&u, 1, word + pos);
1350       for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--);
1351       u8_u16(&w, 1, p);
1352       unsigned short a = (u.h << 8) + u.l;
1353       unsigned short b = (w.h << 8) + w.l;
1354       if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) &&
1355           (a != '-') && (b != '-')) return 1;
1356   } else {
1357       unsigned char a = *(word + pos - 1);
1358       unsigned char b = *(word + pos);
1359       if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1;
1360   }
1361   return 0;
1362 }
1363
1364 // check compound patterns
1365 int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all)
1366 {
1367   signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking
1368   signed short btwp[MAXWORDLEN]; // word positions for metacharacters
1369   int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions
1370   short bt = 0;  
1371   int i, j;
1372   int ok;
1373   int w = 0;
1374
1375   if (!*words) {
1376     w = 1;
1377     *words = def;
1378   }
1379
1380   if (!*words) {
1381     return 0;
1382   }
1383
1384   (*words)[wnum] = rv;
1385
1386   // has the last word COMPOUNDRULE flag?
1387   if (rv->alen == 0) {
1388     (*words)[wnum] = NULL;
1389     if (w) *words = NULL;
1390     return 0;
1391   }
1392   ok = 0;
1393   for (i = 0; i < numdefcpd; i++) {
1394     for (j = 0; j < defcpdtable[i].len; j++) {
1395        if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' &&
1396           TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) ok = 1;
1397     }
1398   }
1399   if (ok == 0) {
1400     (*words)[wnum] = NULL;
1401     if (w) *words = NULL;
1402     return 0;
1403   }
1404
1405   for (i = 0; i < numdefcpd; i++) {
1406     signed short pp = 0; // pattern position
1407     signed short wp = 0; // "words" position
1408     int ok2;
1409     ok = 1;
1410     ok2 = 1;
1411     do {
1412       while ((pp < defcpdtable[i].len) && (wp <= wnum)) {
1413         if (((pp+1) < defcpdtable[i].len) &&
1414           ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) {
1415             int wend = (defcpdtable[i].def[pp+1] == '?') ? wp : wnum;
1416             ok2 = 1;
1417             pp+=2;
1418             btpp[bt] = pp;
1419             btwp[bt] = wp;
1420             while (wp <= wend) {
1421                 if (!(*words)[wp]->alen || 
1422                   !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp-2], (*words)[wp]->alen)) {
1423                     ok2 = 0;
1424                     break;
1425                 }
1426                 wp++;
1427             }
1428             if (wp <= wnum) ok2 = 0;
1429             btnum[bt] = wp - btwp[bt];
1430             if (btnum[bt] > 0) bt++;
1431             if (ok2) break;
1432         } else {
1433             ok2 = 1;
1434             if (!(*words)[wp] || !(*words)[wp]->alen || 
1435               !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], (*words)[wp]->alen)) {
1436                 ok = 0;
1437                 break;
1438             }
1439             pp++;
1440             wp++;
1441             if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0;
1442         }
1443       }
1444     if (ok && ok2) { 
1445         int r = pp;
1446         while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) &&
1447             ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2;
1448         if (defcpdtable[i].len <= r) return 1;
1449     }
1450     // backtrack
1451     if (bt) do {
1452         ok = 1;
1453         btnum[bt - 1]--;
1454         pp = btpp[bt - 1];
1455         wp = btwp[bt - 1] + (signed short) btnum[bt - 1];
1456     } while ((btnum[bt - 1] < 0) && --bt);
1457   } while (bt);
1458
1459   if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1;
1460
1461   // check zero ending
1462   while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) &&
1463     ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2;
1464   if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1;
1465   }
1466   (*words)[wnum] = NULL;
1467   if (w) *words = NULL;
1468   return 0;
1469 }
1470
1471 inline int AffixMgr::candidate_check(const char * word, int len)
1472 {
1473   struct hentry * rv=NULL;
1474   
1475   rv = lookup(word);
1476   if (rv) return 1;
1477
1478 //  rv = prefix_check(word,len,1);
1479 //  if (rv) return 1;
1480   
1481   rv = affix_check(word,len);
1482   if (rv) return 1;
1483   return 0;
1484 }
1485
1486 // calculate number of syllable for compound-checking
1487 short AffixMgr::get_syllable(const char * word, int wlen)
1488 {
1489     if (cpdmaxsyllable==0) return 0;
1490     
1491     short num=0;
1492
1493     if (!utf8) {
1494         for (int i=0; i<wlen; i++) {
1495             if (strchr(cpdvowels, word[i])) num++;
1496         }
1497     } else if (cpdvowels_utf16) {
1498         w_char w[MAXWORDUTF8LEN];
1499         int i = u8_u16(w, MAXWORDUTF8LEN, word);
1500         for (; i > 0; i--) {
1501             if (flag_bsearch((unsigned short *) cpdvowels_utf16,
1502                 ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++;
1503         }
1504     }
1505     return num;
1506 }
1507
1508 void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) {
1509     if (utf8) {
1510         int i;
1511         for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) {
1512           for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++);
1513         }
1514         for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) {
1515           for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--);
1516         }
1517     } else {
1518         *cmin = cpdmin;
1519         *cmax = len - cpdmin + 1;
1520     }
1521 }
1522
1523
1524 // check if compound word is correctly spelled
1525 // hu_mov_rule = spec. Hungarian rule (XXX)
1526 struct hentry * AffixMgr::compound_check(const char * word, int len, 
1527     short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL,
1528     char hu_mov_rule = 0, char is_sug = 0, int info = 0)
1529 {
1530     int i; 
1531     short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
1532     struct hentry * rv = NULL;
1533     struct hentry * rv_first;
1534     struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
1535     char st [MAXWORDUTF8LEN + 4];
1536     char ch = '\0';
1537     int cmin;
1538     int cmax;
1539     int striple = 0;
1540     int scpd = 0;
1541     int soldi = 0;
1542     int oldcmin = 0;
1543     int oldcmax = 0;
1544     int oldlen = 0;
1545     int checkedstriple = 0;
1546     int onlycpdrule;
1547     int affixed = 0;
1548
1549     int checked_prefix;
1550
1551     setcminmax(&cmin, &cmax, word, len);
1552
1553     strcpy(st, word);
1554
1555     for (i = cmin; i < cmax; i++) {
1556         // go to end of the UTF-8 character
1557         if (utf8) {
1558             for (; (st[i] & 0xc0) == 0x80; i++);
1559             if (i >= cmax) return NULL;
1560         }
1561
1562         onlycpdrule = 0;
1563
1564         do { // onlycpdrule loop
1565
1566         oldnumsyllable = numsyllable;
1567         oldwordnum = wordnum;
1568         checked_prefix = 0;
1569
1570         do { // simplified checkcompoundpattern loop
1571
1572         if (scpd > 0) {
1573           for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 ||
1574             strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++);
1575
1576           if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop
1577           strcpy(st + i, checkcpdtable[scpd-1].pattern);
1578           soldi = i;
1579           i += strlen(checkcpdtable[scpd-1].pattern);
1580           strcpy(st + i, checkcpdtable[scpd-1].pattern2);
1581           strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3));
1582
1583           oldlen = len;
1584           len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3);
1585           oldcmin = cmin;
1586           oldcmax = cmax;
1587           setcminmax(&cmin, &cmax, st, len);
1588
1589           cmax = len - cpdmin + 1;
1590         }
1591
1592         ch = st[i];
1593         st[i] = '\0';
1594
1595         sfx = NULL;
1596         pfx = NULL;
1597
1598         // FIRST WORD
1599
1600         affixed = 1;
1601         rv = lookup(st); // perhaps without prefix
1602
1603         // search homonym with compound flag
1604         while ((rv) && !hu_mov_rule &&
1605             ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
1606                 !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1607                   (compoundbegin && !wordnum && !onlycpdrule && 
1608                         TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
1609                   (compoundmiddle && wordnum && !words && !onlycpdrule &&
1610                     TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
1611                   (numdefcpd &&
1612                     ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
1613                     (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))) && (onlycpdrule = 1))) || // switch onlycpdrule on
1614                   (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL &&
1615                     !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)))
1616                   ) {
1617             rv = rv->next_homonym;
1618         }
1619
1620         if (rv) affixed = 0;
1621
1622         if (!rv) {
1623             if (onlycpdrule) break;
1624             if (compoundflag && !words && 
1625              !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
1626                 if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
1627                         FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
1628                     sfx->getCont() &&
1629                         ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag, 
1630                             sfx->getContLen())) || (compoundend &&
1631                         TESTAFF(sfx->getCont(), compoundend, 
1632                             sfx->getContLen())))) {
1633                         rv = NULL;
1634                 }
1635             }
1636
1637             if (rv ||
1638               (((wordnum == 0) && compoundbegin &&
1639                 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
1640                 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
1641               ((wordnum > 0) && compoundmiddle &&
1642                 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
1643                 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
1644               ) checked_prefix = 1;
1645         // else check forbiddenwords and needaffix
1646         } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1647             TESTAFF(rv->astr, needaffix, rv->alen) ||
1648             TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1649             (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen))
1650              )) {
1651                 st[i] = ch;
1652                 //continue;
1653                 break;
1654         }
1655
1656             // check non_compound flag in suffix and prefix
1657             if ((rv) && !hu_mov_rule &&
1658                 ((pfx && pfx->getCont() &&
1659                     TESTAFF(pfx->getCont(), compoundforbidflag, 
1660                         pfx->getContLen())) ||
1661                 (sfx && sfx->getCont() &&
1662                     TESTAFF(sfx->getCont(), compoundforbidflag, 
1663                         sfx->getContLen())))) {
1664                     rv = NULL;
1665             }
1666
1667             // check compoundend flag in suffix and prefix
1668             if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
1669                 ((pfx && pfx->getCont() &&
1670                     TESTAFF(pfx->getCont(), compoundend, 
1671                         pfx->getContLen())) ||
1672                 (sfx && sfx->getCont() &&
1673                     TESTAFF(sfx->getCont(), compoundend, 
1674                         sfx->getContLen())))) {
1675                     rv = NULL;
1676             }
1677
1678             // check compoundmiddle flag in suffix and prefix
1679             if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
1680                 ((pfx && pfx->getCont() &&
1681                     TESTAFF(pfx->getCont(), compoundmiddle, 
1682                         pfx->getContLen())) ||
1683                 (sfx && sfx->getCont() &&
1684                     TESTAFF(sfx->getCont(), compoundmiddle, 
1685                         sfx->getContLen())))) {
1686                     rv = NULL;
1687             }
1688
1689         // check forbiddenwords
1690         if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1691             TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1692             (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
1693                 return NULL;
1694             }
1695
1696         // increment word number, if the second root has a compoundroot flag
1697         if ((rv) && compoundroot && 
1698             (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1699                 wordnum++;
1700         }
1701
1702         // first word is acceptable in compound words?
1703         if (((rv) && 
1704           ( checked_prefix || (words && words[wnum]) ||
1705             (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1706             ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
1707             ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))// ||
1708 //            (numdefcpd && )
1709
1710 // LANG_hu section: spec. Hungarian rule
1711             || ((langnum == LANG_hu) && hu_mov_rule && (
1712                     TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes
1713                     TESTAFF(rv->astr, 'G', rv->alen) ||
1714                     TESTAFF(rv->astr, 'H', rv->alen)
1715                 )
1716               )
1717 // END of LANG_hu section
1718           ) &&
1719           (
1720              // test CHECKCOMPOUNDPATTERN conditions
1721              scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL || 
1722                 TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)
1723           )
1724           && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters
1725                    (word[i-1]==word[i]) && (
1726                       ((i>1) && (word[i-1]==word[i-2])) ||
1727                       ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
1728                    )
1729                ) ||
1730                (
1731                  checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i)
1732                ))
1733          )
1734 // LANG_hu section: spec. Hungarian rule
1735          || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
1736               (sfx && sfx->getCont() && ( // XXX hardwired Hungarian dic. codes
1737                         TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
1738                         TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
1739                     )
1740                )
1741              )
1742          ) { // first word is ok condition
1743
1744 // LANG_hu section: spec. Hungarian rule
1745             if (langnum == LANG_hu) {
1746                 // calculate syllable number of the word
1747                 numsyllable += get_syllable(st, i);
1748
1749                 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
1750                 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
1751             }
1752 // END of LANG_hu section
1753
1754             // NEXT WORD(S)
1755             rv_first = rv;
1756             st[i] = ch;
1757
1758         do { // striple loop
1759
1760             // check simplifiedtriple
1761             if (simplifiedtriple) { 
1762               if (striple) { 
1763                 checkedstriple = 1;
1764                 i--; // check "fahrt" instead of "ahrt" in "Schiffahrt"
1765               } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1;
1766             }
1767
1768             rv = lookup((st+i)); // perhaps without prefix
1769
1770         // search homonym with compound flag
1771         while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
1772                         !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1773                           (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
1774                            (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) ||
1775                              (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL &&
1776                                 !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
1777                            )) {
1778             rv = rv->next_homonym;
1779         }
1780
1781             // check FORCEUCASE
1782             if (rv && forceucase && (rv) &&
1783                 (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info & SPELL_ORIGCAP)) rv = NULL;
1784
1785             if (rv && words && words[wnum + 1]) return rv_first;
1786
1787             oldnumsyllable2 = numsyllable;
1788             oldwordnum2 = wordnum;
1789
1790
1791 // LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code
1792             if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
1793                 numsyllable--;
1794             }
1795 // END of LANG_hu section
1796
1797             // increment word number, if the second root has a compoundroot flag
1798             if ((rv) && (compoundroot) && 
1799                 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1800                     wordnum++;
1801             }
1802
1803             // check forbiddenwords
1804             if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1805                 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1806                (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
1807
1808             // second word is acceptable, as a root?
1809             // hungarian conventions: compounding is acceptable,
1810             // when compound forms consist of 2 words, or if more,
1811             // then the syllable number of root words must be 6, or lesser.
1812
1813             if ((rv) && (
1814                       (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
1815                       (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
1816                     )
1817                 && (
1818                       ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) || 
1819                       ((cpdmaxsyllable!=0) && 
1820                           (numsyllable + get_syllable(HENTRY_WORD(rv), rv->clen)<=cpdmaxsyllable))
1821                     ) &&
1822                (
1823                  // test CHECKCOMPOUNDPATTERN
1824                  !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv, 0)
1825                ) &&
1826                 (
1827                      (!checkcompounddup || (rv != rv_first))
1828                    )
1829             // test CHECKCOMPOUNDPATTERN conditions
1830                 && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
1831                       TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
1832                 )
1833                  {
1834                       // forbid compound word, if it is a non compound word with typical fault
1835                       if (checkcompoundrep && cpdrep_check(word,len)) return NULL;
1836                       return rv_first;
1837             }
1838
1839             numsyllable = oldnumsyllable2;
1840             wordnum = oldwordnum2;
1841
1842             // perhaps second word has prefix or/and suffix
1843             sfx = NULL;
1844             sfxflag = FLAG_NULL;
1845             rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL;
1846             if (!rv && compoundend && !onlycpdrule) {
1847                 sfx = NULL;
1848                 pfx = NULL;
1849                 rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END);
1850             }
1851
1852             if (!rv && numdefcpd && words) {
1853                 rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
1854                 if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first;
1855                 rv = NULL;
1856             }
1857
1858             // test CHECKCOMPOUNDPATTERN conditions (allowed forms)
1859             if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL || 
1860                 TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL;
1861
1862             // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds)
1863             if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL;
1864
1865             // check non_compound flag in suffix and prefix
1866             if ((rv) && 
1867                 ((pfx && pfx->getCont() &&
1868                     TESTAFF(pfx->getCont(), compoundforbidflag, 
1869                         pfx->getContLen())) ||
1870                 (sfx && sfx->getCont() &&
1871                     TESTAFF(sfx->getCont(), compoundforbidflag, 
1872                         sfx->getContLen())))) {
1873                     rv = NULL;
1874             }
1875
1876             // check FORCEUCASE
1877             if (rv && forceucase && (rv) &&
1878                 (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info & SPELL_ORIGCAP)) rv = NULL;
1879
1880             // check forbiddenwords
1881             if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
1882                 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
1883                (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL;
1884
1885             // pfxappnd = prefix of word+i, or NULL
1886             // calculate syllable number of prefix.
1887             // hungarian convention: when syllable number of prefix is more,
1888             // than 1, the prefix+word counts as two words.
1889
1890             if (langnum == LANG_hu) {
1891                 // calculate syllable number of the word
1892                 numsyllable += get_syllable(word + i, strlen(word + i));
1893
1894                 // - affix syllable num.
1895                 // XXX only second suffix (inflections, not derivations)
1896                 if (sfxappnd) {
1897                     char * tmp = myrevstrdup(sfxappnd);
1898                     numsyllable -= get_syllable(tmp, strlen(tmp));
1899                     free(tmp);
1900                 }
1901
1902                 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
1903                 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
1904
1905                 // increment syllable num, if last word has a SYLLABLENUM flag
1906                 // and the suffix is beginning `s'
1907
1908                 if (cpdsyllablenum) {
1909                     switch (sfxflag) {
1910                         case 'c': { numsyllable+=2; break; }
1911                         case 'J': { numsyllable += 1; break; }
1912                         case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
1913                     }
1914                 }
1915             }
1916
1917             // increment word number, if the second word has a compoundroot flag
1918             if ((rv) && (compoundroot) && 
1919                 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
1920                     wordnum++;
1921             }
1922
1923             // second word is acceptable, as a word with prefix or/and suffix?
1924             // hungarian conventions: compounding is acceptable,
1925             // when compound forms consist 2 word, otherwise
1926             // the syllable number of root words is 6, or lesser.
1927             if ((rv) && 
1928                     (
1929                       ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) || 
1930                       ((cpdmaxsyllable != 0) && 
1931                           (numsyllable <= cpdmaxsyllable))
1932                     )
1933                 && (
1934                    (!checkcompounddup || (rv != rv_first))
1935                    )) {
1936                     // forbid compound word, if it is a non compound word with typical fault
1937                     if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
1938                     return rv_first;
1939             }
1940
1941             numsyllable = oldnumsyllable2;
1942             wordnum = oldwordnum2;
1943
1944             // perhaps second word is a compound word (recursive call)
1945             if (wordnum < maxwordnum) {
1946                 rv = compound_check((st+i),strlen(st+i), wordnum+1,
1947                      numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info);
1948                 if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) ||
1949                    (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL;
1950             } else {
1951                 rv=NULL;
1952             }
1953             if (rv) {
1954                 // forbid compound word, if it is a non compound word with typical fault
1955                 if (checkcompoundrep || forbiddenword) {
1956                     struct hentry * rv2 = NULL;
1957                     if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
1958                     // check first part
1959                     if (strncmp(rv->word, word + i, rv->blen) == 0) {
1960                         char r = *(st + i + rv->blen);
1961                         *(st + i + rv->blen) = '\0';
1962                         if (checkcompoundrep && cpdrep_check(st, i + rv->blen)) return NULL;
1963                         if (forbiddenword) {
1964                             rv2 = lookup(word);
1965                             if (!rv2) rv2 = affix_check(word, len);
1966                             if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) && 
1967                                 (strncmp(rv2->word, st, i + rv->blen) == 0)) {
1968                                     return NULL;
1969                             }
1970                         }
1971                         *(st + i + rv->blen) = r;
1972                     }
1973                 }
1974
1975                 return rv_first;
1976             }
1977           } while (striple && !checkedstriple); // end of striple loop
1978
1979           if (checkedstriple) {
1980             i++;
1981             checkedstriple = 0;
1982             striple = 0;
1983           }
1984
1985         } // first word is ok condition
1986
1987         if (soldi != 0) {
1988           i = soldi;
1989           soldi = 0;
1990           len = oldlen;
1991           cmin = oldcmin;
1992           cmax = oldcmax;
1993         }
1994         scpd++;
1995
1996         } while (!onlycpdrule && simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop
1997
1998         scpd = 0;
1999         wordnum = oldwordnum;
2000         numsyllable = oldnumsyllable;
2001
2002         if (soldi != 0) {
2003           i = soldi;
2004           strcpy(st, word); // XXX add more optim.
2005           soldi = 0;
2006         } else st[i] = ch;
2007
2008         } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop
2009
2010     }
2011
2012     return NULL;
2013 }
2014
2015 // check if compound word is correctly spelled
2016 // hu_mov_rule = spec. Hungarian rule (XXX)
2017 int AffixMgr::compound_check_morph(const char * word, int len, 
2018     short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words,
2019     char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL)
2020 {
2021     int i;
2022     short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
2023     int ok = 0;
2024
2025     struct hentry * rv = NULL;
2026     struct hentry * rv_first;
2027     struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
2028     char st [MAXWORDUTF8LEN + 4];
2029     char ch;
2030
2031     int checked_prefix;
2032     char presult[MAXLNLEN];
2033
2034     int cmin;
2035     int cmax;
2036
2037     setcminmax(&cmin, &cmax, word, len);
2038
2039     strcpy(st, word);
2040
2041     for (i = cmin; i < cmax; i++) {
2042         oldnumsyllable = numsyllable;
2043         oldwordnum = wordnum;
2044         checked_prefix = 0;
2045
2046         // go to end of the UTF-8 character
2047         if (utf8) {
2048             for (; (st[i] & 0xc0) == 0x80; i++);
2049             if (i >= cmax) return 0;
2050         }
2051
2052         ch = st[i];
2053         st[i] = '\0';
2054         sfx = NULL;
2055
2056         // FIRST WORD
2057         *presult = '\0';
2058         if (partresult) mystrcat(presult, partresult, MAXLNLEN);
2059
2060         rv = lookup(st); // perhaps without prefix
2061
2062         // search homonym with compound flag
2063         while ((rv) && !hu_mov_rule && 
2064             ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
2065                 !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2066                 (compoundbegin && !wordnum &&
2067                         TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
2068                 (compoundmiddle && wordnum && !words &&
2069                     TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
2070                   (numdefcpd &&
2071                     ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
2072                     (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))
2073                   ))) {
2074             rv = rv->next_homonym;
2075         }
2076
2077         if (rv)  {
2078             sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st);
2079             if (!HENTRY_FIND(rv, MORPH_STEM)) {
2080                 sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st);
2081             }
2082             // store the pointer of the hash entry
2083 //            sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv);
2084             if (HENTRY_DATA(rv)) {
2085                 sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv));
2086             }
2087         }        
2088         if (!rv) {
2089             if (compoundflag && !words && 
2090              !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
2091                 if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
2092                         FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
2093                     sfx->getCont() &&
2094                         ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag, 
2095                             sfx->getContLen())) || (compoundend &&
2096                         TESTAFF(sfx->getCont(), compoundend, 
2097                             sfx->getContLen())))) {
2098                         rv = NULL;
2099                 }
2100             }
2101
2102             if (rv ||
2103               (((wordnum == 0) && compoundbegin &&
2104                 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
2105                 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
2106               ((wordnum > 0) && compoundmiddle &&
2107                 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
2108                 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
2109               ) {
2110                 // char * p = prefix_check_morph(st, i, 0, compound);
2111                 char * p = NULL;
2112                 if (compoundflag) p = affix_check_morph(st, i, compoundflag);
2113                 if (!p || (*p == '\0')) {
2114                    if (p) free(p);
2115                    p = NULL;
2116                    if ((wordnum == 0) && compoundbegin) {
2117                      p = affix_check_morph(st, i, compoundbegin);
2118                    } else if ((wordnum > 0) && compoundmiddle) {
2119                      p = affix_check_morph(st, i, compoundmiddle);                   
2120                    }
2121                 }
2122                 if (p && (*p != '\0')) {
2123                     sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD,
2124                         MORPH_PART, st, line_uniq_app(&p, MSEP_REC));
2125                 }
2126                 if (p) free(p);
2127                 checked_prefix = 1;
2128             }
2129         // else check forbiddenwords
2130         } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
2131             TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
2132             TESTAFF(rv->astr, needaffix, rv->alen))) {
2133                 st[i] = ch;
2134                 continue;
2135         }
2136
2137             // check non_compound flag in suffix and prefix
2138             if ((rv) && !hu_mov_rule &&
2139                 ((pfx && pfx->getCont() &&
2140                     TESTAFF(pfx->getCont(), compoundforbidflag, 
2141                         pfx->getContLen())) ||
2142                 (sfx && sfx->getCont() &&
2143                     TESTAFF(sfx->getCont(), compoundforbidflag, 
2144                         sfx->getContLen())))) {
2145                     continue;
2146             }
2147
2148             // check compoundend flag in suffix and prefix
2149             if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
2150                 ((pfx && pfx->getCont() &&
2151                     TESTAFF(pfx->getCont(), compoundend, 
2152                         pfx->getContLen())) ||
2153                 (sfx && sfx->getCont() &&
2154                     TESTAFF(sfx->getCont(), compoundend, 
2155                         sfx->getContLen())))) {
2156                     continue;
2157             }
2158
2159             // check compoundmiddle flag in suffix and prefix
2160             if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
2161                 ((pfx && pfx->getCont() &&
2162                     TESTAFF(pfx->getCont(), compoundmiddle, 
2163                         pfx->getContLen())) ||
2164                 (sfx && sfx->getCont() &&
2165                     TESTAFF(sfx->getCont(), compoundmiddle, 
2166                         sfx->getContLen())))) {
2167                     rv = NULL;
2168             }       
2169
2170         // check forbiddenwords
2171         if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen)
2172             || TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) continue;
2173
2174         // increment word number, if the second root has a compoundroot flag
2175         if ((rv) && (compoundroot) && 
2176             (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2177                 wordnum++;
2178         }
2179
2180         // first word is acceptable in compound words?
2181         if (((rv) && 
2182           ( checked_prefix || (words && words[wnum]) ||
2183             (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2184             ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
2185             ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen)) 
2186 // LANG_hu section: spec. Hungarian rule
2187             || ((langnum == LANG_hu) && // hu_mov_rule
2188                 hu_mov_rule && (
2189                     TESTAFF(rv->astr, 'F', rv->alen) ||
2190                     TESTAFF(rv->astr, 'G', rv->alen) ||
2191                     TESTAFF(rv->astr, 'H', rv->alen)
2192                 )
2193               )
2194 // END of LANG_hu section
2195           )
2196           && ! (( checkcompoundtriple && !words && // test triple letters
2197                    (word[i-1]==word[i]) && (
2198                       ((i>1) && (word[i-1]==word[i-2])) || 
2199                       ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
2200                    )
2201                ) ||
2202                (
2203                    // test CHECKCOMPOUNDPATTERN
2204                    numcheckcpd && !words && cpdpat_check(word, i, rv, NULL, 0)
2205                ) ||
2206                ( 
2207                  checkcompoundcase && !words && cpdcase_check(word, i)
2208                ))
2209          )
2210 // LANG_hu section: spec. Hungarian rule
2211          || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
2212               (sfx && sfx->getCont() && (
2213                         TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) ||
2214                         TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen())
2215                     )                
2216                )
2217              )
2218 // END of LANG_hu section
2219          ) {
2220
2221 // LANG_hu section: spec. Hungarian rule
2222             if (langnum == LANG_hu) {
2223                 // calculate syllable number of the word
2224                 numsyllable += get_syllable(st, i);
2225
2226                 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
2227                 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
2228             }
2229 // END of LANG_hu section
2230
2231             // NEXT WORD(S)
2232             rv_first = rv;
2233             rv = lookup((word+i)); // perhaps without prefix
2234
2235         // search homonym with compound flag
2236         while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
2237                         !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2238                           (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
2239                            (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) {
2240             rv = rv->next_homonym;
2241         }
2242
2243             if (rv && words && words[wnum + 1]) {
2244                   mystrcat(*result, presult, MAXLNLEN);
2245                   mystrcat(*result, " ", MAXLNLEN);
2246                   mystrcat(*result, MORPH_PART, MAXLNLEN);
2247                   mystrcat(*result, word+i, MAXLNLEN);
2248                   if (complexprefixes && HENTRY_DATA(rv)) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2249                   if (!HENTRY_FIND(rv, MORPH_STEM)) {
2250                     mystrcat(*result, " ", MAXLNLEN);
2251                     mystrcat(*result, MORPH_STEM, MAXLNLEN);
2252                     mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
2253                   }
2254                   // store the pointer of the hash entry
2255 //                  sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
2256                   if (!complexprefixes && HENTRY_DATA(rv)) {
2257                     mystrcat(*result, " ", MAXLNLEN);
2258                     mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2259                   }
2260                   mystrcat(*result, "\n", MAXLNLEN);
2261                   ok = 1;
2262                   return 0;
2263             }
2264
2265             oldnumsyllable2 = numsyllable;
2266             oldwordnum2 = wordnum;
2267
2268 // LANG_hu section: spec. Hungarian rule
2269             if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
2270                 numsyllable--;
2271             }
2272 // END of LANG_hu section
2273             // increment word number, if the second root has a compoundroot flag
2274             if ((rv) && (compoundroot) && 
2275                 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2276                     wordnum++;
2277             }
2278
2279             // check forbiddenwords
2280             if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
2281                 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) {
2282                 st[i] = ch;
2283                 continue;
2284             }
2285
2286             // second word is acceptable, as a root?
2287             // hungarian conventions: compounding is acceptable,
2288             // when compound forms consist of 2 words, or if more,
2289             // then the syllable number of root words must be 6, or lesser.
2290             if ((rv) && (
2291                       (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
2292                       (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
2293                     )
2294                 && (
2295                       ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) || 
2296                       ((cpdmaxsyllable!=0) &&
2297                           (numsyllable+get_syllable(HENTRY_WORD(rv),rv->blen)<=cpdmaxsyllable))
2298                     )
2299                 && (
2300                      (!checkcompounddup || (rv != rv_first))
2301                    )
2302                 )
2303                  {
2304                       // bad compound word
2305                       mystrcat(*result, presult, MAXLNLEN);
2306                       mystrcat(*result, " ", MAXLNLEN);
2307                       mystrcat(*result, MORPH_PART, MAXLNLEN);
2308                       mystrcat(*result, word+i, MAXLNLEN);
2309
2310                       if (HENTRY_DATA(rv)) {
2311                         if (complexprefixes) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2312                         if (! HENTRY_FIND(rv, MORPH_STEM)) {
2313                            mystrcat(*result, " ", MAXLNLEN);
2314                            mystrcat(*result, MORPH_STEM, MAXLNLEN);
2315                            mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN);
2316                         }
2317                         // store the pointer of the hash entry
2318 //                        sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
2319                         if (!complexprefixes) {
2320                             mystrcat(*result, " ", MAXLNLEN);
2321                             mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN);
2322                         }
2323                       }
2324                       mystrcat(*result, "\n", MAXLNLEN);
2325                               ok = 1;
2326             }
2327
2328             numsyllable = oldnumsyllable2 ;
2329             wordnum = oldwordnum2;
2330
2331             // perhaps second word has prefix or/and suffix
2332             sfx = NULL;
2333             sfxflag = FLAG_NULL;
2334
2335             if (compoundflag) rv = affix_check((word+i),strlen(word+i), compoundflag); else rv = NULL;
2336
2337             if (!rv && compoundend) {
2338                 sfx = NULL;
2339                 pfx = NULL;
2340                 rv = affix_check((word+i),strlen(word+i), compoundend);
2341             }
2342
2343             if (!rv && numdefcpd && words) {
2344                 rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
2345                 if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
2346                       char * m = NULL;
2347                       if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
2348                       if ((!m || *m == '\0') && compoundend) {
2349                             if (m) free(m);
2350                             m = affix_check_morph((word+i),strlen(word+i), compoundend);
2351                       }
2352                       mystrcat(*result, presult, MAXLNLEN);
2353                       if (m || (*m != '\0')) {
2354                         sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
2355                             MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
2356                       }
2357                       if (m) free(m);
2358                       mystrcat(*result, "\n", MAXLNLEN);
2359                       ok = 1;
2360                 }
2361             }
2362
2363             // check non_compound flag in suffix and prefix
2364             if ((rv) && 
2365                 ((pfx && pfx->getCont() &&
2366                     TESTAFF(pfx->getCont(), compoundforbidflag, 
2367                         pfx->getContLen())) ||
2368                 (sfx && sfx->getCont() &&
2369                     TESTAFF(sfx->getCont(), compoundforbidflag, 
2370                         sfx->getContLen())))) {
2371                     rv = NULL;
2372             }
2373
2374             // check forbiddenwords
2375             if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen) ||
2376                     TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))
2377                     && (! TESTAFF(rv->astr, needaffix, rv->alen))) {
2378                         st[i] = ch;
2379                         continue;
2380                     }
2381
2382             if (langnum == LANG_hu) {
2383                 // calculate syllable number of the word
2384                 numsyllable += get_syllable(word + i, strlen(word + i));
2385
2386                 // - affix syllable num.
2387                 // XXX only second suffix (inflections, not derivations)
2388                 if (sfxappnd) {
2389                     char * tmp = myrevstrdup(sfxappnd);
2390                     numsyllable -= get_syllable(tmp, strlen(tmp));
2391                     free(tmp);
2392                 }
2393
2394                 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
2395                 if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++;
2396
2397                 // increment syllable num, if last word has a SYLLABLENUM flag
2398                 // and the suffix is beginning `s'
2399
2400                 if (cpdsyllablenum) {
2401                     switch (sfxflag) {
2402                         case 'c': { numsyllable+=2; break; }
2403                         case 'J': { numsyllable += 1; break; }
2404                         case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
2405                     }
2406                 }
2407             }
2408
2409             // increment word number, if the second word has a compoundroot flag
2410             if ((rv) && (compoundroot) && 
2411                 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
2412                     wordnum++;
2413             }
2414             // second word is acceptable, as a word with prefix or/and suffix?
2415             // hungarian conventions: compounding is acceptable,
2416             // when compound forms consist 2 word, otherwise
2417             // the syllable number of root words is 6, or lesser.
2418             if ((rv) && 
2419                     (
2420                       ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) || 
2421                       ((cpdmaxsyllable!=0) &&
2422                           (numsyllable <= cpdmaxsyllable))
2423                     )
2424                 && (
2425                    (!checkcompounddup || (rv != rv_first))
2426                    )) {
2427                       char * m = NULL;
2428                       if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
2429                       if ((!m || *m == '\0') && compoundend) {
2430                             if (m) free(m);
2431                             m = affix_check_morph((word+i),strlen(word+i), compoundend);
2432                       }
2433                       mystrcat(*result, presult, MAXLNLEN);
2434                       if (m && (*m != '\0')) {
2435                         sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
2436                             MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
2437                       }
2438                       if (m) free(m);
2439                       sprintf(*result + strlen(*result), "%c", MSEP_REC);
2440                       ok = 1;
2441             }
2442
2443             numsyllable = oldnumsyllable2;
2444             wordnum = oldwordnum2;
2445
2446             // perhaps second word is a compound word (recursive call)
2447             if ((wordnum < maxwordnum) && (ok == 0)) {
2448                         compound_check_morph((word+i),strlen(word+i), wordnum+1, 
2449                              numsyllable, maxwordnum, wnum + 1, words, 0, result, presult);
2450             } else {
2451                 rv=NULL;
2452             }
2453         }
2454         st[i] = ch;
2455         wordnum = oldwordnum;
2456         numsyllable = oldnumsyllable;
2457     }
2458     return 0;
2459 }    
2460
2461  // return 1 if s1 (reversed) is a leading subset of end of s2
2462 /* inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
2463  {
2464     while ((len > 0) && *s1 && (*s1 == *end_of_s2)) {
2465         s1++;
2466         end_of_s2--;
2467         len--;
2468     }
2469     return (*s1 == '\0');
2470  }
2471  */
2472
2473 inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
2474  {
2475     while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) {
2476         s1++;
2477         end_of_s2--;
2478         len--;
2479     }
2480     return (*s1 == '\0');
2481  }
2482
2483 // check word for suffixes
2484
2485 struct hentry * AffixMgr::suffix_check (const char * word, int len, 
2486        int sfxopts, PfxEntry * ppfx, char ** wlst, int maxSug, int * ns, 
2487        const FLAG cclass, const FLAG needflag, char in_compound)
2488 {
2489     struct hentry * rv = NULL;
2490     PfxEntry* ep = ppfx;
2491
2492     // first handle the special case of 0 length suffixes
2493     SfxEntry * se = sStart[0];
2494
2495     while (se) {
2496         if (!cclass || se->getCont()) {
2497             // suffixes are not allowed in beginning of compounds
2498             if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2499              // except when signed with compoundpermitflag flag
2500              (se->getCont() && compoundpermitflag &&
2501                 TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
2502               // no circumfix flag in prefix and suffix
2503               ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2504                    circumfix, ep->getContLen())) &&
2505                (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
2506               // circumfix flag in prefix AND suffix
2507               ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2508                    circumfix, ep->getContLen())) &&
2509                (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen())))))  &&
2510             // fogemorpheme
2511               (in_compound || 
2512                  !(se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) &&
2513             // needaffix on prefix or first suffix
2514               (cclass || 
2515                    !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
2516                    (ppfx && !((ep->getCont()) &&
2517                      TESTAFF(ep->getCont(), needaffix,
2518                        ep->getContLen())))
2519               )) {
2520                 rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass, 
2521                     needflag, (in_compound ? 0 : onlyincompound));
2522                 if (rv) {
2523                     sfx=se; // BUG: sfx not stateless
2524                     return rv;
2525                 }
2526             }
2527         }
2528        se = se->getNext();
2529     }
2530
2531     // now handle the general case
2532     if (len == 0) return NULL; // FULLSTRIP
2533     unsigned char sp= *((const unsigned char *)(word + len - 1));
2534     SfxEntry * sptr = sStart[sp];
2535
2536     while (sptr) {
2537         if (isRevSubset(sptr->getKey(), word + len - 1, len)
2538         ) {
2539             // suffixes are not allowed in beginning of compounds
2540             if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2541              // except when signed with compoundpermitflag flag
2542              (sptr->getCont() && compoundpermitflag &&
2543                 TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
2544               // no circumfix flag in prefix and suffix
2545               ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2546                    circumfix, ep->getContLen())) &&
2547                (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
2548               // circumfix flag in prefix AND suffix
2549               ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2550                    circumfix, ep->getContLen())) &&
2551                (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))))  &&
2552             // fogemorpheme
2553               (in_compound || 
2554                  !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
2555             // needaffix on prefix or first suffix
2556               (cclass || 
2557                   !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
2558                   (ppfx && !((ep->getCont()) &&
2559                      TESTAFF(ep->getCont(), needaffix,
2560                        ep->getContLen())))
2561               )
2562             ) if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) {
2563                 rv = sptr->checkword(word,len, sfxopts, ppfx, wlst,
2564                     maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound));
2565                 if (rv) {
2566                     sfx=sptr; // BUG: sfx not stateless
2567                     sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2568                     if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2569                     return rv;
2570                 }
2571              }
2572              sptr = sptr->getNextEQ();
2573         } else {
2574              sptr = sptr->getNextNE();
2575         }
2576     }
2577
2578     return NULL;
2579 }
2580
2581 // check word for two-level suffixes
2582
2583 struct hentry * AffixMgr::suffix_check_twosfx(const char * word, int len, 
2584        int sfxopts, PfxEntry * ppfx, const FLAG needflag)
2585 {
2586     struct hentry * rv = NULL;
2587
2588     // first handle the special case of 0 length suffixes
2589     SfxEntry * se = sStart[0];
2590     while (se) {
2591         if (contclasses[se->getFlag()])
2592         {
2593             rv = se->check_twosfx(word,len, sfxopts, ppfx, needflag);
2594             if (rv) return rv;
2595         }
2596         se = se->getNext();
2597     }
2598
2599     // now handle the general case
2600     if (len == 0) return NULL; // FULLSTRIP
2601     unsigned char sp = *((const unsigned char *)(word + len - 1));
2602     SfxEntry * sptr = sStart[sp];
2603
2604     while (sptr) {
2605         if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
2606             if (contclasses[sptr->getFlag()])
2607             {
2608                 rv = sptr->check_twosfx(word,len, sfxopts, ppfx, needflag);
2609                 if (rv) {
2610                     sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2611                     if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2612                     return rv;
2613                 }
2614             }
2615             sptr = sptr->getNextEQ();
2616         } else {
2617              sptr = sptr->getNextNE();
2618         }
2619     }
2620
2621     return NULL;
2622 }
2623
2624 char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len, 
2625        int sfxopts, PfxEntry * ppfx, const FLAG needflag)
2626 {
2627     char result[MAXLNLEN];
2628     char result2[MAXLNLEN];
2629     char result3[MAXLNLEN];
2630     
2631     char * st;
2632
2633     result[0] = '\0';
2634     result2[0] = '\0';
2635     result3[0] = '\0';
2636
2637     // first handle the special case of 0 length suffixes
2638     SfxEntry * se = sStart[0];
2639     while (se) {
2640         if (contclasses[se->getFlag()])
2641         {
2642             st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
2643             if (st) {
2644                 if (ppfx) {
2645                     if (ppfx->getMorph()) {
2646                         mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2647                         mystrcat(result, " ", MAXLNLEN);
2648                     } else debugflag(result, ppfx->getFlag());
2649                 }
2650                 mystrcat(result, st, MAXLNLEN);
2651                 free(st);
2652                 if (se->getMorph()) {
2653                     mystrcat(result, " ", MAXLNLEN);
2654                     mystrcat(result, se->getMorph(), MAXLNLEN);
2655                 } else debugflag(result, se->getFlag());
2656                 mystrcat(result, "\n", MAXLNLEN);
2657             }
2658         }
2659         se = se->getNext();
2660     }
2661
2662     // now handle the general case
2663     if (len == 0) return NULL; // FULLSTRIP
2664     unsigned char sp = *((const unsigned char *)(word + len - 1));
2665     SfxEntry * sptr = sStart[sp];
2666
2667     while (sptr) {
2668         if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
2669             if (contclasses[sptr->getFlag()]) 
2670             {
2671                 st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
2672                 if (st) {
2673                     sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
2674                     if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
2675                     strcpy(result2, st);
2676                     free(st);
2677
2678                 result3[0] = '\0';
2679
2680                 if (sptr->getMorph()) {
2681                     mystrcat(result3, " ", MAXLNLEN);
2682                     mystrcat(result3, sptr->getMorph(), MAXLNLEN);
2683                 } else debugflag(result3, sptr->getFlag());
2684                 strlinecat(result2, result3);
2685                 mystrcat(result2, "\n", MAXLNLEN);
2686                 mystrcat(result,  result2, MAXLNLEN);
2687                 }
2688             }
2689             sptr = sptr->getNextEQ();
2690         } else {
2691              sptr = sptr->getNextNE();
2692         }
2693     }
2694     if (*result) return mystrdup(result);
2695     return NULL;
2696 }
2697
2698 char * AffixMgr::suffix_check_morph(const char * word, int len, 
2699        int sfxopts, PfxEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound)
2700 {
2701     char result[MAXLNLEN];
2702     
2703     struct hentry * rv = NULL;
2704
2705     result[0] = '\0';
2706
2707     PfxEntry* ep = ppfx;
2708
2709     // first handle the special case of 0 length suffixes
2710     SfxEntry * se = sStart[0];
2711     while (se) {
2712         if (!cclass || se->getCont()) {
2713             // suffixes are not allowed in beginning of compounds
2714             if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2715              // except when signed with compoundpermitflag flag
2716              (se->getCont() && compoundpermitflag &&
2717                 TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix ||
2718               // no circumfix flag in prefix and suffix
2719               ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2720                    circumfix, ep->getContLen())) &&
2721                (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
2722               // circumfix flag in prefix AND suffix
2723               ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2724                    circumfix, ep->getContLen())) &&
2725                (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen())))))  &&
2726             // fogemorpheme
2727               (in_compound || 
2728                  !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) &&
2729             // needaffix on prefix or first suffix
2730               (cclass || 
2731                    !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
2732                    (ppfx && !((ep->getCont()) &&
2733                      TESTAFF(ep->getCont(), needaffix,
2734                        ep->getContLen())))
2735               )
2736             ))
2737             rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
2738          while (rv) {
2739            if (ppfx) {
2740                 if (ppfx->getMorph()) {
2741                     mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2742                     mystrcat(result, " ", MAXLNLEN);
2743                 } else debugflag(result, ppfx->getFlag());
2744             }
2745             if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2746             if (! HENTRY_FIND(rv, MORPH_STEM)) {
2747                 mystrcat(result, " ", MAXLNLEN);                                
2748                 mystrcat(result, MORPH_STEM, MAXLNLEN);
2749                 mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
2750             }
2751             // store the pointer of the hash entry
2752 //            sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
2753             
2754             if (!complexprefixes && HENTRY_DATA(rv)) {
2755                     mystrcat(result, " ", MAXLNLEN);                                
2756                     mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2757             }
2758             if (se->getMorph()) {
2759                 mystrcat(result, " ", MAXLNLEN);                                
2760                 mystrcat(result, se->getMorph(), MAXLNLEN);
2761             } else debugflag(result, se->getFlag());
2762             mystrcat(result, "\n", MAXLNLEN);
2763             rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
2764          }
2765        }
2766        se = se->getNext();
2767     }
2768
2769     // now handle the general case
2770     if (len == 0) return NULL; // FULLSTRIP
2771     unsigned char sp = *((const unsigned char *)(word + len - 1));
2772     SfxEntry * sptr = sStart[sp];
2773
2774     while (sptr) {
2775         if (isRevSubset(sptr->getKey(), word + len - 1, len)
2776         ) {
2777             // suffixes are not allowed in beginning of compounds
2778             if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass
2779              // except when signed with compoundpermitflag flag
2780              (sptr->getCont() && compoundpermitflag &&
2781                 TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix ||
2782               // no circumfix flag in prefix and suffix
2783               ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(),
2784                    circumfix, ep->getContLen())) &&
2785                (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
2786               // circumfix flag in prefix AND suffix
2787               ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
2788                    circumfix, ep->getContLen())) &&
2789                (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))))  &&
2790             // fogemorpheme
2791               (in_compound || 
2792                  !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
2793             // needaffix on first suffix
2794               (cclass || !(sptr->getCont() && 
2795                    TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())))
2796             )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
2797             while (rv) {
2798                     if (ppfx) {
2799                         if (ppfx->getMorph()) {
2800                             mystrcat(result, ppfx->getMorph(), MAXLNLEN);
2801                             mystrcat(result, " ", MAXLNLEN);
2802                         } else debugflag(result, ppfx->getFlag());
2803                     }    
2804                     if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2805                     if (! HENTRY_FIND(rv, MORPH_STEM)) {
2806                             mystrcat(result, " ", MAXLNLEN);                                
2807                             mystrcat(result, MORPH_STEM, MAXLNLEN);
2808                             mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
2809                     }
2810                     // store the pointer of the hash entry
2811 //                    sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
2812
2813                     if (!complexprefixes && HENTRY_DATA(rv)) {
2814                         mystrcat(result, " ", MAXLNLEN);                                
2815                         mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
2816                     }
2817
2818                 if (sptr->getMorph()) {
2819                     mystrcat(result, " ", MAXLNLEN);
2820                     mystrcat(result, sptr->getMorph(), MAXLNLEN);
2821                 } else debugflag(result, sptr->getFlag());
2822                 mystrcat(result, "\n", MAXLNLEN);
2823                 rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
2824             }
2825              sptr = sptr->getNextEQ();
2826         } else {
2827              sptr = sptr->getNextNE();
2828         }
2829     }
2830
2831     if (*result) return mystrdup(result);
2832     return NULL;
2833 }
2834
2835 // check if word with affixes is correctly spelled
2836 struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound)
2837 {
2838     struct hentry * rv= NULL;
2839
2840     // check all prefixes (also crossed with suffixes if allowed) 
2841     rv = prefix_check(word, len, in_compound, needflag);
2842     if (rv) return rv;
2843
2844     // if still not found check all suffixes
2845     rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound);
2846
2847     if (havecontclass) {
2848         sfx = NULL;
2849         pfx = NULL;
2850
2851         if (rv) return rv;
2852         // if still not found check all two-level suffixes
2853         rv = suffix_check_twosfx(word, len, 0, NULL, needflag);
2854
2855         if (rv) return rv;
2856         // if still not found check all two-level suffixes
2857         rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag);
2858     }
2859
2860     return rv;
2861 }
2862
2863 // check if word with affixes is correctly spelled
2864 char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound)
2865 {
2866     char result[MAXLNLEN];
2867     char * st = NULL;
2868
2869     *result = '\0';
2870     
2871     // check all prefixes (also crossed with suffixes if allowed) 
2872     st = prefix_check_morph(word, len, in_compound);
2873     if (st) {
2874         mystrcat(result, st, MAXLNLEN);
2875         free(st);
2876     }
2877
2878     // if still not found check all suffixes    
2879     st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound);
2880     if (st) {
2881         mystrcat(result, st, MAXLNLEN);
2882         free(st);
2883     }
2884
2885     if (havecontclass) {
2886         sfx = NULL;
2887         pfx = NULL;
2888         // if still not found check all two-level suffixes
2889         st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag);
2890         if (st) {
2891             mystrcat(result, st, MAXLNLEN);
2892             free(st);
2893         }
2894
2895         // if still not found check all two-level suffixes
2896         st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag);
2897         if (st) {
2898             mystrcat(result, st, MAXLNLEN);
2899             free(st);
2900         }
2901     }
2902
2903     return mystrdup(result);
2904 }
2905
2906 char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap,
2907     unsigned short al, char * morph, char * targetmorph, int level)
2908 {
2909     // handle suffixes
2910     char * stemmorph;
2911     char * stemmorphcatpos;
2912     char mymorph[MAXLNLEN];
2913
2914     if (!morph) return NULL;
2915
2916     // check substandard flag
2917     if (TESTAFF(ap, substandard, al)) return NULL;
2918
2919     if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts);
2920
2921 //    int targetcount = get_sfxcount(targetmorph);
2922
2923     // use input suffix fields, if exist
2924     if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) {
2925         stemmorph = mymorph;
2926         strcpy(stemmorph, morph);
2927         mystrcat(stemmorph, " ", MAXLNLEN);
2928         stemmorphcatpos = stemmorph + strlen(stemmorph);
2929     } else {
2930         stemmorph = morph;
2931         stemmorphcatpos = NULL;
2932     }
2933
2934     for (int i = 0; i < al; i++) {
2935         const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
2936         SfxEntry * sptr = sFlag[c];
2937         while (sptr) {
2938             if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) || 
2939                 // don't generate forms with substandard affixes
2940                 !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) {
2941
2942                 if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph());
2943                 else stemmorph = (char *) sptr->getMorph();
2944
2945                 int cmp = morphcmp(stemmorph, targetmorph);
2946
2947                 if (cmp == 0) {
2948                     char * newword = sptr->add(ts, wl);
2949                     if (newword) {
2950                         hentry * check = pHMgr->lookup(newword); // XXX extra dic
2951                         if (!check || !check->astr || 
2952                             !(TESTAFF(check->astr, forbiddenword, check->alen) || 
2953                               TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) {
2954                                 return newword;
2955                         }
2956                         free(newword);
2957                     }
2958                 }
2959                 
2960                 // recursive call for secondary suffixes
2961                 if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) &&
2962 //                    (get_sfxcount(stemmorph) < targetcount) &&
2963                     !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) {
2964                     char * newword = sptr->add(ts, wl);
2965                     if (newword) {
2966                         char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(),
2967                             sptr->getContLen(), stemmorph, targetmorph, 1);
2968
2969                         if (newword2) {
2970                             free(newword);
2971                             return newword2;
2972                         }
2973                         free(newword);
2974                         newword = NULL;
2975                     }
2976                 }
2977             }
2978             sptr = sptr->getFlgNxt();
2979         }
2980     }
2981    return NULL;
2982 }
2983
2984
2985 int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts,
2986     int wl, const unsigned short * ap, unsigned short al, char * bad, int badl,
2987     char * phon)
2988 {
2989     int nh=0;
2990     // first add root word to list
2991     if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) ||
2992          (onlyincompound && TESTAFF(ap, onlyincompound, al))))) {
2993        wlst[nh].word = mystrdup(ts);
2994        if (!wlst[nh].word) return 0;
2995        wlst[nh].allow = (1 == 0);
2996        wlst[nh].orig = NULL;
2997        nh++;
2998        // add special phonetic version
2999        if (phon && (nh < maxn)) {
3000             wlst[nh].word = mystrdup(phon);
3001             if (!wlst[nh].word) return nh - 1;
3002             wlst[nh].allow = (1 == 0);
3003             wlst[nh].orig = mystrdup(ts);
3004             if (!wlst[nh].orig) return nh - 1;
3005             nh++;
3006        }
3007     }
3008
3009     // handle suffixes
3010     for (int i = 0; i < al; i++) {
3011        const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
3012        SfxEntry * sptr = sFlag[c];
3013        while (sptr) {
3014          if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) &&
3015                 (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) &&
3016                 // check needaffix flag
3017                 !(sptr->getCont() && ((needaffix && 
3018                       TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
3019                   (circumfix && 
3020                       TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) ||
3021                   (onlyincompound && 
3022                       TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))
3023                 ) {
3024             char * newword = sptr->add(ts, wl);
3025             if (newword) {
3026                 if (nh < maxn) {
3027                     wlst[nh].word = newword;
3028                     wlst[nh].allow = sptr->allowCross();
3029                     wlst[nh].orig = NULL;
3030                     nh++;
3031                     // add special phonetic version
3032                     if (phon && (nh < maxn)) {
3033                         char st[MAXWORDUTF8LEN];
3034                         strcpy(st, phon);
3035                         strcat(st, sptr->getKey());
3036                         reverseword(st + strlen(phon));
3037                         wlst[nh].word = mystrdup(st);
3038                         if (!wlst[nh].word) return nh - 1;
3039                         wlst[nh].allow = (1 == 0);
3040                         wlst[nh].orig = mystrdup(newword);
3041                         if (!wlst[nh].orig) return nh - 1;
3042                         nh++;
3043                     }
3044                 } else {
3045                     free(newword);
3046                 }
3047             }
3048          }
3049          sptr = sptr->getFlgNxt();
3050        }
3051     }
3052
3053     int n = nh;
3054
3055     // handle cross products of prefixes and suffixes
3056     for (int j=1;j<n ;j++)
3057        if (wlst[j].allow) {
3058           for (int k = 0; k < al; k++) {
3059              const unsigned char c = (unsigned char) (ap[k] & 0x00FF);
3060              PfxEntry * cptr = pFlag[c];
3061              while (cptr) {
3062                 if ((cptr->getFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) &&
3063                         (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) {
3064                     int l1 = strlen(wlst[j].word);
3065                     char * newword = cptr->add(wlst[j].word, l1);
3066                     if (newword) {
3067                        if (nh < maxn) {
3068                           wlst[nh].word = newword;
3069                           wlst[nh].allow = cptr->allowCross();
3070                           wlst[nh].orig = NULL;
3071                           nh++;
3072                        } else {
3073                           free(newword);
3074                        }
3075                     }
3076                 }
3077                 cptr = cptr->getFlgNxt();
3078              }
3079           }
3080        }
3081
3082
3083     // now handle pure prefixes
3084     for (int m = 0; m < al; m ++) {
3085        const unsigned char c = (unsigned char) (ap[m] & 0x00FF);
3086        PfxEntry * ptr = pFlag[c];
3087        while (ptr) {
3088          if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) &&
3089                 (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) &&
3090                 // check needaffix flag
3091                 !(ptr->getCont() && ((needaffix && 
3092                       TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) ||
3093                      (circumfix && 
3094                       TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) ||                      
3095                   (onlyincompound && 
3096                       TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen()))))
3097                 ) {
3098             char * newword = ptr->add(ts, wl);
3099             if (newword) {
3100                 if (nh < maxn) {
3101                     wlst[nh].word = newword;
3102                     wlst[nh].allow = ptr->allowCross();
3103                     wlst[nh].orig = NULL;
3104                     nh++;
3105                 } else {
3106                     free(newword);
3107                 } 
3108             }
3109          }
3110          ptr = ptr->getFlgNxt();
3111        }
3112     }
3113
3114     return nh;
3115 }
3116
3117 // return length of replacing table
3118 int AffixMgr::get_numrep() const
3119 {
3120   return numrep;
3121 }
3122
3123 // return replacing table
3124 struct replentry * AffixMgr::get_reptable() const
3125 {
3126   if (! reptable ) return NULL;
3127   return reptable;
3128 }
3129
3130 // return iconv table
3131 RepList * AffixMgr::get_iconvtable() const
3132 {
3133   if (! iconvtable ) return NULL;
3134   return iconvtable;
3135 }
3136
3137 // return oconv table
3138 RepList * AffixMgr::get_oconvtable() const
3139 {
3140   if (! oconvtable ) return NULL;
3141   return oconvtable;
3142 }
3143
3144 // return replacing table
3145 struct phonetable * AffixMgr::get_phonetable() const
3146 {
3147   if (! phone ) return NULL;
3148   return phone;
3149 }
3150
3151 // return length of character map table
3152 int AffixMgr::get_nummap() const
3153 {
3154   return nummap;
3155 }
3156
3157 // return character map table
3158 struct mapentry * AffixMgr::get_maptable() const
3159 {
3160   if (! maptable ) return NULL;
3161   return maptable;
3162 }
3163
3164 // return length of word break table
3165 int AffixMgr::get_numbreak() const
3166 {
3167   return numbreak;
3168 }
3169
3170 // return character map table
3171 char ** AffixMgr::get_breaktable() const
3172 {
3173   if (! breaktable ) return NULL;
3174   return breaktable;
3175 }
3176
3177 // return text encoding of dictionary
3178 char * AffixMgr::get_encoding()
3179 {
3180   if (! encoding ) encoding = mystrdup(SPELL_ENCODING);
3181   return mystrdup(encoding);
3182 }
3183
3184 // return text encoding of dictionary
3185 int AffixMgr::get_langnum() const
3186 {
3187   return langnum;
3188 }
3189
3190 // return double prefix option
3191 int AffixMgr::get_complexprefixes() const
3192 {
3193   return complexprefixes;
3194 }
3195
3196 // return FULLSTRIP option
3197 int AffixMgr::get_fullstrip() const
3198 {
3199   return fullstrip;
3200 }
3201
3202 FLAG AffixMgr::get_keepcase() const
3203 {
3204   return keepcase;
3205 }
3206
3207 FLAG AffixMgr::get_forceucase() const
3208 {
3209   return forceucase;
3210 }
3211
3212 FLAG AffixMgr::get_warn() const
3213 {
3214   return warn;
3215 }
3216
3217 int AffixMgr::get_forbidwarn() const
3218 {
3219   return forbidwarn;
3220 }
3221
3222 int AffixMgr::get_checksharps() const
3223 {
3224   return checksharps;
3225 }
3226
3227 char * AffixMgr::encode_flag(unsigned short aflag) const
3228 {
3229   return pHMgr->encode_flag(aflag);
3230 }
3231
3232
3233 // return the preferred ignore string for suggestions
3234 char * AffixMgr::get_ignore() const
3235 {
3236   if (!ignorechars) return NULL;
3237   return ignorechars;
3238 }
3239
3240 // return the preferred ignore string for suggestions
3241 unsigned short * AffixMgr::get_ignore_utf16(int * len) const
3242 {
3243   *len = ignorechars_utf16_len;
3244   return ignorechars_utf16;
3245 }
3246
3247 // return the keyboard string for suggestions
3248 char * AffixMgr::get_key_string()
3249 {
3250   if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING);
3251   return mystrdup(keystring);
3252 }
3253
3254 // return the preferred try string for suggestions
3255 char * AffixMgr::get_try_string() const
3256 {
3257   if (! trystring ) return NULL;
3258   return mystrdup(trystring);
3259 }
3260
3261 // return the preferred try string for suggestions
3262 const char * AffixMgr::get_wordchars() const
3263 {
3264   return wordchars;
3265 }
3266
3267 unsigned short * AffixMgr::get_wordchars_utf16(int * len) const
3268 {
3269   *len = wordchars_utf16_len;
3270   return wordchars_utf16;
3271 }
3272
3273 // is there compounding?
3274 int AffixMgr::get_compound() const
3275 {
3276   return compoundflag || compoundbegin || numdefcpd;
3277 }
3278
3279 // return the compound words control flag
3280 FLAG AffixMgr::get_compoundflag() const
3281 {
3282   return compoundflag;
3283 }
3284
3285 // return the forbidden words control flag
3286 FLAG AffixMgr::get_forbiddenword() const
3287 {
3288   return forbiddenword;
3289 }
3290
3291 // return the forbidden words control flag
3292 FLAG AffixMgr::get_nosuggest() const
3293 {
3294   return nosuggest;
3295 }
3296
3297 // return the forbidden words control flag
3298 FLAG AffixMgr::get_nongramsuggest() const
3299 {
3300   return nongramsuggest;
3301 }
3302
3303 // return the forbidden words flag modify flag
3304 FLAG AffixMgr::get_needaffix() const
3305 {
3306   return needaffix;
3307 }
3308
3309 // return the onlyincompound flag
3310 FLAG AffixMgr::get_onlyincompound() const
3311 {
3312   return onlyincompound;
3313 }
3314
3315 // return the compound word signal flag
3316 FLAG AffixMgr::get_compoundroot() const
3317 {
3318   return compoundroot;
3319 }
3320
3321 // return the compound begin signal flag
3322 FLAG AffixMgr::get_compoundbegin() const
3323 {
3324   return compoundbegin;
3325 }
3326
3327 // return the value of checknum
3328 int AffixMgr::get_checknum() const
3329 {
3330   return checknum;
3331 }
3332
3333 // return the value of prefix
3334 const char * AffixMgr::get_prefix() const
3335 {
3336   if (pfx) return pfx->getKey();
3337   return NULL;
3338 }
3339
3340 // return the value of suffix
3341 const char * AffixMgr::get_suffix() const
3342 {
3343   return sfxappnd;
3344 }
3345
3346 // return the value of suffix
3347 const char * AffixMgr::get_version() const
3348 {
3349   return version;
3350 }
3351
3352 // return lemma_present flag
3353 FLAG AffixMgr::get_lemma_present() const
3354 {
3355   return lemma_present;
3356 }
3357
3358 // utility method to look up root words in hash table
3359 struct hentry * AffixMgr::lookup(const char * word)
3360 {
3361   int i;
3362   struct hentry * he = NULL;
3363   for (i = 0; i < *maxdic && !he; i++) {
3364     he = (alldic[i])->lookup(word);
3365   }
3366   return he;
3367 }
3368
3369 // return the value of suffix
3370 int AffixMgr::have_contclass() const
3371 {
3372   return havecontclass;
3373 }
3374
3375 // return utf8
3376 int AffixMgr::get_utf8() const
3377 {
3378   return utf8;
3379 }
3380
3381 int AffixMgr::get_maxngramsugs(void) const
3382 {
3383   return maxngramsugs;
3384 }
3385
3386 int AffixMgr::get_maxcpdsugs(void) const
3387 {
3388   return maxcpdsugs;
3389 }
3390
3391 int AffixMgr::get_maxdiff(void) const
3392 {
3393   return maxdiff;
3394 }
3395
3396 int AffixMgr::get_onlymaxdiff(void) const
3397 {
3398   return onlymaxdiff;
3399 }
3400
3401 // return nosplitsugs
3402 int AffixMgr::get_nosplitsugs(void) const
3403 {
3404   return nosplitsugs;
3405 }
3406
3407 // return sugswithdots
3408 int AffixMgr::get_sugswithdots(void) const
3409 {
3410   return sugswithdots;
3411 }
3412
3413 /* parse flag */
3414 int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) {
3415    char * s = NULL;
3416    if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) {
3417       HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
3418       return 1;
3419    }
3420    if (parse_string(line, &s, af->getlinenum())) return 1;
3421    *out = pHMgr->decode_flag(s);
3422    free(s);
3423    return 0;
3424 }
3425
3426 /* parse num */
3427 int AffixMgr::parse_num(char * line, int * out, FileMgr * af) {
3428    char * s = NULL;
3429    if (*out != -1) {
3430       HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
3431       return 1;
3432    }
3433    if (parse_string(line, &s, af->getlinenum())) return 1;
3434    *out = atoi(s);
3435    free(s);
3436    return 0;
3437 }
3438
3439 /* parse in the max syllablecount of compound words and  */
3440 int  AffixMgr::parse_cpdsyllable(char * line, FileMgr * af)
3441 {
3442    char * tp = line;
3443    char * piece;
3444    int i = 0;
3445    int np = 0;
3446    w_char w[MAXWORDLEN];
3447    piece = mystrsep(&tp, 0);
3448    while (piece) {
3449       if (*piece != '\0') {
3450           switch(i) {
3451              case 0: { np++; break; }
3452              case 1: { cpdmaxsyllable = atoi(piece); np++; break; }
3453              case 2: {
3454                 if (!utf8) {
3455                     cpdvowels = mystrdup(piece);
3456                 } else {
3457                     int n = u8_u16(w, MAXWORDLEN, piece);
3458                     if (n > 0) {
3459                         flag_qsort((unsigned short *) w, 0, n);
3460                         cpdvowels_utf16 = (w_char *) malloc(n * sizeof(w_char));
3461                         if (!cpdvowels_utf16) return 1;
3462                         memcpy(cpdvowels_utf16, w, n * sizeof(w_char));
3463                     }
3464                     cpdvowels_utf16_len = n;
3465                 }
3466                 np++;
3467                 break;
3468              }
3469              default: break;
3470           }
3471           i++;
3472       }
3473       piece = mystrsep(&tp, 0);
3474    }
3475    if (np < 2) {
3476       HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum());
3477       return 1;
3478    }
3479    if (np == 2) cpdvowels = mystrdup("aeiouAEIOU");
3480    return 0;
3481 }
3482
3483 /* parse in the typical fault correcting table */
3484 int  AffixMgr::parse_reptable(char * line, FileMgr * af)
3485 {
3486    if (numrep != 0) {
3487       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3488       return 1;
3489    }
3490    char * tp = line;
3491    char * piece;
3492    int i = 0;
3493    int np = 0;
3494    piece = mystrsep(&tp, 0);
3495    while (piece) {
3496        if (*piece != '\0') {
3497           switch(i) {
3498              case 0: { np++; break; }
3499              case 1: { 
3500                        numrep = atoi(piece);
3501                        if (numrep < 1) {
3502                           HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
3503                           return 1;
3504                        }
3505                        reptable = (replentry *) malloc(numrep * sizeof(struct replentry));
3506                        if (!reptable) return 1;
3507                        np++;
3508                        break;
3509                      }
3510              default: break;
3511           }
3512           i++;
3513        }
3514        piece = mystrsep(&tp, 0);
3515    }
3516    if (np != 2) {
3517       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3518       return 1;
3519    } 
3520  
3521    /* now parse the numrep lines to read in the remainder of the table */
3522    char * nl;
3523    for (int j=0; j < numrep; j++) {
3524         if (!(nl = af->getline())) return 1;
3525         mychomp(nl);
3526         tp = nl;
3527         i = 0;
3528         reptable[j].pattern = NULL;
3529         reptable[j].pattern2 = NULL;
3530         piece = mystrsep(&tp, 0);
3531         while (piece) {
3532            if (*piece != '\0') {
3533                switch(i) {
3534                   case 0: {
3535                              if (strncmp(piece,"REP",3) != 0) {
3536                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3537                                  numrep = 0;
3538                                  return 1;
3539                              }
3540                              break;
3541                           }
3542                   case 1: {
3543                             if (*piece == '^') reptable[j].start = true; else reptable[j].start = false;
3544                             reptable[j].pattern = mystrrep(mystrdup(piece + int(reptable[j].start)),"_"," ");
3545                             int lr = strlen(reptable[j].pattern) - 1;
3546                             if (reptable[j].pattern[lr] == '$') {
3547                                 reptable[j].end = true;
3548                                 reptable[j].pattern[lr] = '\0';
3549                             } else reptable[j].end = false;
3550                             break;
3551                           }
3552                   case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; }
3553                   default: break;
3554                }
3555                i++;
3556            }
3557            piece = mystrsep(&tp, 0);
3558         }
3559         if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) {
3560              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3561              numrep = 0;
3562              return 1;
3563         }
3564    }
3565    return 0;
3566 }
3567
3568 /* parse in the typical fault correcting table */
3569 int  AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword)
3570 {
3571    if (*rl) {
3572       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3573       return 1;
3574    }
3575    char * tp = line;
3576    char * piece;
3577    int i = 0;
3578    int np = 0;
3579    int numrl = 0;
3580    piece = mystrsep(&tp, 0);
3581    while (piece) {
3582        if (*piece != '\0') {
3583           switch(i) {
3584              case 0: { np++; break; }
3585              case 1: { 
3586                        numrl = atoi(piece);
3587                        if (numrl < 1) {
3588                           HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
3589                           return 1;
3590                        }
3591                        *rl = new RepList(numrl);
3592                        if (!*rl) return 1;
3593                        np++;
3594                        break;
3595                      }
3596              default: break;
3597           }
3598           i++;
3599        }
3600        piece = mystrsep(&tp, 0);
3601    }
3602    if (np != 2) {
3603       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3604       return 1;
3605    } 
3606  
3607    /* now parse the num lines to read in the remainder of the table */
3608    char * nl;
3609    for (int j=0; j < numrl; j++) {
3610         if (!(nl = af->getline())) return 1;
3611         mychomp(nl);
3612         tp = nl;
3613         i = 0;
3614         char * pattern = NULL;
3615         char * pattern2 = NULL;
3616         piece = mystrsep(&tp, 0);
3617         while (piece) {
3618            if (*piece != '\0') {
3619                switch(i) {
3620                   case 0: {
3621                              if (strncmp(piece, keyword, sizeof(keyword)) != 0) {
3622                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3623                                  delete *rl;
3624                                  *rl = NULL;
3625                                  return 1;
3626                              }
3627                              break;
3628                           }
3629                   case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; }
3630                   case 2: { 
3631                     pattern2 = mystrrep(mystrdup(piece),"_"," ");
3632                     break; 
3633                   }
3634                   default: break;
3635                }
3636                i++;
3637            }
3638            piece = mystrsep(&tp, 0);
3639         }
3640         if (!pattern || !pattern2) {
3641             if (pattern)
3642                 free(pattern);
3643             if (pattern2)
3644                 free(pattern2);
3645             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3646             return 1;
3647         }
3648         (*rl)->add(pattern, pattern2);
3649    }
3650    return 0;
3651 }
3652
3653
3654 /* parse in the typical fault correcting table */
3655 int  AffixMgr::parse_phonetable(char * line, FileMgr * af)
3656 {
3657    if (phone) {
3658       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3659       return 1;
3660    }
3661    char * tp = line;
3662    char * piece;
3663    int i = 0;
3664    int np = 0;
3665    piece = mystrsep(&tp, 0);
3666    while (piece) {
3667        if (*piece != '\0') {
3668           switch(i) {
3669              case 0: { np++; break; }
3670              case 1: { 
3671                        phone = (phonetable *) malloc(sizeof(struct phonetable));
3672                        if (!phone) return 1;
3673                        phone->num = atoi(piece);
3674                        phone->rules = NULL;
3675                        phone->utf8 = (char) utf8;
3676                        if (phone->num < 1) {
3677                           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3678                           return 1;
3679                        }
3680                        phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *));
3681                        if (!phone->rules) {
3682                           free(phone);
3683                           phone = NULL;
3684                           return 1;
3685                        }
3686                        np++;
3687                        break;
3688                      }
3689              default: break;
3690           }
3691           i++;
3692        }
3693        piece = mystrsep(&tp, 0);
3694    }
3695    if (np != 2) {
3696       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3697       return 1;
3698    } 
3699  
3700    /* now parse the phone->num lines to read in the remainder of the table */
3701    char * nl;
3702    for (int j=0; j < phone->num; j++) {
3703         if (!(nl = af->getline())) return 1;
3704         mychomp(nl);
3705         tp = nl;
3706         i = 0;
3707         phone->rules[j * 2] = NULL;
3708         phone->rules[j * 2 + 1] = NULL;
3709         piece = mystrsep(&tp, 0);
3710         while (piece) {
3711            if (*piece != '\0') {
3712                switch(i) {
3713                   case 0: {
3714                              if (strncmp(piece,"PHONE",5) != 0) {
3715                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3716                                  phone->num = 0;
3717                                  return 1;
3718                              }
3719                              break;
3720                           }
3721                   case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; }
3722                   case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; }
3723                   default: break;
3724                }
3725                i++;
3726            }
3727            piece = mystrsep(&tp, 0);
3728         }
3729         if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) {
3730              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3731              phone->num = 0;
3732              return 1;
3733         }
3734    }
3735    phone->rules[phone->num * 2] = mystrdup("");
3736    phone->rules[phone->num * 2 + 1] = mystrdup("");
3737    init_phonet_hash(*phone);
3738    return 0;
3739 }
3740
3741 /* parse in the checkcompoundpattern table */
3742 int  AffixMgr::parse_checkcpdtable(char * line, FileMgr * af)
3743 {
3744    if (numcheckcpd != 0) {
3745       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3746       return 1;
3747    }
3748    char * tp = line;
3749    char * piece;
3750    int i = 0;
3751    int np = 0;
3752    piece = mystrsep(&tp, 0);
3753    while (piece) {
3754        if (*piece != '\0') {
3755           switch(i) {
3756              case 0: { np++; break; }
3757              case 1: { 
3758                        numcheckcpd = atoi(piece);
3759                        if (numcheckcpd < 1) {
3760                           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3761                           return 1;
3762                        }
3763                        checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry));
3764                        if (!checkcpdtable) return 1;
3765                        np++;
3766                        break;
3767                      }
3768              default: break;
3769           }
3770           i++;
3771        }
3772        piece = mystrsep(&tp, 0);
3773    }
3774    if (np != 2) {
3775       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",  af->getlinenum());
3776       return 1;
3777    }
3778
3779    /* now parse the numcheckcpd lines to read in the remainder of the table */
3780    char * nl;
3781    for (int j=0; j < numcheckcpd; j++) {
3782         if (!(nl = af->getline())) return 1;
3783         mychomp(nl);
3784         tp = nl;
3785         i = 0;
3786         checkcpdtable[j].pattern = NULL;
3787         checkcpdtable[j].pattern2 = NULL;
3788         checkcpdtable[j].pattern3 = NULL;
3789         checkcpdtable[j].cond = FLAG_NULL;
3790         checkcpdtable[j].cond2 = FLAG_NULL;
3791         piece = mystrsep(&tp, 0);
3792         while (piece) {
3793            if (*piece != '\0') {
3794                switch(i) {
3795                   case 0: {
3796                              if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) {
3797                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3798                                  numcheckcpd = 0;
3799                                  return 1;
3800                              }
3801                              break;
3802                           }
3803                   case 1: { 
3804                     checkcpdtable[j].pattern = mystrdup(piece); 
3805                     char * p = strchr(checkcpdtable[j].pattern, '/');
3806                     if (p) {
3807                       *p = '\0';
3808                     checkcpdtable[j].cond = pHMgr->decode_flag(p + 1);
3809                     }
3810                     break; }
3811                   case 2: { 
3812                     checkcpdtable[j].pattern2 = mystrdup(piece);
3813                     char * p = strchr(checkcpdtable[j].pattern2, '/');
3814                     if (p) {
3815                       *p = '\0';
3816                       checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1);
3817                     }
3818                     break;
3819                     }
3820                   case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; }
3821                   default: break;
3822                }
3823                i++;
3824            }
3825            piece = mystrsep(&tp, 0);
3826         }
3827         if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) {
3828              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3829              numcheckcpd = 0;
3830              return 1;
3831         }
3832    }
3833    return 0;
3834 }
3835
3836 /* parse in the compound rule table */
3837 int  AffixMgr::parse_defcpdtable(char * line, FileMgr * af)
3838 {
3839    if (numdefcpd != 0) {
3840       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3841       return 1;
3842    }
3843    char * tp = line;
3844    char * piece;
3845    int i = 0;
3846    int np = 0;
3847    piece = mystrsep(&tp, 0);
3848    while (piece) {
3849        if (*piece != '\0') {
3850           switch(i) {
3851              case 0: { np++; break; }
3852              case 1: { 
3853                        numdefcpd = atoi(piece);
3854                        if (numdefcpd < 1) {
3855                           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3856                           return 1;
3857                        }
3858                        defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry));
3859                        if (!defcpdtable) return 1;
3860                        np++;
3861                        break;
3862                      }
3863              default: break;
3864           }
3865           i++;
3866        }
3867        piece = mystrsep(&tp, 0);
3868    }
3869    if (np != 2) {
3870       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3871       return 1;
3872    } 
3873  
3874    /* now parse the numdefcpd lines to read in the remainder of the table */
3875    char * nl;
3876    for (int j=0; j < numdefcpd; j++) {
3877         if (!(nl = af->getline())) return 1;
3878         mychomp(nl);
3879         tp = nl;
3880         i = 0;
3881         defcpdtable[j].def = NULL;
3882         piece = mystrsep(&tp, 0);
3883         while (piece) {
3884            if (*piece != '\0') {
3885                switch(i) {
3886                   case 0: {
3887                              if (strncmp(piece, "COMPOUNDRULE", 12) != 0) {
3888                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3889                                  numdefcpd = 0;
3890                                  return 1;
3891                              }
3892                              break;
3893                           }
3894                   case 1: { // handle parenthesized flags
3895                             if (strchr(piece, '(')) {
3896                                 defcpdtable[j].def = (FLAG *) malloc(strlen(piece) * sizeof(FLAG));
3897                                 defcpdtable[j].len = 0;
3898                                 int end = 0;
3899                                 FLAG * conv;
3900                                 while (!end) {
3901                                     char * par = piece + 1;
3902                                     while (*par != '(' && *par != ')' && *par != '\0') par++;
3903                                     if (*par == '\0') end = 1; else *par = '\0';
3904                                     if (*piece == '(') piece++;
3905                                     if (*piece == '*' || *piece == '?') {
3906                                         defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece;
3907                                     } else if (*piece != '\0') {
3908                                         int l = pHMgr->decode_flags(&conv, piece, af);
3909                                         for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k];
3910                                         free(conv);
3911                                     }
3912                                     piece = par + 1;
3913                                 }
3914                             } else {
3915                                 defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af);
3916                             }
3917                             break; 
3918                            }
3919                   default: break;
3920                }
3921                i++;
3922            }
3923            piece = mystrsep(&tp, 0);
3924         }
3925         if (!defcpdtable[j].len) {
3926              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3927              numdefcpd = 0;
3928              return 1;
3929         }
3930    }
3931    return 0;
3932 }
3933
3934
3935 /* parse in the character map table */
3936 int  AffixMgr::parse_maptable(char * line, FileMgr * af)
3937 {
3938    if (nummap != 0) {
3939       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
3940       return 1;
3941    }
3942    char * tp = line;
3943    char * piece;
3944    int i = 0;
3945    int np = 0;
3946    piece = mystrsep(&tp, 0);
3947    while (piece) {
3948        if (*piece != '\0') {
3949           switch(i) {
3950              case 0: { np++; break; }
3951              case 1: { 
3952                        nummap = atoi(piece);
3953                        if (nummap < 1) {
3954                           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
3955                           return 1;
3956                        }
3957                        maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry));
3958                        if (!maptable) return 1;
3959                        np++;
3960                        break;
3961                      }
3962              default: break;
3963           }
3964           i++;
3965        }
3966        piece = mystrsep(&tp, 0);
3967    }
3968    if (np != 2) {
3969       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
3970       return 1;
3971    } 
3972  
3973    /* now parse the nummap lines to read in the remainder of the table */
3974    char * nl;
3975    for (int j=0; j < nummap; j++) {
3976         if (!(nl = af->getline())) return 1;
3977         mychomp(nl);
3978         tp = nl;
3979         i = 0;
3980         maptable[j].set = NULL;
3981         maptable[j].len = 0;
3982         piece = mystrsep(&tp, 0);
3983         while (piece) {
3984            if (*piece != '\0') {
3985                switch(i) {
3986                   case 0: {
3987                              if (strncmp(piece,"MAP",3) != 0) {
3988                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
3989                                  nummap = 0;
3990                                  return 1;
3991                              }
3992                              break;
3993                           }
3994                   case 1: {
3995                             int setn = 0;
3996                             maptable[j].len = strlen(piece);
3997                             maptable[j].set = (char **) malloc(maptable[j].len * sizeof(char*));
3998                             if (!maptable[j].set) return 1;
3999                             for (int k = 0; k < maptable[j].len; k++) {
4000                                 int chl = 1;
4001                                 int chb = k;
4002                                 if (piece[k] == '(') {
4003                                     char * parpos = strchr(piece + k, ')');
4004                                     if (parpos != NULL) {
4005                                         chb = k + 1;
4006                                         chl = (int)(parpos - piece) - k - 1;
4007                                         k = k + chl + 1;
4008                                     }
4009                                 } else {
4010                                     if (utf8 && (piece[k] & 0xc0) == 0xc0) {
4011                                         for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++);
4012                                         chl = k - chb;
4013                                         k--;
4014                                     }
4015                                 }
4016                                 maptable[j].set[setn] = (char *) malloc(chl + 1);
4017                                 if (!maptable[j].set[setn]) return 1;
4018                                 strncpy(maptable[j].set[setn], piece + chb, chl);
4019                                 maptable[j].set[setn][chl] = '\0';
4020                                 setn++;
4021                             }
4022                             maptable[j].len = setn;
4023                             break; }
4024                   default: break;
4025                }
4026                i++;
4027            }
4028            piece = mystrsep(&tp, 0);
4029         }
4030         if (!maptable[j].set || !maptable[j].len) {
4031              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4032              nummap = 0;
4033              return 1;
4034         }
4035    }
4036    return 0;
4037 }
4038
4039 /* parse in the word breakpoint table */
4040 int  AffixMgr::parse_breaktable(char * line, FileMgr * af)
4041 {
4042    if (numbreak > -1) {
4043       HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
4044       return 1;
4045    }
4046    char * tp = line;
4047    char * piece;
4048    int i = 0;
4049    int np = 0;
4050    piece = mystrsep(&tp, 0);
4051    while (piece) {
4052        if (*piece != '\0') {
4053           switch(i) {
4054              case 0: { np++; break; }
4055              case 1: { 
4056                        numbreak = atoi(piece);
4057                        if (numbreak < 0) {
4058                           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
4059                           return 1;
4060                        }
4061                        if (numbreak == 0) return 0;
4062                        breaktable = (char **) malloc(numbreak * sizeof(char *));
4063                        if (!breaktable) return 1;
4064                        np++;
4065                        break;
4066                      }
4067              default: break;
4068           }
4069           i++;
4070        }
4071        piece = mystrsep(&tp, 0);
4072    }
4073    if (np != 2) {
4074       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
4075       return 1;
4076    } 
4077  
4078    /* now parse the numbreak lines to read in the remainder of the table */
4079    char * nl;
4080    for (int j=0; j < numbreak; j++) {
4081         if (!(nl = af->getline())) return 1;
4082         mychomp(nl);
4083         tp = nl;
4084         i = 0;
4085         piece = mystrsep(&tp, 0);
4086         while (piece) {
4087            if (*piece != '\0') {
4088                switch(i) {
4089                   case 0: {
4090                              if (strncmp(piece,"BREAK",5) != 0) {
4091                                  HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4092                                  numbreak = 0;
4093                                  return 1;
4094                              }
4095                              break;
4096                           }
4097                   case 1: {
4098                             breaktable[j] = mystrdup(piece);
4099                             break;
4100                           }
4101                   default: break;
4102                }
4103                i++;
4104            }
4105            piece = mystrsep(&tp, 0);
4106         }
4107         if (!breaktable) {
4108              HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
4109              numbreak = 0;
4110              return 1;
4111         }
4112    }
4113    return 0;
4114 }
4115
4116 void AffixMgr::reverse_condition(char * piece) {
4117     int neg = 0;
4118     for (char * k = piece + strlen(piece) - 1; k >= piece; k--) {
4119         switch(*k) {
4120           case '[': {
4121                 if (neg) *(k+1) = '['; else *k = ']';
4122                     break;
4123             }
4124           case ']': {
4125                 *k = '[';
4126                 if (neg) *(k+1) = '^';
4127                 neg = 0;
4128                 break;
4129             }
4130           case '^': {
4131                if (*(k+1) == ']') neg = 1; else *(k+1) = *k;
4132                break;
4133                 }
4134           default: {
4135             if (neg) *(k+1) = *k;
4136           }
4137        }
4138     }
4139 }
4140
4141 int  AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags)
4142 {
4143    int numents = 0;      // number of affentry structures to parse
4144
4145    unsigned short aflag = 0;      // affix char identifier
4146
4147    char ff=0;
4148    std::vector<affentry> affentries;
4149
4150    char * tp = line;
4151    char * nl = line;
4152    char * piece;
4153    int i = 0;
4154
4155    // checking lines with bad syntax
4156 #ifdef DEBUG
4157    int basefieldnum = 0;
4158 #endif
4159
4160    // split affix header line into pieces
4161
4162    int np = 0;
4163
4164    piece = mystrsep(&tp, 0);
4165    while (piece) {
4166       if (*piece != '\0') {
4167           switch(i) {
4168              // piece 1 - is type of affix
4169              case 0: { np++; break; }
4170           
4171              // piece 2 - is affix char
4172              case 1: { 
4173                     np++;
4174                     aflag = pHMgr->decode_flag(piece);
4175                     if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
4176                         ((at == 'P') && (dupflags[aflag] & dupPFX))) {
4177                         HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n",
4178                             af->getlinenum());
4179                         // return 1; XXX permissive mode for bad dictionaries
4180                     }
4181                     dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX);
4182                     break; 
4183                     }
4184              // piece 3 - is cross product indicator 
4185              case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; }
4186
4187              // piece 4 - is number of affentries
4188              case 3: { 
4189                        np++;
4190                        numents = atoi(piece); 
4191                        if (numents == 0) {
4192                            char * err = pHMgr->encode_flag(aflag);
4193                            if (err) {
4194                                 HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
4195                                    af->getlinenum());
4196                                 free(err);
4197                            }
4198                            return 1;
4199                        }
4200                        affentries.resize(numents);
4201                        affentries[0].opts = ff;
4202                        if (utf8) affentries[0].opts += aeUTF8;
4203                        if (pHMgr->is_aliasf()) affentries[0].opts += aeALIASF;
4204                        if (pHMgr->is_aliasm()) affentries[0].opts += aeALIASM;
4205                        affentries[0].aflag = aflag;
4206                      }
4207
4208              default: break;
4209           }
4210           i++;
4211       }
4212       piece = mystrsep(&tp, 0);
4213    }
4214    // check to make sure we parsed enough pieces
4215    if (np != 4) {
4216        char * err = pHMgr->encode_flag(aflag);
4217        if (err) {
4218             HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
4219             free(err);
4220        }
4221        return 1;
4222    }
4223  
4224    // now parse numents affentries for this affix
4225    std::vector<affentry>::iterator start = affentries.begin();
4226    std::vector<affentry>::iterator end = affentries.end();
4227    for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
4228       if (!(nl = af->getline())) return 1;
4229       mychomp(nl);
4230       tp = nl;
4231       i = 0;
4232       np = 0;
4233
4234       // split line into pieces
4235       piece = mystrsep(&tp, 0);
4236       while (piece) {
4237          if (*piece != '\0') {
4238              switch(i) {
4239                 // piece 1 - is type
4240                 case 0: { 
4241                           np++;
4242                           if (entry != start) entry->opts = start->opts &
4243                              (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM);
4244                           break;
4245                         }
4246
4247                 // piece 2 - is affix char
4248                 case 1: { 
4249                           np++;
4250                           if (pHMgr->decode_flag(piece) != aflag) {
4251                               char * err = pHMgr->encode_flag(aflag);
4252                               if (err) {
4253                                 HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
4254                                     af->getlinenum(), err);
4255                                 free(err);
4256                               }
4257                               return 1;
4258                           }
4259
4260                           if (entry != start) entry->aflag = start->aflag;
4261                           break;
4262                         }
4263
4264                 // piece 3 - is string to strip or 0 for null 
4265                 case 2: { 
4266                           np++;
4267                           if (complexprefixes) {
4268                             if (utf8) reverseword_utf(piece); else reverseword(piece);
4269                           }
4270                           entry->strip = mystrdup(piece);
4271                           entry->stripl = (unsigned char) strlen(entry->strip);
4272                           if (strcmp(entry->strip,"0") == 0) {
4273                               free(entry->strip);
4274                               entry->strip=mystrdup("");
4275                               entry->stripl = 0;
4276                           }   
4277                           break; 
4278                         }
4279
4280                 // piece 4 - is affix string or 0 for null
4281                 case 3: { 
4282                           char * dash;  
4283                           entry->morphcode = NULL;
4284                           entry->contclass = NULL;
4285                           entry->contclasslen = 0;
4286                           np++;
4287                           dash = strchr(piece, '/');
4288                           if (dash) {
4289                             *dash = '\0';
4290
4291                             if (ignorechars) {
4292                               if (utf8) {
4293                                 remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
4294                               } else {
4295                                 remove_ignored_chars(piece,ignorechars);
4296                               }
4297                             }
4298
4299                             if (complexprefixes) {
4300                                 if (utf8) reverseword_utf(piece); else reverseword(piece);
4301                             }
4302                             entry->appnd = mystrdup(piece);
4303
4304                             if (pHMgr->is_aliasf()) {
4305                                 int index = atoi(dash + 1);
4306                                 entry->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(entry->contclass), af);
4307                                 if (!entry->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1);
4308                             } else {
4309                                 entry->contclasslen = (unsigned short) pHMgr->decode_flags(&(entry->contclass), dash + 1, af);
4310                                 flag_qsort(entry->contclass, 0, entry->contclasslen);
4311                             }
4312                             *dash = '/';
4313
4314                             havecontclass = 1;
4315                             for (unsigned short _i = 0; _i < entry->contclasslen; _i++) {
4316                               contclasses[(entry->contclass)[_i]] = 1;
4317                             }
4318                           } else {
4319                             if (ignorechars) {
4320                               if (utf8) {
4321                                 remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
4322                               } else {
4323                                 remove_ignored_chars(piece,ignorechars);
4324                               }
4325                             }
4326
4327                             if (complexprefixes) {
4328                                 if (utf8) reverseword_utf(piece); else reverseword(piece);
4329                             }
4330                             entry->appnd = mystrdup(piece);
4331                           }
4332
4333                           entry->appndl = (unsigned char) strlen(entry->appnd);
4334                           if (strcmp(entry->appnd,"0") == 0) {
4335                               free(entry->appnd);
4336                               entry->appnd=mystrdup("");
4337                               entry->appndl = 0;
4338                           }   
4339                           break; 
4340                         }
4341
4342                 // piece 5 - is the conditions descriptions
4343                 case 4: { 
4344                           np++;
4345                           if (complexprefixes) {
4346                             if (utf8) reverseword_utf(piece); else reverseword(piece);
4347                             reverse_condition(piece);
4348                           }
4349                           if (entry->stripl && (strcmp(piece, ".") != 0) &&
4350                             redundant_condition(at, entry->strip, entry->stripl, piece, af->getlinenum()))
4351                                 strcpy(piece, ".");
4352                           if (at == 'S') {
4353                             reverseword(piece);
4354                             reverse_condition(piece);
4355                           }
4356                           if (encodeit(*entry, piece)) return 1;
4357                          break;
4358                 }
4359
4360                 case 5: {
4361                           np++;
4362                           if (pHMgr->is_aliasm()) {
4363                             int index = atoi(piece);
4364                             entry->morphcode = pHMgr->get_aliasm(index);
4365                           } else {
4366                             if (complexprefixes) { // XXX - fix me for morph. gen.
4367                                 if (utf8) reverseword_utf(piece); else reverseword(piece);
4368                             }
4369                             // add the remaining of the line
4370                             if (*tp) {
4371                                 *(tp - 1) = ' ';
4372                                 tp = tp + strlen(tp);
4373                             }
4374                             entry->morphcode = mystrdup(piece);
4375                             if (!entry->morphcode) return 1;
4376                           }
4377                           break; 
4378                 }
4379                 default: break;
4380              }
4381              i++;
4382          }
4383          piece = mystrsep(&tp, 0);
4384       }
4385       // check to make sure we parsed enough pieces
4386       if (np < 4) {
4387           char * err = pHMgr->encode_flag(aflag);
4388           if (err) {
4389             HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
4390                 af->getlinenum(), err);
4391             free(err);
4392           }
4393           return 1;
4394       }
4395
4396 #ifdef DEBUG
4397       // detect unnecessary fields, excepting comments
4398       if (basefieldnum) {
4399         int fieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
4400           if (fieldnum != basefieldnum) 
4401             HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum());
4402       } else {
4403         basefieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6);
4404       }
4405 #endif
4406    }
4407  
4408    // now create SfxEntry or PfxEntry objects and use links to
4409    // build an ordered (sorted by affix string) list
4410    for (std::vector<affentry>::iterator entry = start; entry != end; ++entry) {
4411       if (at == 'P') {
4412           PfxEntry * pfxptr = new PfxEntry(this,&(*entry));
4413           build_pfxtree(pfxptr);
4414       } else {
4415           SfxEntry * sfxptr = new SfxEntry(this,&(*entry));
4416           build_sfxtree(sfxptr); 
4417       }
4418    }
4419    return 0;
4420 }
4421
4422 int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) {
4423   int condl = strlen(cond);
4424   int i;
4425   int j;
4426   int neg;
4427   int in;
4428   if (ft == 'P') { // prefix
4429     if (strncmp(strip, cond, condl) == 0) return 1;
4430     if (utf8) {
4431     } else {
4432       for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) {
4433         if (cond[j] != '[') {
4434           if (cond[j] != strip[i]) {
4435             HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4436             return 0;
4437           }
4438         } else {
4439           neg = (cond[j+1] == '^') ? 1 : 0;
4440           in = 0;
4441           do {
4442             j++;
4443             if (strip[i] == cond[j]) in = 1;
4444           } while ((j < (condl - 1)) && (cond[j] != ']'));
4445           if (j == (condl - 1) && (cond[j] != ']')) {
4446             HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum, cond);
4447             return 0;
4448           }
4449           if ((!neg && !in) || (neg && in)) {
4450             HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4451             return 0;
4452           }
4453         }
4454       }
4455       if (j >= condl) return 1;
4456     }
4457   } else { // suffix
4458     if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1;
4459     if (utf8) {
4460     } else {
4461       for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) {
4462         if (cond[j] != ']') {
4463           if (cond[j] != strip[i]) {
4464             HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4465             return 0;
4466           }
4467         } else {
4468           in = 0;
4469           do {
4470             j--;
4471             if (strip[i] == cond[j]) in = 1;
4472           } while ((j > 0) && (cond[j] != '['));
4473           if ((j == 0) && (cond[j] != '[')) {
4474             HUNSPELL_WARNING(stderr, "error: line: %d: missing ] in condition:\n%s\n", linenum, cond);
4475             return 0;
4476           }
4477           neg = (cond[j+1] == '^') ? 1 : 0;
4478           if ((!neg && !in) || (neg && in)) {
4479             HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
4480             return 0;
4481           }
4482         }
4483       }
4484       if (j < 0) return 1;
4485     }
4486   }
4487   return 0;
4488 }