Pick Ref: Change [...] button in log dialog in a clickable ref name
[TortoiseGit.git] / ext / hunspell / hunspell.cxx
blobe722b75de345c99e906e8a26972e29a551800446
1 #include "license.hunspell"
2 #include "license.myspell"
4 #ifndef MOZILLA_CLIENT
5 #include <cstdlib>
6 #include <cstring>
7 #include <cstdio>
8 #else
9 #include <stdlib.h>
10 #include <string.h>
11 #include <stdio.h>
12 #endif
14 #include "hunspell.hxx"
15 #include "hunspell.h"
17 #ifndef MOZILLA_CLIENT
18 #ifndef W32
19 using namespace std;
20 #endif
21 #endif
23 Hunspell::Hunspell(const char * affpath, const char * dpath)
25 encoding = NULL;
26 csconv = NULL;
27 utf8 = 0;
28 complexprefixes = 0;
30 /* first set up the hash manager */
31 pHMgr = new HashMgr(dpath, affpath);
33 /* next set up the affix manager */
34 /* it needs access to the hash manager lookup methods */
35 pAMgr = new AffixMgr(affpath,pHMgr);
37 /* get the preferred try string and the dictionary */
38 /* encoding from the Affix Manager for that dictionary */
39 char * try_string = pAMgr->get_try_string();
40 encoding = pAMgr->get_encoding();
41 csconv = get_current_cs(encoding);
42 langnum = pAMgr->get_langnum();
43 utf8 = pAMgr->get_utf8();
44 complexprefixes = pAMgr->get_complexprefixes();
45 wordbreak = pAMgr->get_breaktable();
47 /* and finally set up the suggestion manager */
48 pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr);
49 if (try_string) free(try_string);
53 Hunspell::~Hunspell()
55 if (pSMgr) delete pSMgr;
56 if (pAMgr) delete pAMgr;
57 if (pHMgr) delete pHMgr;
58 pSMgr = NULL;
59 pAMgr = NULL;
60 pHMgr = NULL;
61 csconv= NULL;
62 if (encoding) free(encoding);
63 encoding = NULL;
67 // make a copy of src at destination while removing all leading
68 // blanks and removing any trailing periods after recording
69 // their presence with the abbreviation flag
70 // also since already going through character by character,
71 // set the capitalization type
72 // return the length of the "cleaned" (and UTF-8 encoded) word
74 int Hunspell::cleanword2(char * dest, const char * src,
75 w_char * dest_utf, int * nc, int * pcaptype, int * pabbrev)
77 unsigned char * p = (unsigned char *) dest;
78 const unsigned char * q = (const unsigned char * ) src;
79 int firstcap = 0;
81 // first skip over any leading blanks
82 while ((*q != '\0') && (*q == ' ')) q++;
84 // now strip off any trailing periods (recording their presence)
85 *pabbrev = 0;
86 int nl = strlen((const char *)q);
87 while ((nl > 0) && (*(q+nl-1)=='.')) {
88 nl--;
89 (*pabbrev)++;
92 // if no characters are left it can't be capitalized
93 if (nl <= 0) {
94 *pcaptype = NOCAP;
95 *p = '\0';
96 return 0;
99 // now determine the capitalization type of the first nl letters
100 int ncap = 0;
101 int nneutral = 0;
102 *nc = 0;
104 if (!utf8) {
105 while (nl > 0) {
106 (*nc)++;
107 if (csconv[(*q)].ccase) ncap++;
108 if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++;
109 *p++ = *q++;
110 nl--;
112 // remember to terminate the destination string
113 *p = '\0';
114 if (ncap) {
115 firstcap = csconv[(unsigned char)(*dest)].ccase;
117 } else {
118 unsigned short idx;
119 *nc = u8_u16(dest_utf, MAXWORDLEN, (const char *) q);
120 // don't check too long words
121 if (*nc >= MAXWORDLEN) return 0;
122 if (*nc == -1) { // big Unicode character (non BMP area)
123 *pcaptype = NOCAP;
124 strcpy((char *) p, (char *) q);
125 return strlen(dest);
127 *nc -= *pabbrev;
128 for (int i = 0; i < *nc; i++) {
129 idx = (dest_utf[i].h << 8) + dest_utf[i].l;
130 if (idx != unicodetolower(idx, langnum)) ncap++;
131 if (unicodetoupper(idx, langnum) == unicodetolower(idx, langnum)) nneutral++;
133 u16_u8(dest, MAXWORDUTF8LEN, dest_utf, *nc);
134 if (ncap) {
135 idx = (dest_utf[0].h << 8) + dest_utf[0].l;
136 firstcap = (idx != unicodetolower(idx, langnum));
140 // now finally set the captype
141 if (ncap == 0) {
142 *pcaptype = NOCAP;
143 } else if ((ncap == 1) && firstcap) {
144 *pcaptype = INITCAP;
145 } else if ((ncap == *nc) || ((ncap + nneutral) == *nc)) {
146 *pcaptype = ALLCAP;
147 } else if ((ncap > 1) && firstcap) {
148 *pcaptype = HUHINITCAP;
149 } else {
150 *pcaptype = HUHCAP;
152 return strlen(dest);
155 int Hunspell::cleanword(char * dest, const char * src,
156 int * pcaptype, int * pabbrev)
158 unsigned char * p = (unsigned char *) dest;
159 const unsigned char * q = (const unsigned char * ) src;
160 int firstcap = 0;
162 // first skip over any leading blanks
163 while ((*q != '\0') && (*q == ' ')) q++;
165 // now strip off any trailing periods (recording their presence)
166 *pabbrev = 0;
167 int nl = strlen((const char *)q);
168 while ((nl > 0) && (*(q+nl-1)=='.')) {
169 nl--;
170 (*pabbrev)++;
173 // if no characters are left it can't be capitalized
174 if (nl <= 0) {
175 *pcaptype = NOCAP;
176 *p = '\0';
177 return 0;
180 // now determine the capitalization type of the first nl letters
181 int ncap = 0;
182 int nneutral = 0;
183 int nc = 0;
185 if (!utf8) {
186 while (nl > 0) {
187 nc++;
188 if (csconv[(*q)].ccase) ncap++;
189 if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++;
190 *p++ = *q++;
191 nl--;
193 // remember to terminate the destination string
194 *p = '\0';
195 firstcap = csconv[(unsigned char)(*dest)].ccase;
196 } else {
197 unsigned short idx;
198 w_char t[MAXWORDLEN];
199 nc = u8_u16(t, MAXWORDLEN, src);
200 for (int i = 0; i < nc; i++) {
201 idx = (t[i].h << 8) + t[i].l;
202 if (idx != unicodetolower(idx, langnum)) ncap++;
203 if (unicodetoupper(idx, langnum) == unicodetolower(idx, langnum)) nneutral++;
205 u16_u8(dest, MAXWORDUTF8LEN, t, nc);
206 if (ncap) {
207 idx = (t[0].h << 8) + t[0].l;
208 firstcap = (idx != unicodetolower(idx, langnum));
212 // now finally set the captype
213 if (ncap == 0) {
214 *pcaptype = NOCAP;
215 } else if ((ncap == 1) && firstcap) {
216 *pcaptype = INITCAP;
217 } else if ((ncap == nc) || ((ncap + nneutral) == nc)){
218 *pcaptype = ALLCAP;
219 } else if ((ncap > 1) && firstcap) {
220 *pcaptype = HUHINITCAP;
221 } else {
222 *pcaptype = HUHCAP;
224 return strlen(dest);
228 void Hunspell::mkallcap(char * p)
230 if (utf8) {
231 w_char u[MAXWORDLEN];
232 int nc = u8_u16(u, MAXWORDLEN, p);
233 unsigned short idx;
234 for (int i = 0; i < nc; i++) {
235 idx = (u[i].h << 8) + u[i].l;
236 if (idx != unicodetoupper(idx, langnum)) {
237 u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);
238 u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);
241 u16_u8(p, MAXWORDUTF8LEN, u, nc);
242 } else {
243 while (*p != '\0') {
244 *p = csconv[((unsigned char) *p)].cupper;
245 p++;
250 int Hunspell::mkallcap2(char * p, w_char * u, int nc)
252 if (utf8) {
253 unsigned short idx;
254 for (int i = 0; i < nc; i++) {
255 idx = (u[i].h << 8) + u[i].l;
256 if (idx != unicodetoupper(idx, langnum)) {
257 u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);
258 u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);
261 u16_u8(p, MAXWORDUTF8LEN, u, nc);
262 return strlen(p);
263 } else {
264 while (*p != '\0') {
265 *p = csconv[((unsigned char) *p)].cupper;
266 p++;
269 return nc;
273 void Hunspell::mkallsmall(char * p)
275 while (*p != '\0') {
276 *p = csconv[((unsigned char) *p)].clower;
277 p++;
281 int Hunspell::mkallsmall2(char * p, w_char * u, int nc)
283 if (utf8) {
284 unsigned short idx;
285 for (int i = 0; i < nc; i++) {
286 idx = (u[i].h << 8) + u[i].l;
287 if (idx != unicodetolower(idx, langnum)) {
288 u[i].h = (unsigned char) (unicodetolower(idx, langnum) >> 8);
289 u[i].l = (unsigned char) (unicodetolower(idx, langnum) & 0x00FF);
292 u16_u8(p, MAXWORDUTF8LEN, u, nc);
293 return strlen(p);
294 } else {
295 while (*p != '\0') {
296 *p = csconv[((unsigned char) *p)].clower;
297 p++;
300 return nc;
303 // convert UTF-8 sharp S codes to latin 1
304 char * Hunspell::sharps_u8_l1(char * dest, char * source) {
305 char * p = dest;
306 *p = *source;
307 for (p++, source++; *(source - 1); p++, source++) {
308 *p = *source;
309 if (*source == '?') *--p = '?';
311 return dest;
314 // recursive search for right ss-?permutations
315 hentry * Hunspell::spellsharps(char * base, char * pos, int n,
316 int repnum, char * tmp, int * info, char **root) {
317 pos = strstr(pos, "ss");
318 if (pos && (n < MAXSHARPS)) {
319 *pos = '?';
320 *(pos + 1) = '?';
321 hentry * h = spellsharps(base, pos + 2, n + 1, repnum + 1, tmp, info, root);
322 if (h) return h;
323 *pos = 's';
324 *(pos + 1) = 's';
325 h = spellsharps(base, pos + 2, n + 1, repnum, tmp, info, root);
326 if (h) return h;
327 } else if (repnum > 0) {
328 if (utf8) return checkword(base, info, root);
329 return checkword(sharps_u8_l1(tmp, base), info, root);
331 return NULL;
334 int Hunspell::is_keepcase(const hentry * rv) {
335 return pAMgr && rv->astr && pAMgr->get_keepcase() &&
336 TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen);
339 /* check and insert a word to beginning of the suggestion array */
340 int Hunspell::insert_sug(char ***slst, char * word, int *ns) {
341 if (spell(word)) {
342 if (*ns == MAXSUGGESTION) {
343 (*ns)--;
344 free((*slst)[*ns]);
346 for (int k = *ns; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
347 (*slst)[0] = mystrdup(word);
348 (*ns)++;
350 return 0;
353 int Hunspell::spell(const char * word, int * info, char ** root)
355 struct hentry * rv=NULL;
356 // need larger vector. For example, Turkish capital letter I converted a
357 // 2-byte UTF-8 character (dotless i) by mkallsmall.
358 char cw[MAXWORDUTF8LEN + 4];
359 char wspace[MAXWORDUTF8LEN + 4];
360 w_char unicw[MAXWORDLEN + 1];
361 int nc = strlen(word);
362 int wl2 = 0;
363 if (utf8) {
364 if (nc >= MAXWORDUTF8LEN) return 0;
365 } else {
366 if (nc >= MAXWORDLEN) return 0;
368 int captype = 0;
369 int abbv = 0;
370 int wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
372 if (wl == 0) return 1;
374 if (info) *info = 0;
375 if (root) *root = NULL;
377 // allow numbers with dots and commas (but forbid double separators: "..", ",," etc.)
378 enum { NBEGIN, NNUM, NSEP };
379 int nstate = NBEGIN;
380 int i;
382 for (i = 0; (i < wl); i++) {
383 if ((cw[i] <= '9') && (cw[i] >= '0')) {
384 nstate = NNUM;
385 } else if ((cw[i] == ',') || (cw[i] == '.') || (cw[i] == '-')) {
386 if ((nstate == NSEP) || (i == 0)) break;
387 nstate = NSEP;
388 } else break;
390 if ((i == wl) && (nstate == NNUM)) return 1;
392 // LANG_hu section: number(s) + (percent or degree) with suffixes
393 if (langnum == LANG_hu) {
394 if ((nstate == NNUM) && ((cw[i] == '%') || (cw[i] == '?'))
395 && checkword(cw + i, info, root)) return 1;
397 // END of LANG_hu section
399 switch(captype) {
400 case HUHCAP:
401 case HUHINITCAP:
402 case NOCAP: {
403 rv = checkword(cw, info, root);
404 if ((abbv) && !(rv)) {
405 memcpy(wspace,cw,wl);
406 *(wspace+wl) = '.';
407 *(wspace+wl+1) = '\0';
408 rv = checkword(wspace, info, root);
410 break;
412 case ALLCAP: {
413 rv = checkword(cw, info, root);
414 if (rv) break;
415 if (abbv) {
416 memcpy(wspace,cw,wl);
417 *(wspace+wl) = '.';
418 *(wspace+wl+1) = '\0';
419 rv = checkword(wspace, info, root);
420 if (rv) break;
422 if (pAMgr && pAMgr->get_checksharps() && strstr(cw, "SS")) {
423 char tmpword[MAXWORDUTF8LEN];
424 wl = mkallsmall2(cw, unicw, nc);
425 memcpy(wspace,cw,(wl+1));
426 rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
427 if (!rv) {
428 wl2 = mkinitcap2(cw, unicw, nc);
429 rv = spellsharps(cw, cw, 0, 0, tmpword, info, root);
431 if ((abbv) && !(rv)) {
432 *(wspace+wl) = '.';
433 *(wspace+wl+1) = '\0';
434 rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
435 if (!rv) {
436 memcpy(wspace, cw, wl2);
437 *(wspace+wl2) = '.';
438 *(wspace+wl2+1) = '\0';
439 rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
442 if (rv) break;
445 case INITCAP: {
446 wl = mkallsmall2(cw, unicw, nc);
447 memcpy(wspace,cw,(wl+1));
448 rv = checkword(wspace, info, root);
449 if (!rv || (is_keepcase(rv) && !((captype == INITCAP) &&
450 // if CHECKSHARPS: KEEPCASE words with ?are allowed
451 // in INITCAP form, too.
452 pAMgr->get_checksharps() && ((utf8 && strstr(wspace, "脽")) ||
453 (!utf8 && strchr(wspace, '?')))))) {
454 wl2 = mkinitcap2(cw, unicw, nc);
455 rv = checkword(cw, info, root);
456 if (rv && (captype == ALLCAP) && is_keepcase(rv)) rv = NULL;
458 if (abbv && !rv) {
459 *(wspace+wl) = '.';
460 *(wspace+wl+1) = '\0';
461 rv = checkword(wspace, info, root);
462 if (!rv || is_keepcase(rv)) {
463 memcpy(wspace, cw, wl2);
464 *(wspace+wl2) = '.';
465 *(wspace+wl2+1) = '\0';
466 rv = checkword(wspace, info, root);
467 if (rv && ((captype == ALLCAP) && is_keepcase(rv))) rv = NULL;
470 break;
474 if (rv) return 1;
476 // recursive breaking at break points (not good for morphological analysis)
477 if (wordbreak) {
478 char * s;
479 char r;
480 for (int j = 0; j < pAMgr->get_numbreak(); j++) {
481 s=(char *) strstr(cw, wordbreak[j]);
482 if (s) {
483 r = *s;
484 *s = '\0';
485 // examine 2 sides of the break point
486 if (spell(cw) && spell(s + strlen(wordbreak[j]))) {
487 *s = r;
488 return 1;
490 *s = r;
495 // LANG_hu: compoundings with dashes and n-dashes XXX deprecated!
496 if (langnum == LANG_hu) {
497 int n;
498 // compound word with dash (HU) I18n
499 char * dash;
500 int result = 0;
501 // n-dash
502 dash = (char *) strstr(cw,"-");
503 if (dash && !wordbreak) {
504 *dash = '\0';
505 // examine 2 sides of the dash
506 if (spell(cw) && spell(dash + 3)) {
507 *dash = '?';
508 return 1;
510 *dash = '?';
512 dash = (char *) strchr(cw,'-');
513 if (dash) {
514 *dash='\0';
515 // examine 2 sides of the dash
516 if (dash[1] == '\0') { // base word ending with dash
517 if (spell(cw)) return 1;
518 } else {
519 // first word ending with dash: word-
520 char r2 = *(dash + 1);
521 dash[0]='-';
522 dash[1]='\0';
523 result = spell(cw);
524 dash[1] = r2;
525 dash[0]='\0';
526 if (result && spell(dash+1) && ((strlen(dash+1) > 1) || (dash[1] == 'e') ||
527 ((dash[1] > '0') && (dash[1] < '9')))) return 1;
529 // affixed number in correct word
530 if (result && (dash > cw) && (((*(dash-1)<='9') && (*(dash-1)>='0')) || (*(dash-1)>='.'))) {
531 *dash='-';
532 n = 1;
533 if (*(dash - n) == '.') n++;
534 // search first not a number character to left from dash
535 while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {
536 n++;
538 if ((dash - n) < cw) n--;
539 // numbers: deprecated
540 for(; n >= 1; n--) {
541 if ((*(dash - n) >= '0') && (*(dash - n) <= '9') &&
542 checkword(dash - n, info, root)) return 1;
547 return 0;
550 //int Hunspell::spell(const char * word) {
551 // return spell(word, NULL, NULL);
554 struct hentry * Hunspell::checkword(const char * w, int * info, char ** root)
556 struct hentry * he = NULL;
557 int len;
558 char w2[MAXWORDUTF8LEN];
559 const char * word;
561 char * ignoredchars = pAMgr->get_ignore();
562 if (ignoredchars != NULL) {
563 strcpy(w2, w);
564 if (utf8) {
565 int ignoredchars_utf16_len;
566 unsigned short * ignoredchars_utf16 = pAMgr->get_ignore_utf16(&ignoredchars_utf16_len);
567 remove_ignored_chars_utf(w2, ignoredchars_utf16, ignoredchars_utf16_len);
568 } else {
569 remove_ignored_chars(w2,ignoredchars);
571 word = w2;
572 } else word = w;
574 // word reversing wrapper for complex prefixes
575 if (complexprefixes) {
576 if (word != w2) {
577 strcpy(w2, word);
578 word = w2;
580 if (utf8) reverseword_utf(w2); else reverseword(w2);
583 // look word in hash table
584 if (pHMgr) he = pHMgr->lookup(word);
586 // check forbidden and onlyincompound words
587 if ((he) && (he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
588 info += SPELL_FORBIDDEN;
589 // LANG_hu section: set dash information for suggestions
590 if (langnum == LANG_hu) {
591 if (pAMgr->get_compoundflag() &&
592 TESTAFF(he->astr, pAMgr->get_compoundflag(), he->alen)) {
593 info += SPELL_COMPOUND;
596 return NULL;
599 // he = next not pseudoroot and not onlyincompound homonym or NULL
600 while (he && (he->astr) &&
601 ((pAMgr->get_pseudoroot() && TESTAFF(he->astr, pAMgr->get_pseudoroot(), he->alen)) ||
602 (pAMgr->get_onlyincompound() && TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen))
603 )) he = he->next_homonym;
605 // check with affixes
606 if (!he && pAMgr) {
607 // try stripping off affixes */
608 len = strlen(word);
609 he = pAMgr->affix_check(word, len, 0);
611 // check compound restriction
612 if (he && he->astr && pAMgr->get_onlyincompound() &&
613 TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) he = NULL;
615 if (he) {
616 if ((he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
617 info += SPELL_FORBIDDEN;
618 return NULL;
620 if (root) {
621 *root = mystrdup(he->word);
622 if (complexprefixes) {
623 if (utf8) reverseword_utf(*root); else reverseword(*root);
626 // try check compound word
627 } else if (pAMgr->get_compound()) {
628 he = pAMgr->compound_check(word, len,
629 0,0,100,0,NULL,0,NULL,NULL,0);
630 // LANG_hu section: `moving rule' with last dash
631 if ((!he) && (langnum == LANG_hu) && (word[len-1]=='-')) {
632 char * dup = mystrdup(word);
633 dup[len-1] = '\0';
634 he = pAMgr->compound_check(dup, len-1,
635 -5,0,100,0,NULL,1,NULL,NULL,0);
636 free(dup);
638 // end of LANG speficic region
639 if (he) {
640 if (root) {
641 *root = mystrdup(he->word);
642 if (complexprefixes) {
643 if (utf8) reverseword_utf(*root); else reverseword(*root);
646 if (info) *info += SPELL_COMPOUND;
652 return he;
655 int Hunspell::suggest(char*** slst, const char * word)
657 char cw[MAXWORDUTF8LEN + 4];
658 char wspace[MAXWORDUTF8LEN + 4];
659 if (! pSMgr) return 0;
660 w_char unicw[MAXWORDLEN + 1];
661 int nc = strlen(word);
662 if (utf8) {
663 if (nc >= MAXWORDUTF8LEN) return 0;
664 } else {
665 if (nc >= MAXWORDLEN) return 0;
667 int captype = 0;
668 int abbv = 0;
669 int wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
670 if (wl == 0) return 0;
671 int ns = 0;
672 *slst = NULL;
673 int capwords = 0;
674 int ngramsugs = 0;
676 switch(captype) {
677 case NOCAP: {
678 ns = pSMgr->suggest(slst, cw, ns);
679 break;
682 case INITCAP: {
683 capwords = 1;
684 ns = pSMgr->suggest(slst, cw, ns);
685 if (ns == -1) break;
686 memcpy(wspace,cw,(wl+1));
687 mkallsmall2(wspace, unicw, nc);
688 ns = pSMgr->suggest(slst, wspace, ns);
689 break;
691 case HUHINITCAP:
692 capwords = 1;
693 case HUHCAP: {
694 ns = pSMgr->suggest(slst, cw, ns);
695 if (ns != -1) {
696 int prevns;
697 if (captype == HUHINITCAP) {
698 // TheOpenOffice.org -> The OpenOffice.org
699 memcpy(wspace,cw,(wl+1));
700 mkinitsmall2(wspace, unicw, nc);
701 ns = pSMgr->suggest(slst, wspace, ns);
703 memcpy(wspace,cw,(wl+1));
704 mkallsmall2(wspace, unicw, nc);
705 insert_sug(slst, wspace, &ns);
706 prevns = ns;
707 ns = pSMgr->suggest(slst, wspace, ns);
708 if (captype == HUHINITCAP) {
709 mkinitcap2(wspace, unicw, nc);
710 insert_sug(slst, wspace, &ns);
711 ns = pSMgr->suggest(slst, wspace, ns);
713 // aNew -> "a New" (instead of "a new")
714 for (int j = prevns; j < ns; j++) {
715 char * space = strchr((*slst)[j],' ');
716 if (space) {
717 int slen = strlen(space + 1);
718 // different case after space (need capitalisation)
719 if ((slen < wl) && strcmp(cw + wl - slen, space + 1)) {
720 w_char w[MAXWORDLEN + 1];
721 int wc = 0;
722 char * r = (*slst)[j];
723 if (utf8) wc = u8_u16(w, MAXWORDLEN, space + 1);
724 mkinitcap2(space + 1, w, wc);
725 // set as first suggestion
726 for (int k = j; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
727 (*slst)[0] = r;
732 break;
735 case ALLCAP: {
736 memcpy(wspace, cw, (wl+1));
737 mkallsmall2(wspace, unicw, nc);
738 ns = pSMgr->suggest(slst, wspace, ns);
739 if (ns == -1) break;
740 if (pAMgr && pAMgr->get_keepcase()) insert_sug(slst, wspace, &ns);
741 mkinitcap2(wspace, unicw, nc);
742 ns = pSMgr->suggest(slst, wspace, ns);
743 for (int j=0; j < ns; j++) {
744 mkallcap((*slst)[j]);
745 if (pAMgr && pAMgr->get_checksharps()) {
746 char * pos;
747 if (utf8) {
748 pos = strstr((*slst)[j], "脽");
749 while (pos) {
750 *pos = 'S';
751 *(pos+1) = 'S';
752 pos = strstr(pos+2, "脽");
754 } else {
755 pos = strchr((*slst)[j], '?');
756 while (pos) {
757 (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 2);
758 mystrrep((*slst)[j], "?", "SS");
759 pos = strchr((*slst)[j], '?');
764 break;
768 // LANG_hu section: replace '-' with ' ' in Hungarian
769 if (langnum == LANG_hu) {
770 for (int j=0; j < ns; j++) {
771 char * pos = strchr((*slst)[j],'-');
772 if (pos) {
773 int info;
774 char w[MAXWORDUTF8LEN];
775 *pos = '\0';
776 strcpy(w, (*slst)[j]);
777 strcat(w, pos + 1);
778 spell(w, &info, NULL);
779 if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
780 *pos = ' ';
781 } else *pos = '-';
785 // END OF LANG_hu section
787 // try ngram approach since found nothing
788 if ((ns == 0) && pAMgr && (pAMgr->get_maxngramsugs() != 0)) {
789 ngramsugs = 1;
790 switch(captype) {
791 case NOCAP: {
792 ns = pSMgr->ngsuggest(*slst, cw, pHMgr);
793 break;
795 case HUHCAP: {
796 memcpy(wspace,cw,(wl+1));
797 mkallsmall2(wspace, unicw, nc);
798 ns = pSMgr->ngsuggest(*slst, wspace, pHMgr);
799 break;
801 case INITCAP: {
802 capwords = 1;
803 memcpy(wspace,cw,(wl+1));
804 mkallsmall2(wspace, unicw, nc);
805 ns = pSMgr->ngsuggest(*slst, wspace, pHMgr);
806 break;
808 case ALLCAP: {
809 memcpy(wspace,cw,(wl+1));
810 mkallsmall2(wspace, unicw, nc);
811 ns = pSMgr->ngsuggest(*slst, wspace, pHMgr);
812 for (int j=0; j < ns; j++)
813 mkallcap((*slst)[j]);
814 break;
819 // word reversing wrapper for complex prefixes
820 if (complexprefixes) {
821 for (int j = 0; j < ns; j++) {
822 if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
826 // capitalize
827 if (capwords) for (int j=0; j < ns; j++) {
828 mkinitcap((*slst)[j]);
831 // expand suggestions with dot(s)
832 if (abbv && pAMgr && pAMgr->get_sugswithdots()) {
833 for (int j = 0; j < ns; j++) {
834 (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);
835 strcat((*slst)[j], word + strlen(word) - abbv);
839 // suggest keepcase
840 if (pAMgr->get_keepcase()) {
841 switch (captype) {
842 case INITCAP:
843 case ALLCAP: {
844 int l = 0;
845 for (int j=0; j < ns; j++) {
846 if (!spell((*slst)[j])) {
847 char s[MAXSWUTF8L];
848 w_char w[MAXSWL];
849 int len;
850 if (utf8) {
851 len = u8_u16(w, MAXSWL, (*slst)[j]);
852 } else {
853 strcpy(s, (*slst)[j]);
854 len = strlen(s);
856 mkallsmall2(s, w, len);
857 free((*slst)[j]);
858 if (spell(s)) {
859 (*slst)[l] = mystrdup(s);
860 l++;
861 } else {
862 mkinitcap2(s, w, len);
863 if (spell(s)) {
864 (*slst)[l] = mystrdup(s);
865 l++;
868 } else {
869 (*slst)[l] = (*slst)[j];
870 l++;
873 ns = l;
878 // remove duplications
879 int l = 0;
880 for (int j = 0; j < ns; j++) {
881 (*slst)[l] = (*slst)[j];
882 for (int k = 0; k < l; k++) {
883 if (strcmp((*slst)[k], (*slst)[j]) == 0) {
884 free((*slst)[j]);
885 l--;
888 l++;
890 return l;
893 char * Hunspell::get_dic_encoding()
895 return encoding;
898 #ifdef HUNSPELL_EXPERIMENTAL
899 // XXX need UTF-8 support
900 int Hunspell::suggest_auto(char*** slst, const char * word)
902 char cw[MAXWORDUTF8LEN + 4];
903 char wspace[MAXWORDUTF8LEN + 4];
904 if (! pSMgr) return 0;
905 int wl = strlen(word);
906 if (utf8) {
907 if (wl >= MAXWORDUTF8LEN) return 0;
908 } else {
909 if (wl >= MAXWORDLEN) return 0;
911 int captype = 0;
912 int abbv = 0;
913 wl = cleanword(cw, word, &captype, &abbv);
914 if (wl == 0) return 0;
915 int ns = 0;
916 *slst = NULL; // HU, nsug in pSMgr->suggest
918 switch(captype) {
919 case NOCAP: {
920 ns = pSMgr->suggest_auto(slst, cw, ns);
921 if (ns>0) break;
922 break;
925 case INITCAP: {
926 memcpy(wspace,cw,(wl+1));
927 mkallsmall(wspace);
928 ns = pSMgr->suggest_auto(slst, wspace, ns);
929 for (int j=0; j < ns; j++)
930 mkinitcap((*slst)[j]);
931 ns = pSMgr->suggest_auto(slst, cw, ns);
932 break;
936 case HUHCAP: {
937 ns = pSMgr->suggest_auto(slst, cw, ns);
938 if (ns == 0) {
939 memcpy(wspace,cw,(wl+1));
940 mkallsmall(wspace);
941 ns = pSMgr->suggest_auto(slst, wspace, ns);
943 break;
946 case ALLCAP: {
947 memcpy(wspace,cw,(wl+1));
948 mkallsmall(wspace);
949 ns = pSMgr->suggest_auto(slst, wspace, ns);
951 mkinitcap(wspace);
952 ns = pSMgr->suggest_auto(slst, wspace, ns);
954 for (int j=0; j < ns; j++)
955 mkallcap((*slst)[j]);
956 break;
960 // word reversing wrapper for complex prefixes
961 if (complexprefixes) {
962 for (int j = 0; j < ns; j++) {
963 if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
967 // expand suggestions with dot(s)
968 if (abbv && pAMgr && pAMgr->get_sugswithdots()) {
969 for (int j = 0; j < ns; j++) {
970 (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);
971 strcat((*slst)[j], word + strlen(word) - abbv);
975 // LANG_hu section: replace '-' with ' ' in Hungarian
976 if (langnum == LANG_hu) {
977 for (int j=0; j < ns; j++) {
978 char * pos = strchr((*slst)[j],'-');
979 if (pos) {
980 int info;
981 char w[MAXWORDUTF8LEN];
982 *pos = '\0';
983 strcpy(w, (*slst)[j]);
984 strcat(w, pos + 1);
985 spell(w, &info, NULL);
986 if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
987 *pos = ' ';
988 } else *pos = '-';
992 // END OF LANG_hu section
993 return ns;
996 // XXX need UTF-8 support
997 int Hunspell::stem(char*** slst, const char * word)
999 char cw[MAXWORDUTF8LEN + 4];
1000 char wspace[MAXWORDUTF8LEN + 4];
1001 if (! pSMgr) return 0;
1002 int wl = strlen(word);
1003 if (utf8) {
1004 if (wl >= MAXWORDUTF8LEN) return 0;
1005 } else {
1006 if (wl >= MAXWORDLEN) return 0;
1008 int captype = 0;
1009 int abbv = 0;
1010 wl = cleanword(cw, word, &captype, &abbv);
1011 if (wl == 0) return 0;
1013 int ns = 0;
1015 *slst = NULL; // HU, nsug in pSMgr->suggest
1017 switch(captype) {
1018 case HUHCAP:
1019 case NOCAP: {
1020 ns = pSMgr->suggest_stems(slst, cw, ns);
1022 if ((abbv) && (ns == 0)) {
1023 memcpy(wspace,cw,wl);
1024 *(wspace+wl) = '.';
1025 *(wspace+wl+1) = '\0';
1026 ns = pSMgr->suggest_stems(slst, wspace, ns);
1029 break;
1032 case INITCAP: {
1034 ns = pSMgr->suggest_stems(slst, cw, ns);
1036 if (ns == 0) {
1037 memcpy(wspace,cw,(wl+1));
1038 mkallsmall(wspace);
1039 ns = pSMgr->suggest_stems(slst, wspace, ns);
1043 if ((abbv) && (ns == 0)) {
1044 memcpy(wspace,cw,wl);
1045 mkallsmall(wspace);
1046 *(wspace+wl) = '.';
1047 *(wspace+wl+1) = '\0';
1048 ns = pSMgr->suggest_stems(slst, wspace, ns);
1051 break;
1055 case ALLCAP: {
1056 ns = pSMgr->suggest_stems(slst, cw, ns);
1057 if (ns != 0) break;
1059 memcpy(wspace,cw,(wl+1));
1060 mkallsmall(wspace);
1061 ns = pSMgr->suggest_stems(slst, wspace, ns);
1063 if (ns == 0) {
1064 mkinitcap(wspace);
1065 ns = pSMgr->suggest_stems(slst, wspace, ns);
1068 if ((abbv) && (ns == 0)) {
1069 memcpy(wspace,cw,wl);
1070 mkallsmall(wspace);
1071 *(wspace+wl) = '.';
1072 *(wspace+wl+1) = '\0';
1073 ns = pSMgr->suggest_stems(slst, wspace, ns);
1077 break;
1081 return ns;
1084 int Hunspell::suggest_pos_stems(char*** slst, const char * word)
1086 char cw[MAXWORDUTF8LEN + 4];
1087 char wspace[MAXWORDUTF8LEN + 4];
1088 if (! pSMgr) return 0;
1089 int wl = strlen(word);
1090 if (utf8) {
1091 if (wl >= MAXWORDUTF8LEN) return 0;
1092 } else {
1093 if (wl >= MAXWORDLEN) return 0;
1095 int captype = 0;
1096 int abbv = 0;
1097 wl = cleanword(cw, word, &captype, &abbv);
1098 if (wl == 0) return 0;
1100 int ns = 0; // ns=0 = normalized input
1102 *slst = NULL; // HU, nsug in pSMgr->suggest
1104 switch(captype) {
1105 case HUHCAP:
1106 case NOCAP: {
1107 ns = pSMgr->suggest_pos_stems(slst, cw, ns);
1109 if ((abbv) && (ns == 0)) {
1110 memcpy(wspace,cw,wl);
1111 *(wspace+wl) = '.';
1112 *(wspace+wl+1) = '\0';
1113 ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
1116 break;
1119 case INITCAP: {
1121 ns = pSMgr->suggest_pos_stems(slst, cw, ns);
1123 if (ns == 0 || ((*slst)[0][0] == '#')) {
1124 memcpy(wspace,cw,(wl+1));
1125 mkallsmall(wspace);
1126 ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
1129 break;
1133 case ALLCAP: {
1134 ns = pSMgr->suggest_pos_stems(slst, cw, ns);
1135 if (ns != 0) break;
1137 memcpy(wspace,cw,(wl+1));
1138 mkallsmall(wspace);
1139 ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
1141 if (ns == 0) {
1142 mkinitcap(wspace);
1143 ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
1145 break;
1149 return ns;
1151 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
1153 const char * Hunspell::get_wordchars()
1155 return pAMgr->get_wordchars();
1158 unsigned short * Hunspell::get_wordchars_utf16(int * len)
1160 return pAMgr->get_wordchars_utf16(len);
1163 void Hunspell::mkinitcap(char * p)
1165 if (!utf8) {
1166 if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
1167 } else {
1168 int len;
1169 w_char u[MAXWORDLEN];
1170 len = u8_u16(u, MAXWORDLEN, p);
1171 unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
1172 u[0].h = (unsigned char) (i >> 8);
1173 u[0].l = (unsigned char) (i & 0x00FF);
1174 u16_u8(p, MAXWORDUTF8LEN, u, len);
1178 int Hunspell::mkinitcap2(char * p, w_char * u, int nc)
1180 if (!utf8) {
1181 if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
1182 } else if (nc > 0) {
1183 unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
1184 u[0].h = (unsigned char) (i >> 8);
1185 u[0].l = (unsigned char) (i & 0x00FF);
1186 u16_u8(p, MAXWORDUTF8LEN, u, nc);
1187 return strlen(p);
1189 return nc;
1192 int Hunspell::mkinitsmall2(char * p, w_char * u, int nc)
1194 if (!utf8) {
1195 if (*p != '\0') *p = csconv[((unsigned char)*p)].clower;
1196 } else if (nc > 0) {
1197 unsigned short i = unicodetolower((u[0].h << 8) + u[0].l, langnum);
1198 u[0].h = (unsigned char) (i >> 8);
1199 u[0].l = (unsigned char) (i & 0x00FF);
1200 u16_u8(p, MAXWORDUTF8LEN, u, nc);
1201 return strlen(p);
1203 return nc;
1206 int Hunspell::put_word(const char * word)
1208 if (pHMgr) {
1209 return pHMgr->put_word(word, strlen(word), NULL);
1211 return 0;
1214 int Hunspell::put_word_pattern(const char * word, const char * pattern)
1216 if (pHMgr) {
1217 return pHMgr->put_word_pattern(word, strlen(word), pattern);
1219 return 0;
1222 const char * Hunspell::get_version()
1224 return pAMgr->get_version();
1227 struct cs_info * Hunspell::get_csconv()
1229 return csconv;
1232 #ifdef HUNSPELL_EXPERIMENTAL
1233 // XXX need UTF-8 support
1234 char * Hunspell::morph(const char * word)
1236 char cw[MAXWORDUTF8LEN + 4];
1237 char wspace[MAXWORDUTF8LEN + 4];
1238 if (! pSMgr) return 0;
1239 int wl = strlen(word);
1240 if (utf8) {
1241 if (wl >= MAXWORDUTF8LEN) return 0;
1242 } else {
1243 if (wl >= MAXWORDLEN) return 0;
1245 int captype = 0;
1246 int abbv = 0;
1247 wl = cleanword(cw, word, &captype, &abbv);
1248 if (wl == 0) {
1249 if (abbv) {
1250 for (wl = 0; wl < abbv; wl++) cw[wl] = '.';
1251 cw[wl] = '\0';
1252 abbv = 0;
1253 } else return 0;
1256 char result[MAXLNLEN];
1257 char * st = NULL;
1259 *result = '\0';
1261 int n = 0;
1262 int n2 = 0;
1263 int n3 = 0;
1265 // test numbers
1266 // LANG_hu section: set dash information for suggestions
1267 if (langnum == LANG_hu) {
1268 while ((n < wl) &&
1269 (((cw[n] <= '9') && (cw[n] >= '0')) || (((cw[n] == '.') || (cw[n] == ',')) && (n > 0)))) {
1270 n++;
1271 if ((cw[n] == '.') || (cw[n] == ',')) {
1272 if (((n2 == 0) && (n > 3)) ||
1273 ((n2 > 0) && ((cw[n-1] == '.') || (cw[n-1] == ',')))) break;
1274 n2++;
1275 n3 = n;
1279 if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return NULL;
1280 if ((n == wl) || ((n>0) && ((cw[n]=='%') || (cw[n]=='?)) && checkword(cw+n, NULL, NULL))) {
1281 strcat(result, cw);
1282 result[n - 1] = '\0';
1283 if (n == wl) {
1284 st = pSMgr->suggest_morph(cw + n - 1);
1285 if (st) {
1286 strcat(result, st);
1287 free(st);
1289 } else {
1290 char sign = cw[n];
1291 cw[n] = '\0';
1292 st = pSMgr->suggest_morph(cw + n - 1);
1293 if (st) {
1294 strcat(result, st);
1295 free(st);
1297 strcat(result, "+"); // XXX SPEC. MORPHCODE
1298 cw[n] = sign;
1299 st = pSMgr->suggest_morph(cw + n);
1300 if (st) {
1301 strcat(result, st);
1302 free(st);
1305 return mystrdup(result);
1308 // END OF LANG_hu section
1310 switch(captype) {
1311 case NOCAP: {
1312 st = pSMgr->suggest_morph(cw);
1313 if (st) {
1314 strcat(result, st);
1315 free(st);
1317 if (abbv) {
1318 memcpy(wspace,cw,wl);
1319 *(wspace+wl) = '.';
1320 *(wspace+wl+1) = '\0';
1321 st = pSMgr->suggest_morph(wspace);
1322 if (st) {
1323 if (*result) strcat(result, "\n");
1324 strcat(result, st);
1325 free(st);
1328 break;
1330 case INITCAP: {
1331 memcpy(wspace,cw,(wl+1));
1332 mkallsmall(wspace);
1333 st = pSMgr->suggest_morph(wspace);
1334 if (st) {
1335 strcat(result, st);
1336 free(st);
1338 st = pSMgr->suggest_morph(cw);
1339 if (st) {
1340 if (*result) strcat(result, "\n");
1341 strcat(result, st);
1342 free(st);
1344 if (abbv) {
1345 memcpy(wspace,cw,wl);
1346 *(wspace+wl) = '.';
1347 *(wspace+wl+1) = '\0';
1348 mkallsmall(wspace);
1349 st = pSMgr->suggest_morph(wspace);
1350 if (st) {
1351 if (*result) strcat(result, "\n");
1352 strcat(result, st);
1353 free(st);
1355 mkinitcap(wspace);
1356 st = pSMgr->suggest_morph(wspace);
1357 if (st) {
1358 if (*result) strcat(result, "\n");
1359 strcat(result, st);
1360 free(st);
1363 break;
1365 case HUHCAP: {
1366 st = pSMgr->suggest_morph(cw);
1367 if (st) {
1368 strcat(result, st);
1369 free(st);
1371 #if 0
1372 memcpy(wspace,cw,(wl+1));
1373 mkallsmall(wspace);
1374 st = pSMgr->suggest_morph(wspace);
1375 if (st) {
1376 if (*result) strcat(result, "\n");
1377 strcat(result, st);
1378 free(st);
1380 #endif
1381 break;
1383 case ALLCAP: {
1384 memcpy(wspace,cw,(wl+1));
1385 st = pSMgr->suggest_morph(wspace);
1386 if (st) {
1387 strcat(result, st);
1388 free(st);
1390 mkallsmall(wspace);
1391 st = pSMgr->suggest_morph(wspace);
1392 if (st) {
1393 if (*result) strcat(result, "\n");
1394 strcat(result, st);
1395 free(st);
1397 mkinitcap(wspace);
1398 st = pSMgr->suggest_morph(wspace);
1399 if (st) {
1400 if (*result) strcat(result, "\n");
1401 strcat(result, st);
1402 free(st);
1404 if (abbv) {
1405 memcpy(wspace,cw,(wl+1));
1406 *(wspace+wl) = '.';
1407 *(wspace+wl+1) = '\0';
1408 if (*result) strcat(result, "\n");
1409 st = pSMgr->suggest_morph(wspace);
1410 if (st) {
1411 strcat(result, st);
1412 free(st);
1414 mkallsmall(wspace);
1415 st = pSMgr->suggest_morph(wspace);
1416 if (st) {
1417 if (*result) strcat(result, "\n");
1418 strcat(result, st);
1419 free(st);
1421 mkinitcap(wspace);
1422 st = pSMgr->suggest_morph(wspace);
1423 if (st) {
1424 if (*result) strcat(result, "\n");
1425 strcat(result, st);
1426 free(st);
1429 break;
1433 if (result && (*result)) {
1434 // word reversing wrapper for complex prefixes
1435 if (complexprefixes) {
1436 if (utf8) reverseword_utf(result); else reverseword(result);
1438 return mystrdup(result);
1441 // compound word with dash (HU) I18n
1442 char * dash = NULL;
1443 int nresult = 0;
1444 // LANG_hu section: set dash information for suggestions
1445 if (langnum == LANG_hu) dash = (char *) strchr(cw,'-');
1446 if ((langnum == LANG_hu) && dash) {
1447 *dash='\0';
1448 // examine 2 sides of the dash
1449 if (dash[1] == '\0') { // base word ending with dash
1450 if (spell(cw)) return pSMgr->suggest_morph(cw);
1451 } else if ((dash[1] == 'e') && (dash[2] == '\0')) { // XXX (HU) -e hat.
1452 if (spell(cw) && (spell("-e"))) {
1453 st = pSMgr->suggest_morph(cw);
1454 if (st) {
1455 strcat(result, st);
1456 free(st);
1458 strcat(result,"+"); // XXX spec. separator in MORPHCODE
1459 st = pSMgr->suggest_morph("-e");
1460 if (st) {
1461 strcat(result, st);
1462 free(st);
1464 return mystrdup(result);
1466 } else {
1467 // first word ending with dash: word- XXX ???
1468 char r2 = *(dash + 1);
1469 dash[0]='-';
1470 dash[1]='\0';
1471 nresult = spell(cw);
1472 dash[1] = r2;
1473 dash[0]='\0';
1474 if (nresult && spell(dash+1) && ((strlen(dash+1) > 1) ||
1475 ((dash[1] > '0') && (dash[1] < '9')))) {
1476 st = morph(cw);
1477 if (st) {
1478 strcat(result, st);
1479 free(st);
1480 strcat(result,"+"); // XXX spec. separator in MORPHCODE
1482 st = morph(dash+1);
1483 if (st) {
1484 strcat(result, st);
1485 free(st);
1487 return mystrdup(result);
1490 // affixed number in correct word
1491 if (nresult && (dash > cw) && (((*(dash-1)<='9') &&
1492 (*(dash-1)>='0')) || (*(dash-1)=='.'))) {
1493 *dash='-';
1494 n = 1;
1495 if (*(dash - n) == '.') n++;
1496 // search first not a number character to left from dash
1497 while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {
1498 n++;
1500 if ((dash - n) < cw) n--;
1501 // numbers: valami1000000-hoz
1502 // examine 100000-hoz, 10000-hoz 1000-hoz, 10-hoz,
1503 // 56-hoz, 6-hoz
1504 for(; n >= 1; n--) {
1505 if ((*(dash - n) >= '0') && (*(dash - n) <= '9') && checkword(dash - n, NULL, NULL)) {
1506 strcat(result, cw);
1507 result[dash - cw - n] = '\0';
1508 st = pSMgr->suggest_morph(dash - n);
1509 if (st) {
1510 strcat(result, st);
1511 free(st);
1513 return mystrdup(result);
1518 return NULL;
1521 // XXX need UTF-8 support
1522 char * Hunspell::morph_with_correction(const char * word)
1524 char cw[MAXWORDUTF8LEN + 4];
1525 char wspace[MAXWORDUTF8LEN + 4];
1526 if (! pSMgr) return 0;
1527 int wl = strlen(word);
1528 if (utf8) {
1529 if (wl >= MAXWORDUTF8LEN) return 0;
1530 } else {
1531 if (wl >= MAXWORDLEN) return 0;
1533 int captype = 0;
1534 int abbv = 0;
1535 wl = cleanword(cw, word, &captype, &abbv);
1536 if (wl == 0) return 0;
1538 char result[MAXLNLEN];
1539 char * st = NULL;
1541 *result = '\0';
1544 switch(captype) {
1545 case NOCAP: {
1546 st = pSMgr->suggest_morph_for_spelling_error(cw);
1547 if (st) {
1548 strcat(result, st);
1549 free(st);
1551 if (abbv) {
1552 memcpy(wspace,cw,wl);
1553 *(wspace+wl) = '.';
1554 *(wspace+wl+1) = '\0';
1555 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1556 if (st) {
1557 if (*result) strcat(result, "\n");
1558 strcat(result, st);
1559 free(st);
1562 break;
1564 case INITCAP: {
1565 memcpy(wspace,cw,(wl+1));
1566 mkallsmall(wspace);
1567 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1568 if (st) {
1569 strcat(result, st);
1570 free(st);
1572 st = pSMgr->suggest_morph_for_spelling_error(cw);
1573 if (st) {
1574 if (*result) strcat(result, "\n");
1575 strcat(result, st);
1576 free(st);
1578 if (abbv) {
1579 memcpy(wspace,cw,wl);
1580 *(wspace+wl) = '.';
1581 *(wspace+wl+1) = '\0';
1582 mkallsmall(wspace);
1583 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1584 if (st) {
1585 if (*result) strcat(result, "\n");
1586 strcat(result, st);
1587 free(st);
1589 mkinitcap(wspace);
1590 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1591 if (st) {
1592 if (*result) strcat(result, "\n");
1593 strcat(result, st);
1594 free(st);
1597 break;
1599 case HUHCAP: {
1600 st = pSMgr->suggest_morph_for_spelling_error(cw);
1601 if (st) {
1602 strcat(result, st);
1603 free(st);
1605 memcpy(wspace,cw,(wl+1));
1606 mkallsmall(wspace);
1607 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1608 if (st) {
1609 if (*result) strcat(result, "\n");
1610 strcat(result, st);
1611 free(st);
1613 break;
1615 case ALLCAP: {
1616 memcpy(wspace,cw,(wl+1));
1617 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1618 if (st) {
1619 strcat(result, st);
1620 free(st);
1622 mkallsmall(wspace);
1623 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1624 if (st) {
1625 if (*result) strcat(result, "\n");
1626 strcat(result, st);
1627 free(st);
1629 mkinitcap(wspace);
1630 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1631 if (st) {
1632 if (*result) strcat(result, "\n");
1633 strcat(result, st);
1634 free(st);
1636 if (abbv) {
1637 memcpy(wspace,cw,(wl+1));
1638 *(wspace+wl) = '.';
1639 *(wspace+wl+1) = '\0';
1640 if (*result) strcat(result, "\n");
1641 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1642 if (st) {
1643 strcat(result, st);
1644 free(st);
1646 mkallsmall(wspace);
1647 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1648 if (st) {
1649 if (*result) strcat(result, "\n");
1650 strcat(result, st);
1651 free(st);
1653 mkinitcap(wspace);
1654 st = pSMgr->suggest_morph_for_spelling_error(wspace);
1655 if (st) {
1656 if (*result) strcat(result, "\n");
1657 strcat(result, st);
1658 free(st);
1661 break;
1665 if (result) return mystrdup(result);
1666 return NULL;
1669 /* analyze word
1670 * return line count
1671 * XXX need a better data structure for morphological analysis */
1672 int Hunspell::analyze(char ***out, const char *word) {
1673 int n = 0;
1674 if (!word) return 0;
1675 char * m = morph(word);
1676 if(!m) return 0;
1677 if (!out) return line_tok(m, out);
1679 // without memory allocation
1680 /* BUG missing buffer size checking */
1681 int i, p;
1682 for(p = 0, i = 0; m[i]; i++) {
1683 if(m[i] == '\n' || !m[i+1]) {
1684 n++;
1685 strncpy((*out)[n++], m + p, i - p + 1);
1686 if (m[i] == '\n') (*out)[n++][i - p] = '\0';
1687 if(!m[i+1]) break;
1688 p = i + 1;
1691 free(m);
1692 return n;
1695 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
1697 Hunhandle *Hunspell_create(const char * affpath, const char * dpath)
1699 return (Hunhandle*)(new Hunspell(affpath, dpath));
1702 void Hunspell_destroy(Hunhandle *pHunspell)
1704 delete (Hunspell*)(pHunspell);
1707 int Hunspell_spell(Hunhandle *pHunspell, const char *word)
1709 return ((Hunspell*)pHunspell)->spell(word);
1712 char *Hunspell_get_dic_encoding(Hunhandle *pHunspell)
1714 return ((Hunspell*)pHunspell)->get_dic_encoding();
1717 int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word)
1719 return ((Hunspell*)pHunspell)->suggest(slst, word);