OSDN Git Service

Pimped TortoiseGit logo up a bit
[tortoisegit/TortoiseGitJp.git] / ext / hunspell / suggestmgr.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 <cstdio>\r
8 #include <cctype>\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 "suggestmgr.hxx"\r
17 \r
18 #ifndef MOZILLA_CLIENT\r
19 #ifndef W32\r
20 using namespace std;\r
21 #endif\r
22 #endif\r
23 \r
24 \r
25 SuggestMgr::SuggestMgr(const char * tryme, int maxn, \r
26                        AffixMgr * aptr)\r
27 {\r
28 \r
29   // register affix manager and check in string of chars to \r
30   // try when building candidate suggestions\r
31   pAMgr = aptr;\r
32 \r
33   ctryl = 0;\r
34   ctry = NULL;\r
35   ctry_utf = NULL;\r
36 \r
37   maxSug = maxn;\r
38   nosplitsugs = 0;\r
39   maxngramsugs = MAXNGRAMSUGS;\r
40 \r
41   utf8 = 0;\r
42   complexprefixes = 0;\r
43 \r
44   if (pAMgr) {\r
45         char * enc = pAMgr->get_encoding();\r
46         csconv = get_current_cs(enc);\r
47         free(enc);\r
48         nosplitsugs = pAMgr->get_nosplitsugs();\r
49         if (pAMgr->get_maxngramsugs() >= 0) maxngramsugs = pAMgr->get_maxngramsugs();\r
50         utf8 = pAMgr->get_utf8();\r
51         complexprefixes = pAMgr->get_complexprefixes();\r
52   }\r
53 \r
54   if (tryme) {  \r
55     if (utf8) {\r
56         w_char t[MAXSWL];    \r
57         ctryl = u8_u16(t, MAXSWL, tryme);\r
58         ctry_utf = (w_char *) malloc(ctryl * sizeof(w_char));\r
59         memcpy(ctry_utf, t, ctryl * sizeof(w_char));\r
60     } else {\r
61         ctry = mystrdup(tryme);\r
62         ctryl = strlen(ctry);\r
63     }\r
64   }\r
65 }\r
66 \r
67 \r
68 SuggestMgr::~SuggestMgr()\r
69 {\r
70   pAMgr = NULL;\r
71   if (ctry) free(ctry);\r
72   ctry = NULL;\r
73   if (ctry_utf) free(ctry_utf);\r
74   ctry_utf = NULL;\r
75   ctryl = 0;\r
76   maxSug = 0;\r
77 }\r
78 \r
79 int SuggestMgr::testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest,\r
80    int * timer, time_t * timelimit) {\r
81       int cwrd = 1;\r
82       if (ns == maxSug) return maxSug;\r
83       for (int k=0; k < ns; k++) {\r
84         if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;\r
85       }\r
86       if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) {\r
87         wlst[ns] = mystrdup(candidate);\r
88         if (wlst[ns] == NULL) {\r
89             for (int j=0; j<ns; j++) free(wlst[j]);\r
90             return -1;\r
91         }\r
92         ns++;\r
93       } \r
94       return ns;\r
95 }\r
96 \r
97 // generate suggestions for a mispelled word\r
98 //    pass in address of array of char * pointers\r
99 \r
100 int SuggestMgr::suggest(char*** slst, const char * w, int nsug)\r
101 {\r
102     int nocompoundtwowords = 0;\r
103     char ** wlst;    \r
104     w_char word_utf[MAXSWL];\r
105     int wl = 0;\r
106 \r
107   char w2[MAXWORDUTF8LEN];\r
108   const char * word = w;\r
109 \r
110   // word reversing wrapper for complex prefixes\r
111   if (complexprefixes) {\r
112     strcpy(w2, w);\r
113     if (utf8) reverseword_utf(w2); else reverseword(w2);\r
114     word = w2;\r
115   }\r
116     \r
117     if (*slst) {\r
118         wlst = *slst;\r
119     } else {\r
120         wlst = (char **) malloc(maxSug * sizeof(char *));\r
121         if (wlst == NULL) return -1;\r
122         for (int i = 0; i < maxSug; i++) {\r
123             wlst[i] = NULL;\r
124         }\r
125     }\r
126     \r
127     if (utf8) {\r
128         wl = u8_u16(word_utf, MAXSWL, word);\r
129     }\r
130 \r
131     for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {\r
132 \r
133     // suggestions for an uppercase word (html -> HTML)\r
134     if ((nsug < maxSug) && (nsug > -1)) {\r
135         nsug = (utf8) ? capchars_utf(wlst, word_utf, wl, nsug, cpdsuggest) :\r
136                     capchars(wlst, word, nsug, cpdsuggest);\r
137     }\r
138 \r
139     // perhaps we made a typical fault of spelling\r
140     if ((nsug < maxSug) && (nsug > -1))\r
141     nsug = replchars(wlst, word, nsug, cpdsuggest);\r
142 \r
143     // perhaps we made chose the wrong char from a related set\r
144     if ((nsug < maxSug) && (nsug > -1) && (cpdsuggest == 0)) {\r
145       nsug = mapchars(wlst, word, nsug);\r
146     }\r
147 \r
148     // did we swap the order of chars by mistake\r
149     if ((nsug < maxSug) && (nsug > -1)) {\r
150         nsug = (utf8) ? swapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :\r
151                     swapchar(wlst, word, nsug, cpdsuggest);\r
152     }\r
153 \r
154     // did we swap the order of non adjacent chars by mistake\r
155     if ((nsug < maxSug) && (nsug > -1)) {\r
156         nsug = (utf8) ? longswapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :\r
157                     longswapchar(wlst, word, nsug, cpdsuggest);\r
158     }\r
159 \r
160     // did we forgot a char\r
161     if ((nsug < maxSug) && (nsug > -1)) {\r
162         nsug = (utf8) ? forgotchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :\r
163                     forgotchar(wlst, word, nsug, cpdsuggest);\r
164     }\r
165 \r
166     // did we move a char\r
167     if ((nsug < maxSug) && (nsug > -1)) {\r
168         nsug = (utf8) ? movechar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :\r
169                     movechar(wlst, word, nsug, cpdsuggest);\r
170     }\r
171 \r
172     // did we add a char that should not be there\r
173     if ((nsug < maxSug) && (nsug > -1)) {\r
174         nsug = (utf8) ? extrachar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :\r
175                     extrachar(wlst, word, nsug, cpdsuggest);\r
176     }\r
177 \r
178     // did we just hit the wrong key in place of a good char\r
179     if ((nsug < maxSug) && (nsug > -1)) {\r
180         nsug = (utf8) ? badchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :\r
181                     badchar(wlst, word, nsug, cpdsuggest);\r
182     }\r
183 \r
184     // did we double two characters\r
185     if ((nsug < maxSug) && (nsug > -1)) {\r
186         nsug = (utf8) ? doubletwochars_utf(wlst, word_utf, wl, nsug, cpdsuggest) :\r
187                     doubletwochars(wlst, word, nsug, cpdsuggest);\r
188     }\r
189 \r
190 \r
191     // only suggest compound words when no other suggestion\r
192     if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1;\r
193 \r
194     // perhaps we forgot to hit space and two words ran together\r
195     if ((!nosplitsugs) && (nsug < maxSug) && (nsug > -1)) {\r
196                 nsug = twowords(wlst, word, nsug, cpdsuggest);\r
197         }\r
198 \r
199     } // repeating ``for'' statement compounding support\r
200 \r
201     if (nsug < 0) {\r
202      // we ran out of memory - we should free up as much as possible\r
203        for (int i = 0; i < maxSug; i++)\r
204          if (wlst[i] != NULL) free(wlst[i]);\r
205        free(wlst);\r
206        wlst = NULL;\r
207     }\r
208 \r
209     *slst = wlst;\r
210     return nsug;\r
211 }\r
212 \r
213 // generate suggestions for a word with typical mistake\r
214 //    pass in address of array of char * pointers\r
215 #ifdef HUNSPELL_EXPERIMENTAL\r
216 int SuggestMgr::suggest_auto(char*** slst, const char * w, int nsug)\r
217 {\r
218     int nocompoundtwowords = 0;\r
219     char ** wlst;\r
220 \r
221   char w2[MAXWORDUTF8LEN];\r
222   const char * word = w;\r
223 \r
224   // word reversing wrapper for complex prefixes\r
225   if (complexprefixes) {\r
226     strcpy(w2, w);\r
227     if (utf8) reverseword_utf(w2); else reverseword(w2);\r
228     word = w2;\r
229   }\r
230 \r
231     if (*slst) {\r
232         wlst = *slst;\r
233     } else {\r
234         wlst = (char **) malloc(maxSug * sizeof(char *));\r
235         if (wlst == NULL) return -1;\r
236     }\r
237 \r
238     for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {\r
239 \r
240     // perhaps we made a typical fault of spelling\r
241     if ((nsug < maxSug) && (nsug > -1))\r
242     nsug = replchars(wlst, word, nsug, cpdsuggest);\r
243 \r
244     // perhaps we made chose the wrong char from a related set\r
245     if ((nsug < maxSug) && (nsug > -1) && (cpdsuggest == 0))\r
246       nsug = mapchars(wlst, word, nsug);\r
247 \r
248     if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1;\r
249 \r
250     // perhaps we forgot to hit space and two words ran together\r
251 \r
252     if ((nsug < maxSug) && (nsug > -1) && check_forbidden(word, strlen(word))) {\r
253                 nsug = twowords(wlst, word, nsug, cpdsuggest);\r
254         }\r
255     \r
256     } // repeating ``for'' statement compounding support\r
257 \r
258     if (nsug < 0) {\r
259        for (int i=0;i<maxSug; i++)\r
260          if (wlst[i] != NULL) free(wlst[i]);\r
261        free(wlst);\r
262        return -1;\r
263     }\r
264 \r
265     *slst = wlst;\r
266     return nsug;\r
267 }\r
268 #endif // END OF HUNSPELL_EXPERIMENTAL CODE\r
269 \r
270 // suggestions for an uppercase word (html -> HTML)\r
271 int SuggestMgr::capchars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)\r
272 {\r
273   char candidate[MAXSWUTF8L];\r
274   w_char candidate_utf[MAXSWL];\r
275   memcpy(candidate_utf, word, wl * sizeof(w_char));\r
276   mkallcap_utf(candidate_utf, wl, pAMgr->get_langnum());\r
277   u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);\r
278   return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);\r
279 }\r
280 \r
281 // suggestions for an uppercase word (html -> HTML)\r
282 int SuggestMgr::capchars(char** wlst, const char * word, int ns, int cpdsuggest)\r
283 {\r
284   char candidate[MAXSWUTF8L];\r
285   strcpy(candidate, word);\r
286   mkallcap(candidate, csconv);\r
287   return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);\r
288 }\r
289 \r
290 // suggestions for when chose the wrong char out of a related set\r
291 int SuggestMgr::mapchars(char** wlst, const char * word, int ns)\r
292 {\r
293   time_t timelimit;\r
294   int timer;\r
295   \r
296   int wl = strlen(word);\r
297   if (wl < 2 || ! pAMgr) return ns;\r
298 \r
299   int nummap = pAMgr->get_nummap();\r
300   struct mapentry* maptable = pAMgr->get_maptable();\r
301   if (maptable==NULL) return ns;\r
302 \r
303   timelimit = time(NULL);\r
304   timer = MINTIMER;\r
305   if (utf8) {\r
306     w_char w[MAXSWL];\r
307     int len = u8_u16(w, MAXSWL, word);\r
308     ns = map_related_utf(w, len, 0, wlst, ns, maptable, nummap, &timer, &timelimit);\r
309   } else ns = map_related(word, 0, wlst, ns, maptable, nummap, &timer, &timelimit);\r
310   return ns;\r
311 }\r
312 \r
313 int SuggestMgr::map_related(const char * word, int i, char** wlst, int ns,\r
314     const mapentry* maptable, int nummap, int * timer, time_t * timelimit)\r
315 {\r
316   char c = *(word + i);  \r
317   if (c == 0) {\r
318       int cwrd = 1;\r
319       int wl = strlen(word);\r
320       for (int m=0; m < ns; m++)\r
321           if (strcmp(word,wlst[m]) == 0) cwrd = 0;\r
322       if ((cwrd) && (checkword(word, wl, 0, timer, timelimit) || \r
323         checkword(word, wl, 1, timer, timelimit))) {\r
324           if (ns < maxSug) {\r
325               wlst[ns] = mystrdup(word);\r
326               if (wlst[ns] == NULL) return -1;\r
327               ns++;\r
328           }\r
329       }\r
330       return ns;\r
331   } \r
332   int in_map = 0;\r
333   for (int j = 0; j < nummap; j++) {\r
334     if (strchr(maptable[j].set,c) != 0) {\r
335       in_map = 1;\r
336       char * newword = mystrdup(word);\r
337       for (int k = 0; k < maptable[j].len; k++) {\r
338         *(newword + i) = *(maptable[j].set + k);\r
339         ns = map_related(newword, (i+1), wlst, ns, maptable, nummap, timer, timelimit);\r
340         if (!(*timelimit)) return ns;\r
341       }\r
342       free(newword);\r
343     }\r
344   }\r
345   if (!in_map) {\r
346      i++;\r
347      ns = map_related(word, i, wlst, ns, maptable, nummap, timer, timelimit);\r
348   }\r
349   return ns;\r
350 }\r
351 \r
352 int SuggestMgr::map_related_utf(w_char * word, int len, int i, char** wlst, int ns,\r
353     const mapentry* maptable, int nummap, int * timer, time_t * timelimit) \r
354 {\r
355   if (i == len) {\r
356       int cwrd = 1;\r
357       int wl;\r
358       char s[MAXSWUTF8L];\r
359       u16_u8(s, MAXSWUTF8L, word, len);\r
360       wl = strlen(s);\r
361       for (int m=0; m < ns; m++)\r
362           if (strcmp(s,wlst[m]) == 0) cwrd = 0;\r
363       if ((cwrd) && (checkword(s, wl, 0, timer, timelimit) || \r
364             checkword(s, wl, 1, timer, timelimit))) {\r
365           if (ns < maxSug) {\r
366               wlst[ns] = mystrdup(s);\r
367               if (wlst[ns] == NULL) return -1;\r
368               ns++;\r
369           }\r
370       }\r
371       return ns;\r
372   } \r
373   int in_map = 0;\r
374   unsigned short c = *((unsigned short *) word + i);\r
375   for (int j = 0; j < nummap; j++) {\r
376     if (flag_bsearch((unsigned short *) maptable[j].set_utf16, c, maptable[j].len)) {\r
377       in_map = 1;\r
378       for (int k = 0; k < maptable[j].len; k++) {\r
379         *(word + i) = *(maptable[j].set_utf16 + k);\r
380         ns = map_related_utf(word, len, i + 1, wlst, ns, maptable, nummap, timer, timelimit);\r
381         if (!(*timelimit)) return ns;\r
382       }\r
383       *((unsigned short *) word + i) = c;\r
384     }\r
385   }\r
386   if (!in_map) {\r
387      i++;\r
388      ns = map_related_utf(word, len, i, wlst, ns, maptable, nummap, timer, timelimit);\r
389   }\r
390   return ns;\r
391 }\r
392 \r
393 \r
394 \r
395 // suggestions for a typical fault of spelling, that\r
396 // differs with more, than 1 letter from the right form.\r
397 int SuggestMgr::replchars(char** wlst, const char * word, int ns, int cpdsuggest)\r
398 {\r
399   char candidate[MAXSWUTF8L];\r
400   const char * r;\r
401   int lenr, lenp;\r
402   int wl = strlen(word);\r
403   if (wl < 2 || ! pAMgr) return ns;\r
404   int numrep = pAMgr->get_numrep();\r
405   struct replentry* reptable = pAMgr->get_reptable();\r
406   if (reptable==NULL) return ns;\r
407   for (int i=0; i < numrep; i++ ) {\r
408       r = word;\r
409       lenr = strlen(reptable[i].pattern2);\r
410       lenp = strlen(reptable[i].pattern);\r
411       // search every occurence of the pattern in the word\r
412       while ((r=strstr(r, reptable[i].pattern)) != NULL) {\r
413           strcpy(candidate, word);\r
414           if (r-word + lenr + strlen(r+lenp) >= MAXSWUTF8L) break;\r
415           strcpy(candidate+(r-word),reptable[i].pattern2);\r
416           strcpy(candidate+(r-word)+lenr, r+lenp);\r
417           ns = testsug(wlst, candidate, wl-lenp+lenr, ns, cpdsuggest, NULL, NULL);\r
418           if (ns == -1) return -1;\r
419           r++; // search for the next letter\r
420       }\r
421    }\r
422    return ns;\r
423 }\r
424 \r
425 // perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation)\r
426 int SuggestMgr::doubletwochars(char** wlst, const char * word, int ns, int cpdsuggest)\r
427 {\r
428   char candidate[MAXSWUTF8L];\r
429   int state=0;\r
430   int wl = strlen(word);\r
431   if (wl < 5 || ! pAMgr) return ns;\r
432   for (int i=2; i < wl; i++ ) {\r
433       if (word[i]==word[i-2]) {\r
434           state++;\r
435           if (state==3) {\r
436             strcpy(candidate,word);\r
437             strcpy(candidate+i-1,word+i+1);\r
438             ns = testsug(wlst, candidate, wl-2, ns, cpdsuggest, NULL, NULL);\r
439             if (ns == -1) return -1;\r
440             state=0;\r
441           }\r
442       } else {\r
443             state=0;\r
444       }\r
445   }\r
446   return ns;\r
447 }\r
448 \r
449 // perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation)\r
450 int SuggestMgr::doubletwochars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)\r
451 {\r
452   //w_char        tmpc;\r
453   w_char        candidate_utf[MAXSWL];\r
454   char          candidate[MAXSWUTF8L];\r
455   int state=0;\r
456   if (wl < 5 || ! pAMgr) return ns;\r
457   for (int i=2; i < wl; i++) {\r
458       if ((word[i].l==word[i-2].l) && (word[i].h==word[i-2].h))  {\r
459           state++;\r
460           if (state==3) {\r
461             memcpy(candidate_utf, word, (i - 1) * sizeof(w_char));\r
462             memcpy(candidate_utf+i-1, word+i+1, (wl-i-1) * sizeof(w_char));\r
463             u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl-2);\r
464             ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);\r
465             if (ns == -1) return -1;\r
466             state=0;\r
467           }\r
468       } else {\r
469             state=0;\r
470       }\r
471   }\r
472   return ns;\r
473 }\r
474 \r
475 // error is wrong char in place of correct one\r
476 int SuggestMgr::badchar(char ** wlst, const char * word, int ns, int cpdsuggest)\r
477 {\r
478   char  tmpc;\r
479   char  candidate[MAXSWUTF8L];\r
480   time_t timelimit = time(NULL);\r
481   int timer = MINTIMER;\r
482   int wl = strlen(word);\r
483   strcpy(candidate, word);\r
484   // swap out each char one by one and try all the tryme\r
485   // chars in its place to see if that makes a good word\r
486   for (int i=0; i < wl; i++) {\r
487     tmpc = candidate[i];\r
488     for (int j=0; j < ctryl; j++) {\r
489        if (ctry[j] == tmpc) continue;\r
490        candidate[i] = ctry[j];\r
491        ns = testsug(wlst, candidate, wl, ns, cpdsuggest, &timer, &timelimit);\r
492        if (ns == -1) return -1;\r
493        if (!timelimit) return ns;\r
494        candidate[i] = tmpc;\r
495     }\r
496   }\r
497   return ns;\r
498 }\r
499 \r
500 // error is wrong char in place of correct one\r
501 int SuggestMgr::badchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)\r
502 {\r
503   w_char        tmpc;\r
504   w_char        candidate_utf[MAXSWL];\r
505   char          candidate[MAXSWUTF8L];\r
506   time_t timelimit = time(NULL);\r
507   int timer = MINTIMER;  \r
508   memcpy(candidate_utf, word, wl * sizeof(w_char));\r
509   // swap out each char one by one and try all the tryme\r
510   // chars in its place to see if that makes a good word\r
511   for (int i=0; i < wl; i++) {\r
512     tmpc = candidate_utf[i];\r
513     for (int j=0; j < ctryl; j++) {\r
514        if ((ctry_utf[j].l == tmpc.l) && (ctry_utf[j].h == tmpc.h)) continue;\r
515        candidate_utf[i] = ctry_utf[j];\r
516        u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);\r
517        ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);\r
518        if (ns == -1) return -1;\r
519        if (!timelimit) return ns;\r
520        candidate_utf[i] = tmpc;\r
521     }\r
522   }\r
523   return ns;\r
524 }\r
525 \r
526 // error is word has an extra letter it does not need \r
527 int SuggestMgr::extrachar_utf(char** wlst, const w_char * word, int wl, int ns, int cpdsuggest)\r
528 {\r
529    char candidate[MAXSWUTF8L];\r
530    w_char candidate_utf[MAXSWL];\r
531    const w_char * p;\r
532    w_char * r;\r
533    if (wl < 2) return ns;\r
534    // try omitting one char of word at a time\r
535    memcpy(candidate_utf, word + 1, (wl - 1) * sizeof(w_char));\r
536    for (p = word, r = candidate_utf;  p < word + wl;  ) {\r
537        u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl - 1);       \r
538        ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);\r
539        if (ns == -1) return -1;\r
540        *r++ = *p++;\r
541    }\r
542    return ns;\r
543 }\r
544 \r
545 // error is word has an extra letter it does not need \r
546 int SuggestMgr::extrachar(char** wlst, const char * word, int ns, int cpdsuggest)\r
547 {\r
548    char    candidate[MAXSWUTF8L];\r
549    const char *  p;\r
550    char *  r;\r
551    int wl = strlen(word);\r
552    if (wl < 2) return ns;\r
553    // try omitting one char of word at a time\r
554    strcpy (candidate, word + 1);\r
555    for (p = word, r = candidate;  *p != 0;  ) {\r
556       ns = testsug(wlst, candidate, wl-1, ns, cpdsuggest, NULL, NULL);\r
557       if (ns == -1) return -1;\r
558       *r++ = *p++;\r
559    }\r
560    return ns;\r
561 }\r
562 \r
563 \r
564 // error is missing a letter it needs\r
565 int SuggestMgr::forgotchar(char ** wlst, const char * word, int ns, int cpdsuggest)\r
566 {\r
567    char candidate[MAXSWUTF8L];\r
568    const char * p;\r
569    char *       q;\r
570    //int cwrd;\r
571    time_t timelimit = time(NULL);\r
572    int timer = MINTIMER;\r
573    int wl = strlen(word);\r
574    // try inserting a tryme character before every letter\r
575    strcpy(candidate + 1, word);\r
576    for (p = word, q = candidate;  *p != 0;  )  {\r
577       for (int i = 0;  i < ctryl;  i++) {\r
578          *q = ctry[i];\r
579          ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, &timer, &timelimit);\r
580          if (ns == -1) return -1;\r
581          if (!timelimit) return ns;\r
582       }\r
583       *q++ = *p++;\r
584    }\r
585    // now try adding one to end */\r
586    for (int i = 0;  i < ctryl;  i++) {\r
587       *q = ctry[i];\r
588       ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, NULL, NULL);\r
589       if (ns == -1) return -1;\r
590    }\r
591    return ns;\r
592 }\r
593 \r
594 // error is missing a letter it needs\r
595 int SuggestMgr::forgotchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)\r
596 {\r
597    w_char  candidate_utf[MAXSWL];\r
598    char    candidate[MAXSWUTF8L];\r
599    const w_char * p;\r
600    w_char * q;\r
601    int cwrd;\r
602    time_t timelimit = time(NULL);\r
603    int timer = MINTIMER;\r
604    // try inserting a tryme character before every letter\r
605    memcpy (candidate_utf + 1, word, wl * sizeof(w_char));\r
606    for (p = word, q = candidate_utf;  p < (word + wl); )  {\r
607       for (int i = 0;  i < ctryl;  i++) {\r
608          *q = ctry_utf[i];\r
609          cwrd = 1;\r
610          u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1);\r
611          ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);\r
612          if (ns == -1) return -1;\r
613          if (!timelimit) return ns;\r
614        }\r
615       *q++ = *p++;\r
616    }\r
617    // now try adding one to end */\r
618    for (int i = 0;  i < ctryl;  i++) {\r
619       *q = ctry_utf[i];\r
620       cwrd = 1;\r
621       u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1);\r
622       ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);\r
623       if (ns == -1) return -1;\r
624    }\r
625    return ns;\r
626 }\r
627 \r
628 \r
629 /* error is should have been two words */\r
630 int SuggestMgr::twowords(char ** wlst, const char * word, int ns, int cpdsuggest)\r
631 {\r
632     char candidate[MAXSWUTF8L];\r
633     char * p;\r
634     int c1, c2;\r
635     int forbidden = 0;\r
636     int cwrd;\r
637 \r
638     int wl=strlen(word);\r
639     if (wl < 3) return ns;\r
640     \r
641     if (pAMgr->get_langnum() == LANG_hu) forbidden = check_forbidden(word, wl);\r
642 \r
643     strcpy(candidate + 1, word);\r
644 \r
645     // split the string into two pieces after every char\r
646     // if both pieces are good words make them a suggestion\r
647     for (p = candidate + 1;  p[1] != '\0';  p++) {\r
648        p[-1] = *p;\r
649        // go to end of the UTF-8 character\r
650        while (utf8 && ((p[1] & 0xc0) == 0x80)) {\r
651          p++;\r
652          p[-1] = *p;\r
653        }\r
654        *p = '\0';\r
655        c1 = checkword(candidate,strlen(candidate), cpdsuggest, NULL, NULL);\r
656        if (c1) {\r
657          c2 = checkword((p+1),strlen(p+1), cpdsuggest, NULL, NULL);\r
658          if (c2) {\r
659             *p = ' ';\r
660 \r
661             // spec. Hungarian code (need a better compound word support)\r
662             if ((pAMgr->get_langnum() == LANG_hu) && !forbidden &&\r
663                 // if 3 repeating letter, use - instead of space\r
664                 (((p[-1] == p[1]) && (((p>candidate+1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) ||\r
665                 // or multiple compounding, with more, than 6 syllables\r
666                 ((c1 == 3) && (c2 >= 2)))) *p = '-';\r
667 \r
668             cwrd = 1;\r
669             for (int k=0; k < ns; k++)\r
670                 if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;\r
671             if (ns < maxSug) {\r
672                 if (cwrd) {\r
673                     wlst[ns] = mystrdup(candidate);\r
674                     if (wlst[ns] == NULL) return -1;\r
675                     ns++;\r
676                 }\r
677             } else return ns;\r
678          }\r
679        }\r
680     }\r
681     return ns;\r
682 }\r
683 \r
684 \r
685 // error is adjacent letter were swapped\r
686 int SuggestMgr::swapchar(char ** wlst, const char * word, int ns, int cpdsuggest)\r
687 {\r
688    char candidate[MAXSWUTF8L];\r
689    char * p;\r
690    char tmpc;\r
691    int wl=strlen(word);\r
692    // try swapping adjacent chars one by one\r
693    strcpy(candidate, word);\r
694    for (p = candidate;  p[1] != 0;  p++) {\r
695       tmpc = *p;\r
696       *p = p[1];\r
697       p[1] = tmpc;\r
698       ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);\r
699       if (ns == -1) return -1;\r
700       p[1] = *p;\r
701       *p = tmpc;\r
702    }\r
703    return ns;\r
704 }\r
705 \r
706 // error is adjacent letter were swapped\r
707 int SuggestMgr::swapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)\r
708 {\r
709    w_char candidate_utf[MAXSWL];\r
710    char   candidate[MAXSWUTF8L];\r
711    w_char * p;\r
712    w_char tmpc;\r
713    // try swapping adjacent chars one by one\r
714    memcpy (candidate_utf, word, wl * sizeof(w_char));\r
715    for (p = candidate_utf;  p < (candidate_utf + wl - 1);  p++) {\r
716       tmpc = *p;\r
717       *p = p[1];\r
718       p[1] = tmpc;\r
719       u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);\r
720       ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);\r
721       if (ns == -1) return -1;\r
722       p[1] = *p;\r
723       *p = tmpc;\r
724    }\r
725    return ns;\r
726 }\r
727 \r
728 // error is not adjacent letter were swapped\r
729 int SuggestMgr::longswapchar(char ** wlst, const char * word, int ns, int cpdsuggest)\r
730 {\r
731    char candidate[MAXSWUTF8L];\r
732    char * p;\r
733    char * q;\r
734    char tmpc;\r
735    int wl=strlen(word);\r
736    // try swapping not adjacent chars one by one\r
737    strcpy(candidate, word);\r
738    for (p = candidate;  *p != 0;  p++) {\r
739     for (q = candidate;  *q != 0;  q++) {\r
740      if (abs(p-q) > 1) {\r
741       tmpc = *p;\r
742       *p = *q;\r
743       *q = tmpc;\r
744       ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);\r
745       if (ns == -1) return -1;\r
746       *q = *p;\r
747       *p = tmpc;\r
748      }\r
749     }\r
750    }\r
751    return ns;\r
752 }\r
753 \r
754 \r
755 // error is adjacent letter were swapped\r
756 int SuggestMgr::longswapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)\r
757 {\r
758    w_char candidate_utf[MAXSWL];\r
759    char   candidate[MAXSWUTF8L];\r
760    w_char * p;\r
761    w_char * q;\r
762    w_char tmpc;\r
763    // try swapping not adjacent chars\r
764    memcpy (candidate_utf, word, wl * sizeof(w_char));\r
765    for (p = candidate_utf;  p < (candidate_utf + wl);  p++) {\r
766      for (q = candidate_utf;  q < (candidate_utf + wl);  q++) {\r
767        if (abs(p-q) > 1) {\r
768          tmpc = *p;\r
769          *p = *q;\r
770          *q = tmpc;\r
771          ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);\r
772          if (ns == -1) return -1;\r
773          *q = *p;\r
774          *p = tmpc;\r
775        }\r
776      }\r
777    }\r
778    return ns;\r
779 }\r
780 \r
781 // error is a letter was moved\r
782 int SuggestMgr::movechar(char ** wlst, const char * word, int ns, int cpdsuggest)\r
783 {\r
784    char candidate[MAXSWUTF8L];\r
785    char * p;\r
786    char * q;\r
787    char tmpc;\r
788 \r
789    int wl=strlen(word);\r
790    // try moving a char\r
791    strcpy(candidate, word);\r
792    for (p = candidate;  *p != 0;  p++) {\r
793      for (q = p + 1;  (*q != 0) && ((q - p) < 10);  q++) {\r
794       tmpc = *(q-1);\r
795       *(q-1) = *q;\r
796       *q = tmpc;\r
797       if ((q-p) < 2) continue; // omit swap char\r
798       ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);\r
799       if (ns == -1) return -1;\r
800     }\r
801     strcpy(candidate, word);\r
802    }\r
803    for (p = candidate + wl - 1;  p > candidate;  p--) {\r
804      for (q = p - 1;  (q >= candidate) && ((p - q) < 10);  q--) {\r
805       tmpc = *(q+1);\r
806       *(q+1) = *q;\r
807       *q = tmpc;\r
808       if ((p-q) < 2) continue; // omit swap char\r
809       ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);\r
810       if (ns == -1) return -1;\r
811     }\r
812     strcpy(candidate, word);\r
813    }   \r
814    return ns;\r
815 }\r
816 \r
817 // error is a letter was moved\r
818 int SuggestMgr::movechar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)\r
819 {\r
820    w_char candidate_utf[MAXSWL];\r
821    char   candidate[MAXSWUTF8L];\r
822    w_char * p;\r
823    w_char * q;\r
824    w_char tmpc;\r
825    // try moving a char\r
826    memcpy (candidate_utf, word, wl * sizeof(w_char));\r
827    for (p = candidate_utf;  p < (candidate_utf + wl);  p++) {\r
828      for (q = p + 1;  (q < (candidate_utf + wl)) && ((q - p) < 10);  q++) {\r
829          tmpc = *(q-1);\r
830          *(q-1) = *q;\r
831          *q = tmpc;\r
832          if ((q-p) < 2) continue; // omit swap char\r
833          u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);\r
834          ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);\r
835          if (ns == -1) return -1;\r
836      }\r
837      memcpy (candidate_utf, word, wl * sizeof(w_char));\r
838    }\r
839    for (p = candidate_utf + wl - 1;  p > candidate_utf;  p--) {\r
840      for (q = p - 1;  (q >= candidate_utf) && ((p - q) < 10);  q--) {\r
841          tmpc = *(q+1);\r
842          *(q+1) = *q;\r
843          *q = tmpc;\r
844          if ((p-q) < 2) continue; // omit swap char\r
845          u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);\r
846          ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);\r
847          if (ns == -1) return -1;\r
848      }\r
849      memcpy (candidate_utf, word, wl * sizeof(w_char));\r
850    }\r
851    return ns;   \r
852 }\r
853 \r
854 // generate a set of suggestions for very poorly spelled words\r
855 int SuggestMgr::ngsuggest(char** wlst, char * w, HashMgr* pHMgr)\r
856 {\r
857 \r
858   int i, j;\r
859   int lval;\r
860   int sc;\r
861   int lp;\r
862   int nonbmp = 0;\r
863 \r
864   if (!pHMgr) return 0;\r
865 \r
866   // exhaustively search through all root words\r
867   // keeping track of the MAX_ROOTS most similar root words\r
868   struct hentry * roots[MAX_ROOTS];\r
869   int scores[MAX_ROOTS];\r
870   for (i = 0; i < MAX_ROOTS; i++) {\r
871     roots[i] = NULL;\r
872     scores[i] = -100 * i;\r
873   }\r
874   lp = MAX_ROOTS - 1;\r
875 \r
876   char w2[MAXWORDUTF8LEN];\r
877   char * word = w;\r
878 \r
879   // word reversing wrapper for complex prefixes\r
880   if (complexprefixes) {\r
881     strcpy(w2, w);\r
882     if (utf8) reverseword_utf(w2); else reverseword(w2);\r
883     word = w2;\r
884   }\r
885 \r
886   char mw[MAXSWUTF8L];\r
887   w_char u8[MAXSWL];\r
888   int nc = strlen(word);\r
889   int n = (utf8) ? u8_u16(u8, MAXSWL, word) : nc;\r
890   \r
891   // set character based ngram suggestion for words with non-BMP Unicode characters\r
892   if (n == -1) {\r
893     utf8 = 0;\r
894     n = nc;\r
895     nonbmp = 1;\r
896   }\r
897 \r
898   struct hentry* hp = NULL;\r
899   int col = -1;\r
900   while ((hp = pHMgr->walk_hashtable(col, hp))) {\r
901     if ((hp->astr) && (pAMgr) && \r
902        (TESTAFF(hp->astr, pAMgr->get_forbiddenword(), hp->alen) ||\r
903           TESTAFF(hp->astr, pAMgr->get_nosuggest(), hp->alen) ||\r
904           TESTAFF(hp->astr, pAMgr->get_onlyincompound(), hp->alen))) continue;\r
905     sc = ngram(3, word, hp->word, NGRAM_LONGER_WORSE);\r
906     if (sc > scores[lp]) {\r
907       scores[lp] = sc;  \r
908       roots[lp] = hp;\r
909       lval = sc;\r
910       for (j=0; j < MAX_ROOTS; j++)\r
911         if (scores[j] < lval) {\r
912           lp = j;\r
913           lval = scores[j];\r
914         }\r
915     }  \r
916   }\r
917 \r
918   // find minimum threshhold for a passable suggestion\r
919   // mangle original word three differnt ways\r
920   // and score them to generate a minimum acceptable score\r
921   int thresh = 0;\r
922   for (int sp = 1; sp < 4; sp++) {\r
923      if (utf8) {\r
924        for (int k=sp; k < n; k+=4) *((unsigned short *) u8 + k) = '*';\r
925        u16_u8(mw, MAXSWUTF8L, u8, n);\r
926        thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH);\r
927      } else {\r
928        strcpy(mw, word);\r
929        for (int k=sp; k < n; k+=4) *(mw + k) = '*';\r
930        thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH);\r
931      }\r
932   }\r
933   thresh = thresh / 3;\r
934   thresh--;\r
935 \r
936   // now expand affixes on each of these root words and\r
937   // and use length adjusted ngram scores to select\r
938   // possible suggestions\r
939   char * guess[MAX_GUESS];\r
940   int gscore[MAX_GUESS];\r
941   for(i=0;i<MAX_GUESS;i++) {\r
942      guess[i] = NULL;\r
943      gscore[i] = -100 * i;\r
944   }\r
945 \r
946   lp = MAX_GUESS - 1;\r
947 \r
948   struct guessword * glst;\r
949   glst = (struct guessword *) calloc(MAX_WORDS,sizeof(struct guessword));\r
950   if (! glst) {\r
951     if (nonbmp) utf8 = 1;\r
952     return 0;\r
953   }\r
954 \r
955   for (i = 0; i < MAX_ROOTS; i++) {\r
956 \r
957       if (roots[i]) {\r
958         struct hentry * rp = roots[i];\r
959         int nw = pAMgr->expand_rootword(glst, MAX_WORDS, rp->word, rp->wlen,\r
960                                         rp->astr, rp->alen, word, nc);\r
961 \r
962         for (int k = 0; k < nw ; k++) {\r
963            sc = ngram(n, word, glst[k].word, NGRAM_ANY_MISMATCH);\r
964            if ((sc > thresh)) {\r
965               if (sc > gscore[lp]) {\r
966                  if (guess[lp]) free (guess[lp]);\r
967                  gscore[lp] = sc;\r
968                  guess[lp] = glst[k].word;\r
969                  lval = sc;\r
970                  for (j=0; j < MAX_GUESS; j++)\r
971                     if (gscore[j] < lval) {\r
972                        lp = j;\r
973                        lval = gscore[j];\r
974                     }\r
975               } else free (glst[k].word);  \r
976            } else free(glst[k].word);\r
977         }\r
978       }\r
979   }\r
980   free(glst);\r
981 \r
982   // now we are done generating guesses\r
983   // sort in order of decreasing score\r
984   \r
985   bubblesort(&guess[0], &gscore[0], MAX_GUESS);\r
986 \r
987   // weight suggestions with a similarity index, based on\r
988   // the longest common subsequent algorithm and resort\r
989 \r
990   int is_swap;\r
991   for (i=0; i < MAX_GUESS; i++) {\r
992       if (guess[i]) {\r
993         // lowering guess[i]\r
994         char gl[MAXSWUTF8L];\r
995         int len;\r
996         if (utf8) {\r
997           w_char _w[MAXSWL];\r
998           len = u8_u16(_w, MAXSWL, guess[i]);\r
999           mkallsmall_utf(_w, len, pAMgr->get_langnum());\r
1000           u16_u8(gl, MAXSWUTF8L, _w, len);\r
1001         } else {\r
1002           strcpy(gl, guess[i]);\r
1003           mkallsmall(gl, csconv);\r
1004           len = strlen(guess[i]);\r
1005         }\r
1006 \r
1007         int _lcs = lcslen(word, gl);\r
1008 \r
1009         // same characters with different casing\r
1010         if ((n == len) && (n == _lcs)) {\r
1011             gscore[i] += 2000;\r
1012             break;\r
1013         }\r
1014         \r
1015         // heuristic weigthing of ngram scores\r
1016         gscore[i] +=\r
1017           // length of longest common subsequent minus lenght difference\r
1018           2 * _lcs - abs((int) (n - len)) +\r
1019           // weight equal first letter\r
1020           equalfirstletter(word, gl) +\r
1021           // weight equal character positions\r
1022           ((_lcs == commoncharacterpositions(word, gl, &is_swap)) ? 1: 0) +\r
1023           // swap character (not neighboring)\r
1024           ((is_swap) ? 1000 : 0);\r
1025       }\r
1026   }\r
1027 \r
1028   bubblesort(&guess[0], &gscore[0], MAX_GUESS);\r
1029 \r
1030   // copy over\r
1031 \r
1032   int ns = 0;\r
1033   int same = 0;\r
1034   for (i=0; i < MAX_GUESS; i++) {\r
1035     if (guess[i]) {\r
1036       if ((ns < maxngramsugs) && (ns < maxSug) && (!same || (gscore[i] > 1000))) {\r
1037         int unique = 1;\r
1038         // we have excellent suggestion(s)\r
1039         if (gscore[i] > 1000) same = 1;\r
1040         for (j=0; j < ns; j++)\r
1041           // don't suggest previous suggestions or a previous suggestion with prefixes or affixes\r
1042           if (strstr(guess[i], wlst[j]) || \r
1043             // check forbidden words\r
1044             !checkword(guess[i], strlen(guess[i]), 0, NULL, NULL)) unique = 0;\r
1045         if (unique) wlst[ns++] = guess[i]; else free(guess[i]);\r
1046       } else free(guess[i]);\r
1047     }\r
1048   }\r
1049 \r
1050   if (nonbmp) utf8 = 1;\r
1051   return ns;\r
1052 }\r
1053 \r
1054 \r
1055 // see if a candidate suggestion is spelled correctly\r
1056 // needs to check both root words and words with affixes\r
1057 \r
1058 // obsolote MySpell-HU modifications:\r
1059 // return value 2 and 3 marks compounding with hyphen (-)\r
1060 // `3' marks roots without suffix\r
1061 int SuggestMgr::checkword(const char * word, int len, int cpdsuggest, int * timer, time_t * timelimit)\r
1062 {\r
1063   struct hentry * rv=NULL;\r
1064   int nosuffix = 0;\r
1065   \r
1066   // check time limit\r
1067   if (timer) {\r
1068     (*timer)--;\r
1069     if (!(*timer) && timelimit) {\r
1070       if (time(NULL) > *timelimit) {\r
1071         *timelimit = 0;\r
1072         return 0;\r
1073       }\r
1074       *timer = MAXPLUSTIMER;\r
1075     }\r
1076   }\r
1077   \r
1078   if (pAMgr) { \r
1079     if (cpdsuggest==1) {\r
1080       if (pAMgr->get_compound()) {\r
1081         rv = pAMgr->compound_check(word,len,0,0,0,0,NULL,0,NULL,NULL,1);\r
1082         if (rv) return 3; // XXX obsolote categorisation\r
1083         }\r
1084         return 0;\r
1085     }\r
1086 \r
1087     rv = pAMgr->lookup(word);\r
1088 \r
1089     if (rv) {\r
1090         if ((rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)\r
1091                || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen))) return 0;\r
1092         if (rv->astr && (TESTAFF(rv->astr,pAMgr->get_pseudoroot(),rv->alen) ||\r
1093             TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL;\r
1094     } else rv = pAMgr->prefix_check(word, len, 0); // only prefix, and prefix + suffix XXX\r
1095     \r
1096     if (rv) {\r
1097         nosuffix=1;\r
1098     } else {\r
1099         rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, NULL); // only suffix\r
1100     }\r
1101 \r
1102     if (!rv && pAMgr->have_contclass()) {\r
1103         rv = pAMgr->suffix_check_twosfx(word, len, 0, NULL, FLAG_NULL);\r
1104         if (!rv) rv = pAMgr->prefix_check_twosfx(word, len, 1, FLAG_NULL);\r
1105     }\r
1106 \r
1107     // check forbidden words\r
1108     if ((rv) && (rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)\r
1109       || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen) ||\r
1110       TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) return 0;\r
1111 \r
1112     if (rv) { // XXX obsolote    \r
1113       if ((pAMgr->get_compoundflag()) && \r
1114           TESTAFF(rv->astr, pAMgr->get_compoundflag(), rv->alen)) return 2 + nosuffix; \r
1115       return 1;\r
1116     }\r
1117   }\r
1118   return 0;\r
1119 }\r
1120 \r
1121 int SuggestMgr::check_forbidden(const char * word, int len)\r
1122 {\r
1123   struct hentry * rv = NULL;\r
1124 \r
1125   if (pAMgr) { \r
1126     rv = pAMgr->lookup(word);\r
1127     if (rv && rv->astr && (TESTAFF(rv->astr,pAMgr->get_pseudoroot(),rv->alen) ||\r
1128         TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL;\r
1129     if (!(pAMgr->prefix_check(word,len,1)))\r
1130         rv = pAMgr->suffix_check(word,len, 0, NULL, NULL, 0, NULL); // prefix+suffix, suffix\r
1131     // check forbidden words\r
1132     if ((rv) && (rv->astr) && TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)) return 1;\r
1133    }\r
1134     return 0;\r
1135 }\r
1136 \r
1137 #ifdef HUNSPELL_EXPERIMENTAL\r
1138 // suggest stems, XXX experimental code\r
1139 int SuggestMgr::suggest_stems(char*** slst, const char * w, int nsug)\r
1140 {\r
1141     char buf[MAXSWUTF8L];\r
1142     char ** wlst;    \r
1143     int prevnsug = nsug;\r
1144 \r
1145   char w2[MAXWORDUTF8LEN];\r
1146   const char * word = w;\r
1147 \r
1148   // word reversing wrapper for complex prefixes\r
1149   if (complexprefixes) {\r
1150     strcpy(w2, w);\r
1151     if (utf8) reverseword_utf(w2); else reverseword(w2);\r
1152     word = w2;\r
1153   }\r
1154 \r
1155     if (*slst) {\r
1156         wlst = *slst;\r
1157     } else {\r
1158         wlst = (char **) calloc(maxSug, sizeof(char *));\r
1159         if (wlst == NULL) return -1;\r
1160     }\r
1161     // perhaps there are a fix stem in the dictionary\r
1162     if ((nsug < maxSug) && (nsug > -1)) {\r
1163     \r
1164     nsug = fixstems(wlst, word, nsug);\r
1165     if (nsug == prevnsug) {\r
1166         char * s = mystrdup(word);\r
1167         char * p = s + strlen(s);\r
1168         while ((*p != '-') && (p != s)) p--;\r
1169         if (*p == '-') {\r
1170             *p = '\0';\r
1171             nsug = fixstems(wlst, s, nsug);\r
1172             if ((nsug == prevnsug) && (nsug < maxSug) && (nsug >= 0)) {\r
1173                 char * t;\r
1174                 buf[0] = '\0';\r
1175                 for (t = s; (t[0] != '\0') && ((t[0] >= '0') || (t[0] <= '9')); t++); // is a number?\r
1176                 if (*t != '\0') strcpy(buf, "# ");\r
1177                 strcat(buf, s);\r
1178                 wlst[nsug] = mystrdup(buf);\r
1179                 if (wlst[nsug] == NULL) return -1;\r
1180                 nsug++;\r
1181             }\r
1182             p++;\r
1183             nsug = fixstems(wlst, p, nsug);\r
1184         }\r
1185 \r
1186         free(s);\r
1187     }\r
1188     }\r
1189     \r
1190     if (nsug < 0) {\r
1191        for (int i=0;i<maxSug; i++)\r
1192          if (wlst[i] != NULL) free(wlst[i]);\r
1193          free(wlst);\r
1194        return -1;\r
1195     }\r
1196 \r
1197     *slst = wlst;\r
1198     return nsug;\r
1199 }\r
1200 \r
1201 \r
1202 // there are fix stems in dictionary\r
1203 int SuggestMgr::fixstems(char ** wlst, const char * word, int ns)\r
1204 {\r
1205     char buf[MAXSWUTF8L];\r
1206     char prefix[MAXSWUTF8L] = "";\r
1207 \r
1208     int dicstem = 1; // 0 = lookup, 1= affix, 2 = compound\r
1209     int cpdindex = 0;\r
1210     struct hentry * rv = NULL;\r
1211 \r
1212     int wl = strlen(word);\r
1213     int cmpdstemnum;\r
1214     int cmpdstem[MAXCOMPOUND];\r
1215 \r
1216     if (pAMgr) { \r
1217         rv = pAMgr->lookup(word);\r
1218         if (rv) {\r
1219             dicstem = 0;\r
1220         } else {\r
1221             // try stripping off affixes \r
1222             rv = pAMgr->affix_check(word, wl);\r
1223 \r
1224             // else try check compound word\r
1225             if (!rv && pAMgr->get_compound()) {\r
1226                 rv = pAMgr->compound_check(word, wl,\r
1227                      0, 0, 100, 0, NULL, 0, &cmpdstemnum, cmpdstem,1);\r
1228 \r
1229                 if (rv) {\r
1230                     dicstem = 2;\r
1231                     for (int j = 0; j < cmpdstemnum; j++) {\r
1232                         cpdindex += cmpdstem[j];\r
1233                     }\r
1234                     if(! (pAMgr->lookup(word + cpdindex)))\r
1235                         pAMgr->affix_check(word + cpdindex, wl - cpdindex); // for prefix\r
1236                 }\r
1237             }\r
1238 \r
1239 \r
1240             if (pAMgr->get_prefix()) {\r
1241                 strcpy(prefix, pAMgr->get_prefix());\r
1242             }\r
1243 \r
1244             // XXX obsolete, will be a general solution for stemming\r
1245             if ((prefix) && (strncmp(prefix, "leg", 3)==0)) prefix[0] = '\0'; // (HU)       \r
1246         }\r
1247 \r
1248     }\r
1249 \r
1250 \r
1251 \r
1252     if ((rv) && (ns < maxSug)) {\r
1253     \r
1254         // check fixstem flag and not_valid_stem flag\r
1255         // first word\r
1256         if ((ns < maxSug) && (dicstem < 2)) { \r
1257             strcpy(buf, prefix);\r
1258             if ((dicstem > 0) && pAMgr->get_derived()) {\r
1259                 // XXX obsolote\r
1260                    if (strlen(prefix) == 1) {\r
1261                         strcat(buf, (pAMgr->get_derived()) + 1);\r
1262                    } else {\r
1263                         strcat(buf, pAMgr->get_derived());\r
1264                    }\r
1265                 } else {\r
1266                         // special stem in affix description\r
1267                         const char * wordchars = pAMgr->get_wordchars();\r
1268                         if (rv->description && \r
1269                            (strchr(wordchars, *(rv->description)))) {\r
1270                            char * desc = (rv->description) + 1;\r
1271                            while (strchr(wordchars, *desc)) desc++;\r
1272                            strncat(buf, rv->description, desc - (rv->description));\r
1273                         } else {\r
1274                             strcat(buf, rv->word);\r
1275                         }\r
1276                 }\r
1277             wlst[ns] = mystrdup(buf);\r
1278             if (wlst[ns] == NULL) return -1;\r
1279             ns++;\r
1280         }\r
1281 \r
1282         if (dicstem == 2) {\r
1283 \r
1284             // compound stem\r
1285 \r
1286 //          if (rv->astr && (strchr(rv->astr, '0') == NULL)) {\r
1287             if (rv->astr) {\r
1288                 strcpy(buf, word);\r
1289                 buf[cpdindex] = '\0';\r
1290                 if (prefix) strcat(buf, prefix);\r
1291                 if (pAMgr->get_derived()) {\r
1292                         strcat(buf, pAMgr->get_derived());\r
1293                 } else {\r
1294                         // special stem in affix description\r
1295                         const char * wordchars = pAMgr->get_wordchars();\r
1296                         if (rv->description && \r
1297                            (strchr(wordchars, *(rv->description)))) {\r
1298                            char * desc = (rv->description) + 1;\r
1299                            while (strchr(wordchars, *desc)) desc++;\r
1300                            strncat(buf, rv->description, desc - (rv->description));\r
1301                         } else {\r
1302                             strcat(buf, rv->word);\r
1303                         }\r
1304                 }\r
1305                 if (ns < maxSug) {\r
1306                     wlst[ns] = mystrdup(buf);\r
1307                     if (wlst[ns] == NULL) return -1;\r
1308                     ns++;\r
1309                 }\r
1310             }\r
1311         }\r
1312     }\r
1313     return ns;\r
1314 }\r
1315 \r
1316 // suggest possible stems\r
1317 int SuggestMgr::suggest_pos_stems(char*** slst, const char * w, int nsug)\r
1318 {\r
1319     char ** wlst;    \r
1320 \r
1321     struct hentry * rv = NULL;\r
1322 \r
1323   char w2[MAXSWUTF8L];\r
1324   const char * word = w;\r
1325 \r
1326   // word reversing wrapper for complex prefixes\r
1327   if (complexprefixes) {\r
1328     strcpy(w2, w);\r
1329     if (utf8) reverseword_utf(w2); else reverseword(w2);\r
1330     word = w2;\r
1331   }\r
1332 \r
1333     int wl = strlen(word);\r
1334 \r
1335 \r
1336     if (*slst) {\r
1337         wlst = *slst;\r
1338     } else {\r
1339         wlst = (char **) calloc(maxSug, sizeof(char *));\r
1340         if (wlst == NULL) return -1;\r
1341     }\r
1342 \r
1343     rv = pAMgr->suffix_check(word, wl, 0, NULL, wlst, maxSug, &nsug);\r
1344 \r
1345     // delete dash from end of word\r
1346     if (nsug > 0) {\r
1347         for (int j=0; j < nsug; j++) {\r
1348             if (wlst[j][strlen(wlst[j]) - 1] == '-') wlst[j][strlen(wlst[j]) - 1] = '\0';\r
1349         }\r
1350     }\r
1351 \r
1352     *slst = wlst;\r
1353     return nsug;\r
1354 }\r
1355 \r
1356 \r
1357 char * SuggestMgr::suggest_morph(const char * w)\r
1358 {\r
1359     char result[MAXLNLEN];\r
1360     char * r = (char *) result;\r
1361     char * st;\r
1362 \r
1363     struct hentry * rv = NULL;\r
1364 \r
1365     *result = '\0';\r
1366 \r
1367     if (! pAMgr) return NULL;\r
1368 \r
1369   char w2[MAXSWUTF8L];\r
1370   const char * word = w;\r
1371 \r
1372   // word reversing wrapper for complex prefixes\r
1373   if (complexprefixes) {\r
1374     strcpy(w2, w);\r
1375     if (utf8) reverseword_utf(w2); else reverseword(w2);\r
1376     word = w2;\r
1377   }\r
1378 \r
1379     rv = pAMgr->lookup(word);\r
1380     \r
1381     while (rv) {\r
1382         if ((!rv->astr) || !(TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen) ||\r
1383             TESTAFF(rv->astr, pAMgr->get_pseudoroot(), rv->alen) ||\r
1384             TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) {\r
1385             if (rv->description && ((!rv->astr) || \r
1386                 !TESTAFF(rv->astr, pAMgr->get_lemma_present(), rv->alen)))\r
1387                     strcat(result, word);\r
1388             if (rv->description) strcat(result, rv->description);\r
1389             strcat(result, "\n");\r
1390         }\r
1391         rv = rv->next_homonym;\r
1392     }\r
1393     \r
1394     st = pAMgr->affix_check_morph(word,strlen(word));\r
1395     if (st) {\r
1396         strcat(result, st);\r
1397         free(st);\r
1398     }\r
1399 \r
1400     if (pAMgr->get_compound() && (*result == '\0'))\r
1401         pAMgr->compound_check_morph(word, strlen(word),\r
1402                      0, 0, 100, 0,NULL, 0, &r, NULL);\r
1403     \r
1404     return (*result) ? mystrdup(line_uniq(delete_zeros(result))) : NULL;\r
1405 }\r
1406 \r
1407 char * SuggestMgr::suggest_morph_for_spelling_error(const char * word)\r
1408 {\r
1409     char * p = NULL;\r
1410     char ** wlst = (char **) calloc(maxSug, sizeof(char *));\r
1411     // we will use only the first suggestion\r
1412     for (int i = 0; i < maxSug - 1; i++) wlst[i] = "";\r
1413     int ns = suggest(&wlst, word, maxSug - 1);\r
1414     if (ns == maxSug) {\r
1415         p = suggest_morph(wlst[maxSug - 1]);\r
1416         free(wlst[maxSug - 1]);\r
1417     }\r
1418     if (wlst) free(wlst);\r
1419     return p;    \r
1420 }\r
1421 #endif // END OF HUNSPELL_EXPERIMENTAL CODE\r
1422 \r
1423 \r
1424 // generate an n-gram score comparing s1 and s2\r
1425 int SuggestMgr::ngram(int n, char * s1, const char * s2, int uselen)\r
1426 {\r
1427   int nscore = 0;\r
1428   int ns;\r
1429   int l1;\r
1430   int l2;\r
1431 \r
1432   if (utf8) {\r
1433     w_char su1[MAXSWL];\r
1434     w_char su2[MAXSWL];\r
1435     l1 = u8_u16(su1, MAXSWL, s1);\r
1436     l2 = u8_u16(su2, MAXSWL, s2);\r
1437     if (!l2 || (l1==-1) || (l2==-1)) return 0;\r
1438     // decapitalize dictionary word\r
1439     if (complexprefixes) {\r
1440       mkallsmall_utf(su2+l2-1, 1, pAMgr->get_langnum());\r
1441     } else {\r
1442       mkallsmall_utf(su2, 1, pAMgr->get_langnum());\r
1443     }\r
1444     for (int j = 1; j <= n; j++) {\r
1445       ns = 0;\r
1446       for (int i = 0; i <= (l1-j); i++) {\r
1447         for (int l = 0; l <= (l2-j); l++) {\r
1448             int k;\r
1449             for (k = 0; (k < j); k++) {\r
1450               w_char * c1 = su1 + i + k;\r
1451               w_char * c2 = su2 + l + k;\r
1452               if ((c1->l != c2->l) || (c1->h != c2->h)) break;\r
1453             }\r
1454             if (k == j) {\r
1455                 ns++;\r
1456                 break;\r
1457             }\r
1458         }\r
1459       }\r
1460       nscore = nscore + ns;\r
1461       if (ns < 2) break;\r
1462     }\r
1463   } else {  \r
1464     char t[MAXSWUTF8L];\r
1465     l1 = strlen(s1);\r
1466     l2 = strlen(s2);\r
1467     if (!l2) return 0;\r
1468     strcpy(t, s2);\r
1469     if (complexprefixes) {\r
1470       *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower;\r
1471     } else {\r
1472     mkallsmall(t, csconv);\r
1473     }\r
1474     for (int j = 1; j <= n; j++) {\r
1475       ns = 0;\r
1476       for (int i = 0; i <= (l1-j); i++) {\r
1477         char c = *(s1 + i + j);\r
1478         *(s1 + i + j) = '\0';\r
1479         if (strstr(t,(s1+i))) ns++;\r
1480         *(s1 + i + j ) = c;\r
1481       }\r
1482       nscore = nscore + ns;\r
1483       if (ns < 2) break;\r
1484     }\r
1485   }\r
1486   \r
1487   ns = 0;\r
1488   if (uselen == NGRAM_LONGER_WORSE) ns = (l2-l1)-2;\r
1489   if (uselen == NGRAM_ANY_MISMATCH) ns = abs(l2-l1)-2;\r
1490   ns = (nscore - ((ns > 0) ? ns : 0));\r
1491   return ns;\r
1492 }\r
1493 \r
1494 int SuggestMgr::equalfirstletter(char * s1, const char * s2) {\r
1495   if (utf8) {\r
1496     w_char su1[MAXSWL];\r
1497     w_char su2[MAXSWL];\r
1498     // decapitalize dictionary word\r
1499     if (complexprefixes) {\r
1500       int l1 = u8_u16(su1, MAXSWL, s1);\r
1501       int l2 = u8_u16(su2, MAXSWL, s2);\r
1502       if (*((short *)su1+l1-1) == *((short *)su2+l2-1)) return 1;\r
1503     } else {\r
1504       u8_u16(su1, 1, s1);\r
1505       u8_u16(su2, 1, s2);\r
1506       if (*((short *)su1) == *((short *)su2)) return 1;\r
1507     }\r
1508   } else {\r
1509     if (complexprefixes) {\r
1510       int l1 = strlen(s1);\r
1511       int l2 = strlen(s2);\r
1512       if (*(s2+l1-1) == *(s2+l2-1)) return 1;\r
1513     } else {\r
1514       if (*s1 == *s2) return 1;\r
1515     }\r
1516   }\r
1517   return 0;\r
1518 }\r
1519 \r
1520 int SuggestMgr::commoncharacterpositions(char * s1, const char * s2, int * is_swap) {\r
1521   int num = 0;\r
1522   int diff = 0;\r
1523   int diffpos[2];\r
1524   *is_swap = 0;\r
1525   if (utf8) {\r
1526     w_char su1[MAXSWL];\r
1527     w_char su2[MAXSWL];\r
1528     int l1 = u8_u16(su1, MAXSWL, s1);\r
1529     int l2 = u8_u16(su2, MAXSWL, s2);\r
1530     // decapitalize dictionary word\r
1531     if (complexprefixes) {\r
1532       mkallsmall_utf(su2+l2-1, 1, pAMgr->get_langnum());\r
1533     } else {\r
1534       mkallsmall_utf(su2, 1, pAMgr->get_langnum());\r
1535     }\r
1536     for (int i = 0; (i < l1) && (i < l2); i++) {\r
1537       if (((short *) su1)[i] == ((short *) su2)[i]) {\r
1538         num++;\r
1539       } else {\r
1540         if (diff < 2) diffpos[diff] = i;\r
1541         diff++;\r
1542       }\r
1543     }\r
1544     if ((diff == 2) && (l1 == l2) &&\r
1545         (((short *) su1)[diffpos[0]] == ((short *) su2)[diffpos[1]]) &&\r
1546         (((short *) su1)[diffpos[1]] == ((short *) su2)[diffpos[0]])) *is_swap = 1;\r
1547   } else {\r
1548     int i;\r
1549     char t[MAXSWUTF8L];\r
1550     strcpy(t, s2);\r
1551     // decapitalize dictionary word\r
1552     if (complexprefixes) {\r
1553       int l2 = strlen(t);\r
1554       *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower;\r
1555     } else {\r
1556       mkallsmall(t, csconv);\r
1557     }\r
1558     for (i = 0; (*(s1+i) != 0) && (*(t+i) != 0); i++) {\r
1559       if (*(s1+i) == *(t+i)) {\r
1560         num++;\r
1561       } else {\r
1562         if (diff < 2) diffpos[diff] = i;\r
1563         diff++;\r
1564       }\r
1565     }\r
1566     if ((diff == 2) && (*(s1+i) == 0) && (*(t+i) == 0) &&\r
1567       (*(s1+diffpos[0]) == *(t+diffpos[1])) &&\r
1568       (*(s1+diffpos[1]) == *(t+diffpos[0]))) *is_swap = 1;\r
1569   }\r
1570   return num;\r
1571 }\r
1572 \r
1573 int SuggestMgr::mystrlen(const char * word) {\r
1574   if (utf8) {\r
1575     w_char w[MAXSWL];\r
1576     return u8_u16(w, MAXSWL, word);\r
1577   } else return strlen(word);\r
1578 }\r
1579 \r
1580 // sort in decreasing order of score\r
1581 void SuggestMgr::bubblesort(char** rword, int* rsc, int n )\r
1582 {\r
1583       int m = 1;\r
1584       while (m < n) {\r
1585           int j = m;\r
1586           while (j > 0) {\r
1587             if (rsc[j-1] < rsc[j]) {\r
1588                 int sctmp = rsc[j-1];\r
1589                 char * wdtmp = rword[j-1];\r
1590                 rsc[j-1] = rsc[j];\r
1591                 rword[j-1] = rword[j];\r
1592                 rsc[j] = sctmp;\r
1593                 rword[j] = wdtmp;\r
1594                 j--;\r
1595             } else break;\r
1596           }\r
1597           m++;\r
1598       }\r
1599       return;\r
1600 }\r
1601 \r
1602 // longest common subsequence\r
1603 void SuggestMgr::lcs(const char * s, const char * s2, int * l1, int * l2, char ** result) {\r
1604   int n, m;\r
1605   w_char su[MAXSWL];\r
1606   w_char su2[MAXSWL];\r
1607   char * b;\r
1608   char * c;\r
1609   int i;\r
1610   int j;\r
1611   if (utf8) {\r
1612     m = u8_u16(su, MAXSWL, s);\r
1613     n = u8_u16(su2, MAXSWL, s2);\r
1614   } else {\r
1615     m = strlen(s);\r
1616     n = strlen(s2);\r
1617   }\r
1618   c = (char *) malloc((m + 1) * (n + 1));\r
1619   b = (char *) malloc((m + 1) * (n + 1));\r
1620   for (i = 1; i <= m; i++) c[i*(n+1)] = 0;\r
1621   for (j = 0; j <= n; j++) c[j] = 0;\r
1622   for (i = 1; i <= m; i++) {\r
1623     for (j = 1; j <= n; j++) {\r
1624       if ((utf8) && (*((short *) su+i-1) == *((short *)su2+j-1))\r
1625           || (!utf8) && ((*(s+i-1)) == (*(s2+j-1)))) {\r
1626         c[i*(n+1) + j] = c[(i-1)*(n+1) + j-1]+1;\r
1627         b[i*(n+1) + j] = LCS_UPLEFT;\r
1628       } else if (c[(i-1)*(n+1) + j] >= c[i*(n+1) + j-1]) {\r
1629         c[i*(n+1) + j] = c[(i-1)*(n+1) + j];\r
1630         b[i*(n+1) + j] = LCS_UP;\r
1631       } else {\r
1632         c[i*(n+1) + j] = c[i*(n+1) + j-1];\r
1633         b[i*(n+1) + j] = LCS_LEFT;\r
1634       }\r
1635     }\r
1636   }\r
1637   *result = b;\r
1638   free(c);\r
1639   *l1 = m;\r
1640   *l2 = n;\r
1641 }\r
1642 \r
1643 int SuggestMgr::lcslen(const char * s, const char* s2) {\r
1644   int m;\r
1645   int n;\r
1646   int i;\r
1647   int j;\r
1648   char * result;\r
1649   int len = 0;\r
1650   lcs(s, s2, &m, &n, &result);\r
1651   i = m;\r
1652   j = n;\r
1653   while ((i != 0) && (j != 0)) {\r
1654     if (result[i*(n+1) + j] == LCS_UPLEFT) {\r
1655       len++;\r
1656       i--;\r
1657       j--;\r
1658     } else if (result[i*(n+1) + j] == LCS_UP) {\r
1659       i--;\r
1660     } else j--;\r
1661   }\r
1662   if (result) free(result);\r
1663   return len;\r
1664 }\r