OSDN Git Service

merge original branch.
[tortoisegit/TortoiseGitJp.git] / ext / hunspell / hunspell.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 #else\r
9 #include <stdlib.h> \r
10 #include <string.h>\r
11 #include <stdio.h> \r
12 #endif\r
13 \r
14 #include "hunspell.hxx"\r
15 #include "hunspell.h"\r
16 \r
17 #ifndef MOZILLA_CLIENT\r
18 #ifndef W32\r
19 using namespace std;\r
20 #endif\r
21 #endif\r
22 \r
23 Hunspell::Hunspell(const char * affpath, const char * dpath)\r
24 {\r
25     encoding = NULL;\r
26     csconv = NULL;\r
27     utf8 = 0;\r
28     complexprefixes = 0;\r
29 \r
30     /* first set up the hash manager */\r
31     pHMgr = new HashMgr(dpath, affpath);\r
32 \r
33     /* next set up the affix manager */\r
34     /* it needs access to the hash manager lookup methods */\r
35     pAMgr = new AffixMgr(affpath,pHMgr);\r
36 \r
37     /* get the preferred try string and the dictionary */\r
38     /* encoding from the Affix Manager for that dictionary */\r
39     char * try_string = pAMgr->get_try_string();\r
40     encoding = pAMgr->get_encoding();\r
41     csconv = get_current_cs(encoding);\r
42     langnum = pAMgr->get_langnum();\r
43     utf8 = pAMgr->get_utf8();\r
44     complexprefixes = pAMgr->get_complexprefixes();\r
45     wordbreak = pAMgr->get_breaktable();\r
46 \r
47     /* and finally set up the suggestion manager */\r
48     pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr);\r
49     if (try_string) free(try_string);\r
50 \r
51 }\r
52 \r
53 Hunspell::~Hunspell()\r
54 {\r
55     if (pSMgr) delete pSMgr;\r
56     if (pAMgr) delete pAMgr;\r
57     if (pHMgr) delete pHMgr;\r
58     pSMgr = NULL;\r
59     pAMgr = NULL;\r
60     pHMgr = NULL;\r
61     csconv= NULL;\r
62     if (encoding) free(encoding);\r
63     encoding = NULL;\r
64 }\r
65 \r
66 \r
67 // make a copy of src at destination while removing all leading\r
68 // blanks and removing any trailing periods after recording\r
69 // their presence with the abbreviation flag\r
70 // also since already going through character by character, \r
71 // set the capitalization type\r
72 // return the length of the "cleaned" (and UTF-8 encoded) word\r
73 \r
74 int Hunspell::cleanword2(char * dest, const char * src, \r
75     w_char * dest_utf, int * nc, int * pcaptype, int * pabbrev)\r
76\r
77    unsigned char * p = (unsigned char *) dest;\r
78    const unsigned char * q = (const unsigned char * ) src;\r
79    int firstcap = 0;\r
80 \r
81    // first skip over any leading blanks\r
82    while ((*q != '\0') && (*q == ' ')) q++;\r
83    \r
84    // now strip off any trailing periods (recording their presence)\r
85    *pabbrev = 0;\r
86    int nl = strlen((const char *)q);\r
87    while ((nl > 0) && (*(q+nl-1)=='.')) {\r
88        nl--;\r
89        (*pabbrev)++;\r
90    }\r
91    \r
92    // if no characters are left it can't be capitalized\r
93    if (nl <= 0) { \r
94        *pcaptype = NOCAP;\r
95        *p = '\0';\r
96        return 0;\r
97    }\r
98 \r
99    // now determine the capitalization type of the first nl letters\r
100    int ncap = 0;\r
101    int nneutral = 0;\r
102    *nc = 0;\r
103 \r
104    if (!utf8) {\r
105       while (nl > 0) {\r
106          (*nc)++;\r
107          if (csconv[(*q)].ccase) ncap++;\r
108          if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++;\r
109          *p++ = *q++;\r
110          nl--;\r
111       }\r
112       // remember to terminate the destination string\r
113       *p = '\0';\r
114       if (ncap) {\r
115         firstcap = csconv[(unsigned char)(*dest)].ccase;\r
116       }\r
117    } else {\r
118       unsigned short idx;\r
119       *nc = u8_u16(dest_utf, MAXWORDLEN, (const char *) q);\r
120       // don't check too long words\r
121       if (*nc >= MAXWORDLEN) return 0;\r
122       if (*nc == -1) { // big Unicode character (non BMP area)\r
123          *pcaptype = NOCAP;\r
124          strcpy((char *) p, (char *) q);\r
125          return strlen(dest);\r
126       }\r
127       *nc -= *pabbrev;\r
128       for (int i = 0; i < *nc; i++) {\r
129          idx = (dest_utf[i].h << 8) + dest_utf[i].l;\r
130          if (idx != unicodetolower(idx, langnum)) ncap++;\r
131          if (unicodetoupper(idx, langnum) == unicodetolower(idx, langnum)) nneutral++;\r
132       }\r
133       u16_u8(dest, MAXWORDUTF8LEN, dest_utf, *nc);\r
134       if (ncap) {\r
135          idx = (dest_utf[0].h << 8) + dest_utf[0].l;\r
136          firstcap = (idx != unicodetolower(idx, langnum));\r
137       }\r
138    }\r
139 \r
140    // now finally set the captype\r
141    if (ncap == 0) {\r
142         *pcaptype = NOCAP;\r
143    } else if ((ncap == 1) && firstcap) {\r
144         *pcaptype = INITCAP;\r
145    } else if ((ncap == *nc) || ((ncap + nneutral) == *nc)) {\r
146         *pcaptype = ALLCAP;\r
147    } else if ((ncap > 1) && firstcap) {\r
148         *pcaptype = HUHINITCAP;\r
149    } else {\r
150         *pcaptype = HUHCAP;\r
151    }\r
152    return strlen(dest);\r
153\r
154 \r
155 int Hunspell::cleanword(char * dest, const char * src, \r
156     int * pcaptype, int * pabbrev)\r
157\r
158    unsigned char * p = (unsigned char *) dest;\r
159    const unsigned char * q = (const unsigned char * ) src;\r
160    int firstcap = 0;\r
161 \r
162    // first skip over any leading blanks\r
163    while ((*q != '\0') && (*q == ' ')) q++;\r
164    \r
165    // now strip off any trailing periods (recording their presence)\r
166    *pabbrev = 0;\r
167    int nl = strlen((const char *)q);\r
168    while ((nl > 0) && (*(q+nl-1)=='.')) {\r
169        nl--;\r
170        (*pabbrev)++;\r
171    }\r
172    \r
173    // if no characters are left it can't be capitalized\r
174    if (nl <= 0) { \r
175        *pcaptype = NOCAP;\r
176        *p = '\0';\r
177        return 0;\r
178    }\r
179 \r
180    // now determine the capitalization type of the first nl letters\r
181    int ncap = 0;\r
182    int nneutral = 0;\r
183    int nc = 0;\r
184 \r
185    if (!utf8) {\r
186       while (nl > 0) {\r
187          nc++;\r
188          if (csconv[(*q)].ccase) ncap++;\r
189          if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++;\r
190          *p++ = *q++;\r
191          nl--;\r
192       }\r
193       // remember to terminate the destination string\r
194       *p = '\0';\r
195       firstcap = csconv[(unsigned char)(*dest)].ccase;\r
196    } else {\r
197       unsigned short idx;\r
198       w_char t[MAXWORDLEN];\r
199       nc = u8_u16(t, MAXWORDLEN, src);\r
200       for (int i = 0; i < nc; i++) {\r
201          idx = (t[i].h << 8) + t[i].l;\r
202          if (idx != unicodetolower(idx, langnum)) ncap++;\r
203          if (unicodetoupper(idx, langnum) == unicodetolower(idx, langnum)) nneutral++;\r
204       }\r
205       u16_u8(dest, MAXWORDUTF8LEN, t, nc);\r
206       if (ncap) {\r
207          idx = (t[0].h << 8) + t[0].l;\r
208          firstcap = (idx != unicodetolower(idx, langnum));\r
209       }\r
210    }\r
211 \r
212    // now finally set the captype\r
213    if (ncap == 0) {\r
214         *pcaptype = NOCAP;\r
215    } else if ((ncap == 1) && firstcap) {\r
216         *pcaptype = INITCAP;\r
217    } else if ((ncap == nc) || ((ncap + nneutral) == nc)){\r
218         *pcaptype = ALLCAP;\r
219    } else if ((ncap > 1) && firstcap) {\r
220         *pcaptype = HUHINITCAP;\r
221    } else {\r
222         *pcaptype = HUHCAP;\r
223    }\r
224    return strlen(dest);\r
225\r
226        \r
227 \r
228 void Hunspell::mkallcap(char * p)\r
229 {\r
230   if (utf8) {\r
231       w_char u[MAXWORDLEN];\r
232       int nc = u8_u16(u, MAXWORDLEN, p);\r
233       unsigned short idx;\r
234       for (int i = 0; i < nc; i++) {\r
235          idx = (u[i].h << 8) + u[i].l;\r
236          if (idx != unicodetoupper(idx, langnum)) {\r
237             u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);\r
238             u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);\r
239          }\r
240       }\r
241       u16_u8(p, MAXWORDUTF8LEN, u, nc);\r
242   } else {\r
243     while (*p != '\0') { \r
244         *p = csconv[((unsigned char) *p)].cupper;\r
245         p++;\r
246     }\r
247   }\r
248 }\r
249 \r
250 int Hunspell::mkallcap2(char * p, w_char * u, int nc)\r
251 {\r
252   if (utf8) {\r
253       unsigned short idx;\r
254       for (int i = 0; i < nc; i++) {\r
255          idx = (u[i].h << 8) + u[i].l;\r
256          if (idx != unicodetoupper(idx, langnum)) {\r
257             u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);\r
258             u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);\r
259          }\r
260       }\r
261       u16_u8(p, MAXWORDUTF8LEN, u, nc);\r
262       return strlen(p);  \r
263   } else {\r
264     while (*p != '\0') { \r
265         *p = csconv[((unsigned char) *p)].cupper;\r
266         p++;\r
267     }\r
268   }\r
269   return nc;\r
270 }\r
271 \r
272 \r
273 void Hunspell::mkallsmall(char * p)\r
274 {\r
275     while (*p != '\0') { \r
276         *p = csconv[((unsigned char) *p)].clower;\r
277         p++;\r
278     }\r
279 }\r
280 \r
281 int Hunspell::mkallsmall2(char * p, w_char * u, int nc)\r
282 {\r
283   if (utf8) {\r
284       unsigned short idx;\r
285       for (int i = 0; i < nc; i++) {\r
286          idx = (u[i].h << 8) + u[i].l;\r
287          if (idx != unicodetolower(idx, langnum)) {\r
288             u[i].h = (unsigned char) (unicodetolower(idx, langnum) >> 8);\r
289             u[i].l = (unsigned char) (unicodetolower(idx, langnum) & 0x00FF);\r
290          }\r
291       }\r
292       u16_u8(p, MAXWORDUTF8LEN, u, nc);\r
293       return strlen(p);\r
294   } else {\r
295     while (*p != '\0') { \r
296         *p = csconv[((unsigned char) *p)].clower;\r
297         p++;\r
298     }\r
299   }\r
300   return nc;\r
301 }\r
302 \r
303 // convert UTF-8 sharp S codes to latin 1\r
304 char * Hunspell::sharps_u8_l1(char * dest, char * source) {\r
305     char * p = dest;\r
306     *p = *source;\r
307     for (p++, source++; *(source - 1); p++, source++) {\r
308         *p = *source;\r
309         if (*source == '?') *--p = '?';\r
310     }\r
311     return dest;\r
312 }\r
313 \r
314 // recursive search for right ss-?permutations\r
315 hentry * Hunspell::spellsharps(char * base, char * pos, int n,\r
316         int repnum, char * tmp, int * info, char **root) {\r
317     pos = strstr(pos, "ss");\r
318     if (pos && (n < MAXSHARPS)) {\r
319         *pos = '?';\r
320         *(pos + 1) = '?';\r
321         hentry * h = spellsharps(base, pos + 2, n + 1, repnum + 1, tmp, info, root);\r
322         if (h) return h;\r
323         *pos = 's';\r
324         *(pos + 1) = 's';\r
325         h = spellsharps(base, pos + 2, n + 1, repnum, tmp, info, root);\r
326         if (h) return h;\r
327     } else if (repnum > 0) {\r
328         if (utf8) return checkword(base, info, root);\r
329         return checkword(sharps_u8_l1(tmp, base), info, root);\r
330     }\r
331     return NULL;\r
332 }\r
333 \r
334 int Hunspell::is_keepcase(const hentry * rv) {\r
335     return pAMgr && rv->astr && pAMgr->get_keepcase() &&\r
336         TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen);\r
337 }\r
338 \r
339 /* check and insert a word to beginning of the suggestion array */\r
340 int Hunspell::insert_sug(char ***slst, char * word, int *ns) {\r
341     if (spell(word)) {\r
342         if (*ns == MAXSUGGESTION) {\r
343             (*ns)--;\r
344             free((*slst)[*ns]);\r
345         }\r
346         for (int k = *ns; k > 0; k--) (*slst)[k] = (*slst)[k - 1];\r
347         (*slst)[0] = mystrdup(word);\r
348         (*ns)++;\r
349     }\r
350     return 0;\r
351 }\r
352 \r
353 int Hunspell::spell(const char * word, int * info, char ** root)\r
354 {\r
355   struct hentry * rv=NULL;\r
356   // need larger vector. For example, Turkish capital letter I converted a\r
357   // 2-byte UTF-8 character (dotless i) by mkallsmall.\r
358   char cw[MAXWORDUTF8LEN + 4];\r
359   char wspace[MAXWORDUTF8LEN + 4];\r
360   w_char unicw[MAXWORDLEN + 1];\r
361   int nc = strlen(word);\r
362   int wl2 = 0;\r
363   if (utf8) {\r
364     if (nc >= MAXWORDUTF8LEN) return 0;\r
365   } else {\r
366     if (nc >= MAXWORDLEN) return 0;\r
367   }\r
368   int captype = 0;\r
369   int abbv = 0;\r
370   int wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);\r
371 \r
372   if (wl == 0) return 1;\r
373 \r
374   if (info) *info = 0; \r
375   if (root) *root = NULL;\r
376 \r
377   // allow numbers with dots and commas (but forbid double separators: "..", ",," etc.)\r
378   enum { NBEGIN, NNUM, NSEP };\r
379   int nstate = NBEGIN;\r
380   int i;\r
381 \r
382   for (i = 0; (i < wl); i++) {\r
383     if ((cw[i] <= '9') && (cw[i] >= '0')) {\r
384         nstate = NNUM;\r
385     } else if ((cw[i] == ',') || (cw[i] == '.') || (cw[i] == '-')) {\r
386         if ((nstate == NSEP) || (i == 0)) break;\r
387         nstate = NSEP;\r
388     } else break;\r
389   }\r
390   if ((i == wl) && (nstate == NNUM)) return 1;\r
391 \r
392   // LANG_hu section: number(s) + (percent or degree) with suffixes\r
393   if (langnum == LANG_hu) {\r
394     if ((nstate == NNUM) && ((cw[i] == '%') || (cw[i] == '?'))\r
395         && checkword(cw + i, info, root)) return 1;\r
396   }\r
397   // END of LANG_hu section\r
398 \r
399   switch(captype) {\r
400      case HUHCAP: \r
401      case HUHINITCAP: \r
402      case NOCAP: { \r
403                     rv = checkword(cw, info, root); \r
404                     if ((abbv) && !(rv)) {\r
405                         memcpy(wspace,cw,wl);\r
406                         *(wspace+wl) = '.';\r
407                         *(wspace+wl+1) = '\0';\r
408                         rv = checkword(wspace, info, root);\r
409                     }\r
410                     break;\r
411                  }\r
412      case ALLCAP: {\r
413                     rv = checkword(cw, info, root);\r
414                     if (rv) break;\r
415                     if (abbv) {\r
416                         memcpy(wspace,cw,wl);\r
417                         *(wspace+wl) = '.';\r
418                         *(wspace+wl+1) = '\0';\r
419                         rv = checkword(wspace, info, root);\r
420                         if (rv) break;\r
421                     }\r
422                     if (pAMgr && pAMgr->get_checksharps() && strstr(cw, "SS")) {\r
423                         char tmpword[MAXWORDUTF8LEN];\r
424                         wl = mkallsmall2(cw, unicw, nc);\r
425                         memcpy(wspace,cw,(wl+1));\r
426                         rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);\r
427                         if (!rv) {\r
428                             wl2 = mkinitcap2(cw, unicw, nc);\r
429                             rv = spellsharps(cw, cw, 0, 0, tmpword, info, root);\r
430                         }\r
431                         if ((abbv) && !(rv)) {\r
432                             *(wspace+wl) = '.';\r
433                             *(wspace+wl+1) = '\0';\r
434                             rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);\r
435                             if (!rv) {\r
436                                 memcpy(wspace, cw, wl2);\r
437                                 *(wspace+wl2) = '.';\r
438                                 *(wspace+wl2+1) = '\0';\r
439                                 rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);\r
440                             }\r
441                         }\r
442                         if (rv) break;\r
443                     }\r
444                 }\r
445      case INITCAP: { \r
446                      wl = mkallsmall2(cw, unicw, nc);\r
447                      memcpy(wspace,cw,(wl+1));                     \r
448                      rv = checkword(wspace, info, root);\r
449                      if (!rv || (is_keepcase(rv) && !((captype == INITCAP) &&\r
450                            // if CHECKSHARPS: KEEPCASE words with ?are allowed\r
451                            // in INITCAP form, too.\r
452                            pAMgr->get_checksharps() && ((utf8 && strstr(wspace, "脽")) || \r
453                             (!utf8 && strchr(wspace, '?')))))) {\r
454                         wl2 = mkinitcap2(cw, unicw, nc);\r
455                         rv = checkword(cw, info, root);\r
456                         if (rv && (captype == ALLCAP) && is_keepcase(rv)) rv = NULL;\r
457                      }\r
458                      if (abbv && !rv) {\r
459                          *(wspace+wl) = '.';\r
460                          *(wspace+wl+1) = '\0';\r
461                          rv = checkword(wspace, info, root);\r
462                          if (!rv || is_keepcase(rv)) {\r
463                             memcpy(wspace, cw, wl2);\r
464                             *(wspace+wl2) = '.';\r
465                             *(wspace+wl2+1) = '\0';\r
466                             rv = checkword(wspace, info, root);\r
467                             if (rv && ((captype == ALLCAP) && is_keepcase(rv))) rv = NULL;\r
468                          }\r
469                      }\r
470                      break;\r
471                    }               \r
472   }\r
473   \r
474   if (rv) return 1;\r
475 \r
476   // recursive breaking at break points (not good for morphological analysis)\r
477   if (wordbreak) {\r
478     char * s;\r
479     char r;\r
480     for (int j = 0; j < pAMgr->get_numbreak(); j++) {\r
481       s=(char *) strstr(cw, wordbreak[j]);\r
482       if (s) {\r
483         r = *s;\r
484         *s = '\0';\r
485         // examine 2 sides of the break point\r
486         if (spell(cw) && spell(s + strlen(wordbreak[j]))) {\r
487             *s = r;\r
488             return 1;\r
489         }\r
490         *s = r;\r
491       }\r
492     }\r
493   }\r
494 \r
495   // LANG_hu: compoundings with dashes and n-dashes XXX deprecated!\r
496   if (langnum == LANG_hu) {\r
497     int n;\r
498     // compound word with dash (HU) I18n\r
499     char * dash;\r
500     int result = 0;\r
501     // n-dash\r
502     dash = (char *) strstr(cw,"-");\r
503     if (dash && !wordbreak) {\r
504         *dash = '\0';\r
505         // examine 2 sides of the dash\r
506         if (spell(cw) && spell(dash + 3)) {\r
507             *dash = '?';\r
508             return 1;\r
509         }\r
510         *dash = '?';\r
511     }\r
512     dash = (char *) strchr(cw,'-');\r
513     if (dash) {\r
514         *dash='\0';      \r
515         // examine 2 sides of the dash\r
516         if (dash[1] == '\0') { // base word ending with dash\r
517             if (spell(cw)) return 1;\r
518         } else {\r
519             // first word ending with dash: word-\r
520             char r2 = *(dash + 1);\r
521             dash[0]='-';\r
522             dash[1]='\0';\r
523             result = spell(cw);\r
524             dash[1] = r2;\r
525             dash[0]='\0';\r
526             if (result && spell(dash+1) && ((strlen(dash+1) > 1) || (dash[1] == 'e') ||\r
527                 ((dash[1] > '0') && (dash[1] < '9')))) return 1;\r
528         }\r
529         // affixed number in correct word\r
530         if (result && (dash > cw) && (((*(dash-1)<='9') && (*(dash-1)>='0')) || (*(dash-1)>='.'))) {\r
531             *dash='-';\r
532             n = 1;\r
533             if (*(dash - n) == '.') n++;\r
534             // search first not a number character to left from dash\r
535             while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {\r
536                 n++;\r
537             }\r
538             if ((dash - n) < cw) n--;\r
539             // numbers: deprecated\r
540             for(; n >= 1; n--) {\r
541                 if ((*(dash - n) >= '0') && (*(dash - n) <= '9') &&\r
542                     checkword(dash - n, info, root)) return 1;\r
543             }\r
544         }\r
545     }\r
546   }\r
547   return 0;\r
548 }\r
549 \r
550 //int Hunspell::spell(const char * word) {\r
551 //  return spell(word, NULL, NULL);\r
552 //}\r
553 \r
554 struct hentry * Hunspell::checkword(const char * w, int * info, char ** root)\r
555 {\r
556   struct hentry * he = NULL;\r
557   int len;\r
558   char w2[MAXWORDUTF8LEN];\r
559   const char * word;\r
560 \r
561   char * ignoredchars = pAMgr->get_ignore();\r
562   if (ignoredchars != NULL) {\r
563      strcpy(w2, w);\r
564      if (utf8) {\r
565         int ignoredchars_utf16_len;\r
566         unsigned short * ignoredchars_utf16 = pAMgr->get_ignore_utf16(&ignoredchars_utf16_len);\r
567         remove_ignored_chars_utf(w2, ignoredchars_utf16, ignoredchars_utf16_len);\r
568      } else {\r
569         remove_ignored_chars(w2,ignoredchars);\r
570      }\r
571      word = w2;\r
572   } else word = w;\r
573 \r
574   // word reversing wrapper for complex prefixes\r
575   if (complexprefixes) {\r
576     if (word != w2) {\r
577       strcpy(w2, word);\r
578       word = w2;\r
579     }\r
580     if (utf8) reverseword_utf(w2); else reverseword(w2);\r
581   }\r
582 \r
583   // look word in hash table\r
584   if (pHMgr) he = pHMgr->lookup(word);\r
585 \r
586   // check forbidden and onlyincompound words\r
587   if ((he) && (he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {\r
588     info += SPELL_FORBIDDEN;\r
589     // LANG_hu section: set dash information for suggestions\r
590     if (langnum == LANG_hu) {\r
591         if (pAMgr->get_compoundflag() &&\r
592             TESTAFF(he->astr, pAMgr->get_compoundflag(), he->alen)) {\r
593                 info += SPELL_COMPOUND;\r
594         }\r
595     }\r
596     return NULL;\r
597   }\r
598 \r
599   // he = next not pseudoroot and not onlyincompound homonym or NULL\r
600   while (he && (he->astr) &&\r
601     ((pAMgr->get_pseudoroot() && TESTAFF(he->astr, pAMgr->get_pseudoroot(), he->alen)) ||\r
602        (pAMgr->get_onlyincompound() && TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen))\r
603     )) he = he->next_homonym;\r
604 \r
605   // check with affixes\r
606   if (!he && pAMgr) {\r
607      // try stripping off affixes */\r
608      len = strlen(word);\r
609      he = pAMgr->affix_check(word, len, 0);\r
610 \r
611      // check compound restriction\r
612      if (he && he->astr && pAMgr->get_onlyincompound() &&\r
613          TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) he = NULL;\r
614 \r
615      if (he) {\r
616         if ((he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {\r
617             info += SPELL_FORBIDDEN;\r
618             return NULL;\r
619         }\r
620         if (root) {\r
621             *root = mystrdup(he->word);\r
622             if (complexprefixes) {\r
623                 if (utf8) reverseword_utf(*root); else reverseword(*root);\r
624             }\r
625         }\r
626      // try check compound word\r
627      } else if (pAMgr->get_compound()) {\r
628           he = pAMgr->compound_check(word, len, \r
629                                   0,0,100,0,NULL,0,NULL,NULL,0);\r
630           // LANG_hu section: `moving rule' with last dash\r
631           if ((!he) && (langnum == LANG_hu) && (word[len-1]=='-')) {\r
632              char * dup = mystrdup(word);\r
633              dup[len-1] = '\0';\r
634              he = pAMgr->compound_check(dup, len-1, \r
635                                   -5,0,100,0,NULL,1,NULL,NULL,0);\r
636              free(dup);\r
637           }\r
638           // end of LANG speficic region          \r
639           if (he) {\r
640                 if (root) {\r
641                     *root = mystrdup(he->word);\r
642                     if (complexprefixes) {\r
643                         if (utf8) reverseword_utf(*root); else reverseword(*root);\r
644                     }\r
645                 }\r
646                 if (info) *info += SPELL_COMPOUND;\r
647           }\r
648      }\r
649 \r
650   }\r
651 \r
652   return he;\r
653 }\r
654 \r
655 int Hunspell::suggest(char*** slst, const char * word)\r
656 {\r
657   char cw[MAXWORDUTF8LEN + 4];\r
658   char wspace[MAXWORDUTF8LEN + 4];\r
659   if (! pSMgr) return 0;\r
660   w_char unicw[MAXWORDLEN + 1];\r
661   int nc = strlen(word);\r
662   if (utf8) {\r
663     if (nc >= MAXWORDUTF8LEN) return 0;\r
664   } else {\r
665     if (nc >= MAXWORDLEN) return 0;\r
666   }\r
667   int captype = 0;\r
668   int abbv = 0;\r
669   int wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);\r
670   if (wl == 0) return 0;\r
671   int ns = 0;\r
672   *slst = NULL;\r
673   int capwords = 0;\r
674   int ngramsugs = 0;\r
675 \r
676   switch(captype) {\r
677      case NOCAP:   { \r
678                      ns = pSMgr->suggest(slst, cw, ns);\r
679                      break;\r
680                    }\r
681 \r
682      case INITCAP: { \r
683                      capwords = 1;\r
684                      ns = pSMgr->suggest(slst, cw, ns);\r
685                      if (ns == -1) break;\r
686                      memcpy(wspace,cw,(wl+1));\r
687                      mkallsmall2(wspace, unicw, nc);\r
688                      ns = pSMgr->suggest(slst, wspace, ns);\r
689                      break;\r
690                    }\r
691      case HUHINITCAP:\r
692                     capwords = 1;\r
693      case HUHCAP: { \r
694                      ns = pSMgr->suggest(slst, cw, ns);\r
695                      if (ns != -1) {\r
696                         int prevns;\r
697                         if (captype == HUHINITCAP) {\r
698                             // TheOpenOffice.org -> The OpenOffice.org\r
699                             memcpy(wspace,cw,(wl+1));\r
700                             mkinitsmall2(wspace, unicw, nc);\r
701                             ns = pSMgr->suggest(slst, wspace, ns);\r
702                         }\r
703                         memcpy(wspace,cw,(wl+1));\r
704                         mkallsmall2(wspace, unicw, nc);\r
705                         insert_sug(slst, wspace, &ns);\r
706                         prevns = ns;\r
707                         ns = pSMgr->suggest(slst, wspace, ns);\r
708                         if (captype == HUHINITCAP) {\r
709                             mkinitcap2(wspace, unicw, nc);\r
710                             insert_sug(slst, wspace, &ns);\r
711                             ns = pSMgr->suggest(slst, wspace, ns);\r
712                         }\r
713                         // aNew -> "a New" (instead of "a new")\r
714                         for (int j = prevns; j < ns; j++) {\r
715                            char * space = strchr((*slst)[j],' ');\r
716                            if (space) {\r
717                                 int slen = strlen(space + 1);\r
718                                 // different case after space (need capitalisation)\r
719                                 if ((slen < wl) && strcmp(cw + wl - slen, space + 1)) {\r
720                                     w_char w[MAXWORDLEN + 1];\r
721                                     int wc = 0;\r
722                                     char * r = (*slst)[j];\r
723                                     if (utf8) wc = u8_u16(w, MAXWORDLEN, space + 1);\r
724                                     mkinitcap2(space + 1, w, wc);\r
725                                     // set as first suggestion\r
726                                     for (int k = j; k > 0; k--) (*slst)[k] = (*slst)[k - 1];\r
727                                     (*slst)[0] = r;\r
728                                 }\r
729                            }\r
730                         }\r
731                      }\r
732                      break;\r
733                    }\r
734 \r
735      case ALLCAP: { \r
736                      memcpy(wspace, cw, (wl+1));\r
737                      mkallsmall2(wspace, unicw, nc);\r
738                      ns = pSMgr->suggest(slst, wspace, ns);\r
739                      if (ns == -1) break;\r
740                      if (pAMgr && pAMgr->get_keepcase()) insert_sug(slst, wspace, &ns);\r
741                      mkinitcap2(wspace, unicw, nc);\r
742                      ns = pSMgr->suggest(slst, wspace, ns);\r
743                      for (int j=0; j < ns; j++) {\r
744                         mkallcap((*slst)[j]);\r
745                         if (pAMgr && pAMgr->get_checksharps()) {\r
746                             char * pos;\r
747                             if (utf8) {\r
748                                 pos = strstr((*slst)[j], "脽");\r
749                                 while (pos) {\r
750                                     *pos = 'S';\r
751                                     *(pos+1) = 'S';\r
752                                     pos = strstr(pos+2, "脽");\r
753                                 }\r
754                             } else {\r
755                                 pos = strchr((*slst)[j], '?');\r
756                                 while (pos) {\r
757                                     (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 2);\r
758                                     mystrrep((*slst)[j], "?", "SS");\r
759                                     pos = strchr((*slst)[j], '?');\r
760                                 }\r
761                             }\r
762                         }\r
763                      }\r
764                      break;\r
765                    }\r
766   }\r
767 \r
768   // LANG_hu section: replace '-' with ' ' in Hungarian\r
769   if (langnum == LANG_hu) {\r
770       for (int j=0; j < ns; j++) {\r
771           char * pos = strchr((*slst)[j],'-');\r
772           if (pos) {\r
773               int info;\r
774               char w[MAXWORDUTF8LEN];\r
775               *pos = '\0';\r
776               strcpy(w, (*slst)[j]);\r
777               strcat(w, pos + 1);\r
778               spell(w, &info, NULL);\r
779               if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {\r
780                   *pos = ' ';\r
781               } else *pos = '-';\r
782           }\r
783       }\r
784   }\r
785   // END OF LANG_hu section\r
786 \r
787   // try ngram approach since found nothing\r
788   if ((ns == 0) && pAMgr && (pAMgr->get_maxngramsugs() != 0)) {\r
789       ngramsugs = 1;\r
790       switch(captype) {\r
791           case NOCAP: {\r
792               ns = pSMgr->ngsuggest(*slst, cw, pHMgr);\r
793               break;\r
794           }\r
795           case HUHCAP: {\r
796               memcpy(wspace,cw,(wl+1));\r
797               mkallsmall2(wspace, unicw, nc);\r
798               ns = pSMgr->ngsuggest(*slst, wspace, pHMgr);\r
799               break;\r
800           }\r
801           case INITCAP: { \r
802               capwords = 1;\r
803               memcpy(wspace,cw,(wl+1));\r
804               mkallsmall2(wspace, unicw, nc);\r
805               ns = pSMgr->ngsuggest(*slst, wspace, pHMgr);\r
806               break;\r
807           }\r
808           case ALLCAP: {\r
809               memcpy(wspace,cw,(wl+1));\r
810               mkallsmall2(wspace, unicw, nc);\r
811               ns = pSMgr->ngsuggest(*slst, wspace, pHMgr);\r
812               for (int j=0; j < ns; j++) \r
813                   mkallcap((*slst)[j]);\r
814               break;\r
815          }\r
816       }\r
817   }\r
818 \r
819   // word reversing wrapper for complex prefixes\r
820   if (complexprefixes) {\r
821     for (int j = 0; j < ns; j++) {\r
822       if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);\r
823     }\r
824   }\r
825 \r
826   // capitalize\r
827   if (capwords) for (int j=0; j < ns; j++) {\r
828       mkinitcap((*slst)[j]);\r
829   }\r
830 \r
831   // expand suggestions with dot(s)\r
832   if (abbv && pAMgr && pAMgr->get_sugswithdots()) {\r
833     for (int j = 0; j < ns; j++) {\r
834       (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);\r
835       strcat((*slst)[j], word + strlen(word) - abbv);\r
836     }\r
837   }\r
838 \r
839   // suggest keepcase\r
840   if (pAMgr->get_keepcase()) {\r
841   switch (captype) {\r
842     case INITCAP:\r
843     case ALLCAP: {\r
844       int l = 0;\r
845       for (int j=0; j < ns; j++) {\r
846         if (!spell((*slst)[j])) {\r
847           char s[MAXSWUTF8L];\r
848           w_char w[MAXSWL];\r
849           int len;\r
850           if (utf8) {\r
851             len = u8_u16(w, MAXSWL, (*slst)[j]);\r
852           } else {\r
853             strcpy(s, (*slst)[j]);\r
854             len = strlen(s);\r
855           }\r
856           mkallsmall2(s, w, len);\r
857           free((*slst)[j]);          \r
858           if (spell(s)) {\r
859             (*slst)[l] = mystrdup(s);\r
860             l++;\r
861           } else {\r
862             mkinitcap2(s, w, len);\r
863             if (spell(s)) {\r
864               (*slst)[l] = mystrdup(s);\r
865               l++;\r
866             }\r
867           }\r
868         } else {\r
869           (*slst)[l] = (*slst)[j];\r
870           l++;\r
871         }    \r
872       }\r
873       ns = l;\r
874     }\r
875   }\r
876   }\r
877 \r
878   // remove duplications\r
879   int l = 0;\r
880   for (int j = 0; j < ns; j++) {\r
881     (*slst)[l] = (*slst)[j];\r
882     for (int k = 0; k < l; k++) {\r
883       if (strcmp((*slst)[k], (*slst)[j]) == 0) {\r
884         free((*slst)[j]);\r
885         l--;\r
886       }\r
887     }\r
888     l++;\r
889   }\r
890   return l;\r
891 }\r
892 \r
893 char * Hunspell::get_dic_encoding()\r
894 {\r
895   return encoding;\r
896 }\r
897 \r
898 #ifdef HUNSPELL_EXPERIMENTAL\r
899 // XXX need UTF-8 support\r
900 int Hunspell::suggest_auto(char*** slst, const char * word)\r
901 {\r
902   char cw[MAXWORDUTF8LEN + 4];\r
903   char wspace[MAXWORDUTF8LEN + 4];\r
904   if (! pSMgr) return 0;\r
905   int wl = strlen(word);\r
906   if (utf8) {\r
907     if (wl >= MAXWORDUTF8LEN) return 0;\r
908   } else {\r
909     if (wl >= MAXWORDLEN) return 0;\r
910   }\r
911   int captype = 0;\r
912   int abbv = 0;\r
913   wl = cleanword(cw, word, &captype, &abbv);\r
914   if (wl == 0) return 0;\r
915   int ns = 0;\r
916   *slst = NULL; // HU, nsug in pSMgr->suggest\r
917   \r
918   switch(captype) {\r
919      case NOCAP:   { \r
920                      ns = pSMgr->suggest_auto(slst, cw, ns);\r
921                      if (ns>0) break;\r
922                      break;\r
923                    }\r
924 \r
925      case INITCAP: { \r
926                      memcpy(wspace,cw,(wl+1));\r
927                      mkallsmall(wspace);\r
928                      ns = pSMgr->suggest_auto(slst, wspace, ns);\r
929                      for (int j=0; j < ns; j++)\r
930                        mkinitcap((*slst)[j]);\r
931                      ns = pSMgr->suggest_auto(slst, cw, ns);\r
932                      break;\r
933                      \r
934                    }\r
935 \r
936      case HUHCAP: { \r
937                      ns = pSMgr->suggest_auto(slst, cw, ns);\r
938                      if (ns == 0) {\r
939                         memcpy(wspace,cw,(wl+1));\r
940                         mkallsmall(wspace);\r
941                         ns = pSMgr->suggest_auto(slst, wspace, ns);\r
942                      }\r
943                      break;\r
944                    }\r
945 \r
946      case ALLCAP: { \r
947                      memcpy(wspace,cw,(wl+1));\r
948                      mkallsmall(wspace);\r
949                      ns = pSMgr->suggest_auto(slst, wspace, ns);\r
950 \r
951                      mkinitcap(wspace);\r
952                      ns = pSMgr->suggest_auto(slst, wspace, ns);\r
953 \r
954                      for (int j=0; j < ns; j++)\r
955                        mkallcap((*slst)[j]);\r
956                      break;\r
957                    }\r
958   }\r
959 \r
960   // word reversing wrapper for complex prefixes\r
961   if (complexprefixes) {\r
962     for (int j = 0; j < ns; j++) {\r
963       if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);\r
964     }\r
965   }\r
966 \r
967   // expand suggestions with dot(s)\r
968   if (abbv && pAMgr && pAMgr->get_sugswithdots()) {\r
969     for (int j = 0; j < ns; j++) {\r
970       (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);\r
971       strcat((*slst)[j], word + strlen(word) - abbv);\r
972     }\r
973   }\r
974 \r
975   // LANG_hu section: replace '-' with ' ' in Hungarian\r
976   if (langnum == LANG_hu) {\r
977       for (int j=0; j < ns; j++) {\r
978           char * pos = strchr((*slst)[j],'-');\r
979           if (pos) {\r
980               int info;\r
981               char w[MAXWORDUTF8LEN];\r
982               *pos = '\0';\r
983               strcpy(w, (*slst)[j]);\r
984               strcat(w, pos + 1);\r
985               spell(w, &info, NULL);\r
986               if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {\r
987                   *pos = ' ';\r
988               } else *pos = '-';\r
989           }\r
990       }\r
991   }\r
992   // END OF LANG_hu section  \r
993   return ns;\r
994 }\r
995 \r
996 // XXX need UTF-8 support\r
997 int Hunspell::stem(char*** slst, const char * word)\r
998 {\r
999   char cw[MAXWORDUTF8LEN + 4];\r
1000   char wspace[MAXWORDUTF8LEN + 4];\r
1001   if (! pSMgr) return 0;\r
1002   int wl = strlen(word);\r
1003   if (utf8) {\r
1004     if (wl >= MAXWORDUTF8LEN) return 0;\r
1005   } else {\r
1006     if (wl >= MAXWORDLEN) return 0;\r
1007   }\r
1008   int captype = 0;\r
1009   int abbv = 0;\r
1010   wl = cleanword(cw, word, &captype, &abbv);\r
1011   if (wl == 0) return 0;\r
1012   \r
1013   int ns = 0;\r
1014 \r
1015   *slst = NULL; // HU, nsug in pSMgr->suggest\r
1016   \r
1017   switch(captype) {\r
1018      case HUHCAP:\r
1019      case NOCAP:   { \r
1020                      ns = pSMgr->suggest_stems(slst, cw, ns);\r
1021 \r
1022                      if ((abbv) && (ns == 0)) {\r
1023                          memcpy(wspace,cw,wl);\r
1024                          *(wspace+wl) = '.';\r
1025                          *(wspace+wl+1) = '\0';\r
1026                          ns = pSMgr->suggest_stems(slst, wspace, ns);\r
1027                      }\r
1028 \r
1029                      break;\r
1030                    }\r
1031 \r
1032      case INITCAP: { \r
1033 \r
1034                      ns = pSMgr->suggest_stems(slst, cw, ns);\r
1035 \r
1036                      if (ns == 0) {\r
1037                         memcpy(wspace,cw,(wl+1));\r
1038                         mkallsmall(wspace);\r
1039                         ns = pSMgr->suggest_stems(slst, wspace, ns);\r
1040 \r
1041                      }\r
1042 \r
1043                      if ((abbv) && (ns == 0)) {\r
1044                          memcpy(wspace,cw,wl);\r
1045                          mkallsmall(wspace);\r
1046                          *(wspace+wl) = '.';\r
1047                          *(wspace+wl+1) = '\0';\r
1048                          ns = pSMgr->suggest_stems(slst, wspace, ns);\r
1049                      }\r
1050                      \r
1051                      break;\r
1052                      \r
1053                    }\r
1054 \r
1055      case ALLCAP: { \r
1056                      ns = pSMgr->suggest_stems(slst, cw, ns);\r
1057                      if (ns != 0) break;\r
1058                      \r
1059                      memcpy(wspace,cw,(wl+1));\r
1060                      mkallsmall(wspace);\r
1061                      ns = pSMgr->suggest_stems(slst, wspace, ns);\r
1062 \r
1063                      if (ns == 0) {\r
1064                          mkinitcap(wspace);\r
1065                          ns = pSMgr->suggest_stems(slst, wspace, ns);\r
1066                      }\r
1067 \r
1068                      if ((abbv) && (ns == 0)) {\r
1069                          memcpy(wspace,cw,wl);\r
1070                          mkallsmall(wspace);\r
1071                          *(wspace+wl) = '.';\r
1072                          *(wspace+wl+1) = '\0';\r
1073                          ns = pSMgr->suggest_stems(slst, wspace, ns);\r
1074                      }\r
1075 \r
1076 \r
1077                      break;\r
1078                    }\r
1079   }\r
1080   \r
1081   return ns;\r
1082 }\r
1083 \r
1084 int Hunspell::suggest_pos_stems(char*** slst, const char * word)\r
1085 {\r
1086   char cw[MAXWORDUTF8LEN + 4];\r
1087   char wspace[MAXWORDUTF8LEN + 4];\r
1088   if (! pSMgr) return 0;\r
1089   int wl = strlen(word);\r
1090   if (utf8) {\r
1091     if (wl >= MAXWORDUTF8LEN) return 0;\r
1092   } else {\r
1093     if (wl >= MAXWORDLEN) return 0;\r
1094   }\r
1095   int captype = 0;\r
1096   int abbv = 0;\r
1097   wl = cleanword(cw, word, &captype, &abbv);\r
1098   if (wl == 0) return 0;\r
1099   \r
1100   int ns = 0; // ns=0 = normalized input\r
1101 \r
1102   *slst = NULL; // HU, nsug in pSMgr->suggest\r
1103   \r
1104   switch(captype) {\r
1105      case HUHCAP:\r
1106      case NOCAP:   { \r
1107                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);\r
1108 \r
1109                      if ((abbv) && (ns == 0)) {\r
1110                          memcpy(wspace,cw,wl);\r
1111                          *(wspace+wl) = '.';\r
1112                          *(wspace+wl+1) = '\0';\r
1113                          ns = pSMgr->suggest_pos_stems(slst, wspace, ns);\r
1114                      }\r
1115 \r
1116                      break;\r
1117                    }\r
1118 \r
1119      case INITCAP: { \r
1120 \r
1121                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);\r
1122 \r
1123                      if (ns == 0 || ((*slst)[0][0] == '#')) {\r
1124                         memcpy(wspace,cw,(wl+1));\r
1125                         mkallsmall(wspace);\r
1126                         ns = pSMgr->suggest_pos_stems(slst, wspace, ns);\r
1127                      }\r
1128                      \r
1129                      break;\r
1130                      \r
1131                    }\r
1132 \r
1133      case ALLCAP: { \r
1134                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);\r
1135                      if (ns != 0) break;\r
1136                      \r
1137                      memcpy(wspace,cw,(wl+1));\r
1138                      mkallsmall(wspace);\r
1139                      ns = pSMgr->suggest_pos_stems(slst, wspace, ns);\r
1140 \r
1141                      if (ns == 0) {\r
1142                          mkinitcap(wspace);\r
1143                          ns = pSMgr->suggest_pos_stems(slst, wspace, ns);\r
1144                      }\r
1145                      break;\r
1146                    }\r
1147   }\r
1148 \r
1149   return ns;\r
1150 }\r
1151 #endif // END OF HUNSPELL_EXPERIMENTAL CODE\r
1152 \r
1153 const char * Hunspell::get_wordchars()\r
1154 {\r
1155   return pAMgr->get_wordchars();\r
1156 }\r
1157 \r
1158 unsigned short * Hunspell::get_wordchars_utf16(int * len)\r
1159 {\r
1160   return pAMgr->get_wordchars_utf16(len);\r
1161 }\r
1162 \r
1163 void Hunspell::mkinitcap(char * p)\r
1164 {\r
1165   if (!utf8) {\r
1166     if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;\r
1167   } else {\r
1168       int len;\r
1169       w_char u[MAXWORDLEN];\r
1170       len = u8_u16(u, MAXWORDLEN, p);\r
1171       unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);\r
1172       u[0].h = (unsigned char) (i >> 8);\r
1173       u[0].l = (unsigned char) (i & 0x00FF);\r
1174       u16_u8(p, MAXWORDUTF8LEN, u, len);\r
1175   }\r
1176 }\r
1177 \r
1178 int Hunspell::mkinitcap2(char * p, w_char * u, int nc)\r
1179 {\r
1180   if (!utf8) {\r
1181     if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;\r
1182   } else if (nc > 0) {\r
1183       unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);\r
1184       u[0].h = (unsigned char) (i >> 8);\r
1185       u[0].l = (unsigned char) (i & 0x00FF);\r
1186       u16_u8(p, MAXWORDUTF8LEN, u, nc);\r
1187       return strlen(p);\r
1188   }\r
1189   return nc;\r
1190 }\r
1191 \r
1192 int Hunspell::mkinitsmall2(char * p, w_char * u, int nc)\r
1193 {\r
1194   if (!utf8) {\r
1195     if (*p != '\0') *p = csconv[((unsigned char)*p)].clower;\r
1196   } else if (nc > 0) {\r
1197       unsigned short i = unicodetolower((u[0].h << 8) + u[0].l, langnum);\r
1198       u[0].h = (unsigned char) (i >> 8);\r
1199       u[0].l = (unsigned char) (i & 0x00FF);\r
1200       u16_u8(p, MAXWORDUTF8LEN, u, nc);\r
1201       return strlen(p);\r
1202   }\r
1203   return nc;\r
1204 }\r
1205 \r
1206 int Hunspell::put_word(const char * word)\r
1207 {\r
1208     if (pHMgr) {\r
1209         return pHMgr->put_word(word, strlen(word), NULL);\r
1210     }\r
1211     return 0;\r
1212 }\r
1213 \r
1214 int Hunspell::put_word_pattern(const char * word, const char * pattern)\r
1215 {\r
1216     if (pHMgr) {\r
1217         return pHMgr->put_word_pattern(word, strlen(word), pattern);\r
1218     }\r
1219     return 0;\r
1220 }\r
1221 \r
1222 const char * Hunspell::get_version()\r
1223 {\r
1224   return pAMgr->get_version();\r
1225 }\r
1226 \r
1227 struct cs_info * Hunspell::get_csconv()\r
1228 {\r
1229   return csconv;\r
1230 }\r
1231 \r
1232 #ifdef HUNSPELL_EXPERIMENTAL\r
1233 // XXX need UTF-8 support\r
1234 char * Hunspell::morph(const char * word)\r
1235 {\r
1236   char cw[MAXWORDUTF8LEN + 4];\r
1237   char wspace[MAXWORDUTF8LEN + 4];\r
1238   if (! pSMgr) return 0;\r
1239   int wl = strlen(word);\r
1240   if (utf8) {\r
1241     if (wl >= MAXWORDUTF8LEN) return 0;\r
1242   } else {\r
1243     if (wl >= MAXWORDLEN) return 0;\r
1244   }\r
1245   int captype = 0;\r
1246   int abbv = 0;\r
1247   wl = cleanword(cw, word, &captype, &abbv);\r
1248   if (wl == 0) {\r
1249       if (abbv) {\r
1250           for (wl = 0; wl < abbv; wl++) cw[wl] = '.';\r
1251           cw[wl] = '\0';\r
1252           abbv = 0;\r
1253       } else return 0;\r
1254   }\r
1255 \r
1256   char result[MAXLNLEN];\r
1257   char * st = NULL;\r
1258   \r
1259   *result = '\0';\r
1260 \r
1261   int n = 0;\r
1262   int n2 = 0;\r
1263   int n3 = 0;\r
1264 \r
1265   // test numbers\r
1266   // LANG_hu section: set dash information for suggestions\r
1267   if (langnum == LANG_hu) {\r
1268   while ((n < wl) && \r
1269         (((cw[n] <= '9') && (cw[n] >= '0')) || (((cw[n] == '.') || (cw[n] == ',')) && (n > 0)))) {\r
1270         n++;\r
1271         if ((cw[n] == '.') || (cw[n] == ',')) {\r
1272                 if (((n2 == 0) && (n > 3)) || \r
1273                         ((n2 > 0) && ((cw[n-1] == '.') || (cw[n-1] == ',')))) break;\r
1274                 n2++;\r
1275                 n3 = n;\r
1276         }\r
1277   }\r
1278 \r
1279   if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return NULL;\r
1280   if ((n == wl) || ((n>0) && ((cw[n]=='%') || (cw[n]=='?)) && checkword(cw+n, NULL, NULL))) {\r
1281         strcat(result, cw);\r
1282         result[n - 1] = '\0';\r
1283         if (n == wl) {\r
1284                 st = pSMgr->suggest_morph(cw + n - 1);\r
1285                 if (st) {\r
1286                         strcat(result, st);\r
1287                         free(st);\r
1288                 }\r
1289         } else {\r
1290                 char sign = cw[n];\r
1291                 cw[n] = '\0';\r
1292                 st = pSMgr->suggest_morph(cw + n - 1);\r
1293                 if (st) {\r
1294                         strcat(result, st);\r
1295                         free(st);\r
1296                 }\r
1297                 strcat(result, "+"); // XXX SPEC. MORPHCODE\r
1298                 cw[n] = sign;\r
1299                 st = pSMgr->suggest_morph(cw + n);\r
1300                 if (st) {\r
1301                         strcat(result, st);\r
1302                         free(st);\r
1303                 }\r
1304         }\r
1305         return mystrdup(result);\r
1306   }\r
1307   }\r
1308   // END OF LANG_hu section\r
1309   \r
1310   switch(captype) {\r
1311      case NOCAP:   { \r
1312                      st = pSMgr->suggest_morph(cw);\r
1313                      if (st) {\r
1314                         strcat(result, st);\r
1315                         free(st);\r
1316                      }\r
1317                                          if (abbv) {\r
1318                                         memcpy(wspace,cw,wl);\r
1319                          *(wspace+wl) = '.';\r
1320                          *(wspace+wl+1) = '\0';\r
1321                          st = pSMgr->suggest_morph(wspace);\r
1322                          if (st) {\r
1323                             if (*result) strcat(result, "\n");\r
1324                             strcat(result, st);\r
1325                             free(st);\r
1326                                                  }\r
1327                      }\r
1328                                          break;\r
1329                    }\r
1330      case INITCAP: { \r
1331                      memcpy(wspace,cw,(wl+1));\r
1332                      mkallsmall(wspace);\r
1333                      st = pSMgr->suggest_morph(wspace);\r
1334                      if (st) {\r
1335                         strcat(result, st);\r
1336                         free(st);\r
1337                      }                                   \r
1338                          st = pSMgr->suggest_morph(cw);\r
1339                      if (st) {\r
1340                         if (*result) strcat(result, "\n");\r
1341                         strcat(result, st);\r
1342                         free(st);\r
1343                      }\r
1344                                          if (abbv) {\r
1345                                          memcpy(wspace,cw,wl);\r
1346                          *(wspace+wl) = '.';\r
1347                          *(wspace+wl+1) = '\0';\r
1348                          mkallsmall(wspace);\r
1349                          st = pSMgr->suggest_morph(wspace);\r
1350                          if (st) {\r
1351                             if (*result) strcat(result, "\n");\r
1352                             strcat(result, st);\r
1353                             free(st);\r
1354                                                  }\r
1355                          mkinitcap(wspace);\r
1356                          st = pSMgr->suggest_morph(wspace);\r
1357                          if (st) {\r
1358                             if (*result) strcat(result, "\n");\r
1359                             strcat(result, st);\r
1360                             free(st);\r
1361                                                  }\r
1362                      }\r
1363                      break;\r
1364                    }\r
1365      case HUHCAP: { \r
1366                      st = pSMgr->suggest_morph(cw);\r
1367                      if (st) {\r
1368                         strcat(result, st);\r
1369                         free(st);\r
1370                      }\r
1371 #if 0\r
1372                      memcpy(wspace,cw,(wl+1));\r
1373                      mkallsmall(wspace);\r
1374                      st = pSMgr->suggest_morph(wspace);\r
1375                      if (st) {\r
1376                         if (*result) strcat(result, "\n");\r
1377                         strcat(result, st);\r
1378                         free(st);\r
1379                      }\r
1380 #endif\r
1381                      break;\r
1382                  }\r
1383      case ALLCAP: { \r
1384                      memcpy(wspace,cw,(wl+1));\r
1385                      st = pSMgr->suggest_morph(wspace);\r
1386                      if (st) {\r
1387                         strcat(result, st);\r
1388                         free(st);\r
1389                      }               \r
1390                      mkallsmall(wspace);\r
1391                      st = pSMgr->suggest_morph(wspace);\r
1392                      if (st) {\r
1393                         if (*result) strcat(result, "\n");\r
1394                         strcat(result, st);\r
1395                         free(st);\r
1396                      }\r
1397                              mkinitcap(wspace);\r
1398                              st = pSMgr->suggest_morph(wspace);\r
1399                      if (st) {\r
1400                         if (*result) strcat(result, "\n");\r
1401                         strcat(result, st);\r
1402                         free(st);\r
1403                      }\r
1404                                          if (abbv) {\r
1405                         memcpy(wspace,cw,(wl+1));\r
1406                         *(wspace+wl) = '.';\r
1407                         *(wspace+wl+1) = '\0';\r
1408                         if (*result) strcat(result, "\n");\r
1409                         st = pSMgr->suggest_morph(wspace);\r
1410                         if (st) {\r
1411                                 strcat(result, st);\r
1412                                 free(st);\r
1413                         }                    \r
1414                         mkallsmall(wspace);\r
1415                         st = pSMgr->suggest_morph(wspace);\r
1416                         if (st) {\r
1417                           if (*result) strcat(result, "\n");\r
1418                           strcat(result, st);\r
1419                           free(st);\r
1420                         }\r
1421                                 mkinitcap(wspace);\r
1422                                 st = pSMgr->suggest_morph(wspace);\r
1423                         if (st) {\r
1424                           if (*result) strcat(result, "\n");\r
1425                           strcat(result, st);\r
1426                           free(st);\r
1427                         }\r
1428                                          }\r
1429                      break;\r
1430                    }\r
1431   }\r
1432 \r
1433   if (result && (*result)) {\r
1434     // word reversing wrapper for complex prefixes\r
1435     if (complexprefixes) {\r
1436       if (utf8) reverseword_utf(result); else reverseword(result);\r
1437     }\r
1438     return mystrdup(result);\r
1439   }\r
1440 \r
1441   // compound word with dash (HU) I18n\r
1442   char * dash = NULL;\r
1443   int nresult = 0;\r
1444   // LANG_hu section: set dash information for suggestions\r
1445   if (langnum == LANG_hu) dash = (char *) strchr(cw,'-');\r
1446   if ((langnum == LANG_hu) && dash) {\r
1447       *dash='\0';      \r
1448       // examine 2 sides of the dash\r
1449       if (dash[1] == '\0') { // base word ending with dash\r
1450         if (spell(cw)) return pSMgr->suggest_morph(cw);\r
1451       } else if ((dash[1] == 'e') && (dash[2] == '\0')) { // XXX (HU) -e hat.\r
1452         if (spell(cw) && (spell("-e"))) {\r
1453                         st = pSMgr->suggest_morph(cw);\r
1454                         if (st) {\r
1455                                 strcat(result, st);\r
1456                                 free(st);\r
1457                         }\r
1458                         strcat(result,"+"); // XXX spec. separator in MORPHCODE\r
1459                         st = pSMgr->suggest_morph("-e");\r
1460                         if (st) {\r
1461                                 strcat(result, st);\r
1462                                 free(st);\r
1463                         }\r
1464                         return mystrdup(result);\r
1465                 }\r
1466       } else {\r
1467       // first word ending with dash: word- XXX ???\r
1468         char r2 = *(dash + 1);\r
1469         dash[0]='-';\r
1470         dash[1]='\0';\r
1471         nresult = spell(cw);\r
1472         dash[1] = r2;\r
1473         dash[0]='\0';\r
1474         if (nresult && spell(dash+1) && ((strlen(dash+1) > 1) ||\r
1475                 ((dash[1] > '0') && (dash[1] < '9')))) {\r
1476                             st = morph(cw);\r
1477                             if (st) {\r
1478                                 strcat(result, st);\r
1479                                     free(st);\r
1480                                 strcat(result,"+"); // XXX spec. separator in MORPHCODE\r
1481                             }\r
1482                             st = morph(dash+1);\r
1483                             if (st) {\r
1484                                     strcat(result, st);\r
1485                                     free(st);\r
1486                             }\r
1487                             return mystrdup(result);                    \r
1488                         }\r
1489       }\r
1490       // affixed number in correct word\r
1491      if (nresult && (dash > cw) && (((*(dash-1)<='9') && \r
1492                         (*(dash-1)>='0')) || (*(dash-1)=='.'))) {\r
1493          *dash='-';\r
1494          n = 1;\r
1495          if (*(dash - n) == '.') n++;\r
1496          // search first not a number character to left from dash\r
1497          while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {\r
1498             n++;\r
1499          }\r
1500          if ((dash - n) < cw) n--;\r
1501          // numbers: valami1000000-hoz\r
1502          // examine 100000-hoz, 10000-hoz 1000-hoz, 10-hoz,\r
1503          // 56-hoz, 6-hoz\r
1504          for(; n >= 1; n--) {\r
1505             if ((*(dash - n) >= '0') && (*(dash - n) <= '9') && checkword(dash - n, NULL, NULL)) {\r
1506                     strcat(result, cw);\r
1507                     result[dash - cw - n] = '\0';\r
1508                         st = pSMgr->suggest_morph(dash - n);\r
1509                         if (st) {\r
1510                         strcat(result, st);\r
1511                                 free(st);\r
1512                         }\r
1513                     return mystrdup(result);                    \r
1514             }\r
1515          }\r
1516      }\r
1517   }\r
1518   return NULL;\r
1519 }\r
1520 \r
1521 // XXX need UTF-8 support\r
1522 char * Hunspell::morph_with_correction(const char * word)\r
1523 {\r
1524   char cw[MAXWORDUTF8LEN + 4];\r
1525   char wspace[MAXWORDUTF8LEN + 4];\r
1526   if (! pSMgr) return 0;\r
1527   int wl = strlen(word);\r
1528   if (utf8) {\r
1529     if (wl >= MAXWORDUTF8LEN) return 0;\r
1530   } else {\r
1531     if (wl >= MAXWORDLEN) return 0;\r
1532   }\r
1533   int captype = 0;\r
1534   int abbv = 0;\r
1535   wl = cleanword(cw, word, &captype, &abbv);\r
1536   if (wl == 0) return 0;\r
1537 \r
1538   char result[MAXLNLEN];\r
1539   char * st = NULL;\r
1540   \r
1541   *result = '\0';\r
1542   \r
1543   \r
1544   switch(captype) {\r
1545      case NOCAP:   { \r
1546                      st = pSMgr->suggest_morph_for_spelling_error(cw);\r
1547                      if (st) {\r
1548                         strcat(result, st);\r
1549                         free(st);\r
1550                      }\r
1551                                          if (abbv) {\r
1552                                         memcpy(wspace,cw,wl);\r
1553                          *(wspace+wl) = '.';\r
1554                          *(wspace+wl+1) = '\0';\r
1555                          st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1556                          if (st) {\r
1557                             if (*result) strcat(result, "\n");\r
1558                             strcat(result, st);\r
1559                             free(st);\r
1560                                                  }\r
1561                      }\r
1562                                          break;\r
1563                    }\r
1564      case INITCAP: { \r
1565                      memcpy(wspace,cw,(wl+1));\r
1566                      mkallsmall(wspace);\r
1567                      st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1568                      if (st) {\r
1569                         strcat(result, st);\r
1570                         free(st);\r
1571                      }                                   \r
1572                          st = pSMgr->suggest_morph_for_spelling_error(cw);\r
1573                      if (st) {\r
1574                         if (*result) strcat(result, "\n");\r
1575                         strcat(result, st);\r
1576                         free(st);\r
1577                      }\r
1578                                          if (abbv) {\r
1579                                          memcpy(wspace,cw,wl);\r
1580                          *(wspace+wl) = '.';\r
1581                          *(wspace+wl+1) = '\0';\r
1582                          mkallsmall(wspace);\r
1583                          st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1584                          if (st) {\r
1585                             if (*result) strcat(result, "\n");\r
1586                             strcat(result, st);\r
1587                             free(st);\r
1588                                                  }\r
1589                          mkinitcap(wspace);\r
1590                          st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1591                          if (st) {\r
1592                             if (*result) strcat(result, "\n");\r
1593                             strcat(result, st);\r
1594                             free(st);\r
1595                                                  }\r
1596                      }\r
1597                      break;\r
1598                    }\r
1599      case HUHCAP: { \r
1600                      st = pSMgr->suggest_morph_for_spelling_error(cw);\r
1601                      if (st) {\r
1602                         strcat(result, st);\r
1603                         free(st);\r
1604                      }\r
1605                      memcpy(wspace,cw,(wl+1));\r
1606                      mkallsmall(wspace);\r
1607                      st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1608                      if (st) {\r
1609                         if (*result) strcat(result, "\n");\r
1610                         strcat(result, st);\r
1611                         free(st);\r
1612                      }               \r
1613                      break;\r
1614                  }\r
1615      case ALLCAP: { \r
1616                      memcpy(wspace,cw,(wl+1));\r
1617                      st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1618                      if (st) {\r
1619                         strcat(result, st);\r
1620                         free(st);\r
1621                      }               \r
1622                      mkallsmall(wspace);\r
1623                      st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1624                      if (st) {\r
1625                         if (*result) strcat(result, "\n");\r
1626                         strcat(result, st);\r
1627                         free(st);\r
1628                      }\r
1629                              mkinitcap(wspace);\r
1630                              st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1631                      if (st) {\r
1632                         if (*result) strcat(result, "\n");\r
1633                         strcat(result, st);\r
1634                         free(st);\r
1635                      }\r
1636                                          if (abbv) {\r
1637                         memcpy(wspace,cw,(wl+1));\r
1638                         *(wspace+wl) = '.';\r
1639                         *(wspace+wl+1) = '\0';\r
1640                         if (*result) strcat(result, "\n");\r
1641                         st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1642                         if (st) {\r
1643                                 strcat(result, st);\r
1644                                 free(st);\r
1645                         }                    \r
1646                         mkallsmall(wspace);\r
1647                         st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1648                         if (st) {\r
1649                           if (*result) strcat(result, "\n");\r
1650                           strcat(result, st);\r
1651                           free(st);\r
1652                         }\r
1653                                 mkinitcap(wspace);\r
1654                                 st = pSMgr->suggest_morph_for_spelling_error(wspace);\r
1655                         if (st) {\r
1656                           if (*result) strcat(result, "\n");\r
1657                           strcat(result, st);\r
1658                           free(st);\r
1659                         }\r
1660                                          }\r
1661                      break;\r
1662                    }\r
1663   }\r
1664 \r
1665   if (result) return mystrdup(result);\r
1666   return NULL;\r
1667 }\r
1668 \r
1669 /* analyze word\r
1670  * return line count \r
1671  * XXX need a better data structure for morphological analysis */\r
1672 int Hunspell::analyze(char ***out, const char *word) {\r
1673   int  n = 0;\r
1674   if (!word) return 0;\r
1675   char * m = morph(word);\r
1676   if(!m) return 0;\r
1677   if (!out) return line_tok(m, out);\r
1678 \r
1679   // without memory allocation\r
1680   /* BUG missing buffer size checking */\r
1681   int i, p;\r
1682   for(p = 0, i = 0; m[i]; i++) {\r
1683      if(m[i] == '\n' || !m[i+1]) {\r
1684        n++;\r
1685        strncpy((*out)[n++], m + p, i - p + 1);\r
1686        if (m[i] == '\n') (*out)[n++][i - p] = '\0';\r
1687        if(!m[i+1]) break;\r
1688        p = i + 1;        \r
1689      }\r
1690   }\r
1691   free(m);\r
1692   return n;\r
1693 }\r
1694 \r
1695 #endif // END OF HUNSPELL_EXPERIMENTAL CODE\r
1696 \r
1697 Hunhandle *Hunspell_create(const char * affpath, const char * dpath)\r
1698 {\r
1699         return (Hunhandle*)(new Hunspell(affpath, dpath));\r
1700 }\r
1701 \r
1702 void Hunspell_destroy(Hunhandle *pHunspell)\r
1703 {\r
1704         delete (Hunspell*)(pHunspell);\r
1705 }\r
1706 \r
1707 int Hunspell_spell(Hunhandle *pHunspell, const char *word)\r
1708 {\r
1709         return ((Hunspell*)pHunspell)->spell(word);\r
1710 }\r
1711 \r
1712 char *Hunspell_get_dic_encoding(Hunhandle *pHunspell)\r
1713 {\r
1714         return ((Hunspell*)pHunspell)->get_dic_encoding();\r
1715 }\r
1716 \r
1717 int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word)\r
1718 {\r
1719         return ((Hunspell*)pHunspell)->suggest(slst, word);\r
1720 }\r
1721 \r