OSDN Git Service

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