diff options
author | sidchat@google.com <sidchat@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-04 18:52:34 +0000 |
---|---|---|
committer | sidchat@google.com <sidchat@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-12-04 18:52:34 +0000 |
commit | c0b2c0f842774e99034eb4454132c8d909bac1bb (patch) | |
tree | b1bd93f72d7793915f9c4431bcfbf74db7a6f950 /chrome | |
parent | e89f655d64b6e546ff5e3c80b65a7e2025da3ca0 (diff) | |
download | chromium_src-c0b2c0f842774e99034eb4454132c8d909bac1bb.zip chromium_src-c0b2c0f842774e99034eb4454132c8d909bac1bb.tar.gz chromium_src-c0b2c0f842774e99034eb4454132c8d909bac1bb.tar.bz2 |
Fix the issue in Spell Checker when certain words (such as especelly) did not give any suggestions.
Issue=1677
Review URL: http://codereview.chromium.org/11285
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@6363 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r-- | chrome/third_party/hunspell/src/hunspell/hashmgr.cxx | 49 | ||||
-rw-r--r-- | chrome/third_party/hunspell/src/hunspell/hashmgr.hxx | 5 | ||||
-rw-r--r-- | chrome/third_party/hunspell/src/hunspell/suggestmgr.cxx | 3352 |
3 files changed, 1732 insertions, 1674 deletions
diff --git a/chrome/third_party/hunspell/src/hunspell/hashmgr.cxx b/chrome/third_party/hunspell/src/hunspell/hashmgr.cxx index 8f20dd3..71f2964 100644 --- a/chrome/third_party/hunspell/src/hunspell/hashmgr.cxx +++ b/chrome/third_party/hunspell/src/hunspell/hashmgr.cxx @@ -285,11 +285,9 @@ int HashMgr::put_word_pattern(const char * word, int wl, const char * pattern) struct hentry * HashMgr::walk_hashtable(int &col, struct hentry * hp) const
{
#ifdef HUNSPELL_CHROME_CLIENT
- // DANGER! This is kind of impossible to make work correctly, since Hunspell
- // will keep arbitrary hentry pointers into our table. Therefore, the caller
- // (SuggestMgr::ngsuggest) will need to be modified to not do this for us
- // to be able to uncomment this function.
-/*
+ // This function creates a new hentry if NULL is passed as hp. It also takes
+ // the responsibility of deleting the pointer hp when walk is over.
+
// This function is only ever called by one place and not nested. We can
// therefore keep static state between calls and use |col| as a "reset" flag
// to avoid changing the API. It is set to -1 for the first call.
@@ -302,11 +300,26 @@ struct hentry * HashMgr::walk_hashtable(int &col, struct hentry * hp) const int affix_ids[hunspell::BDict::MAX_AFFIXES_PER_WORD];
static const int kMaxWordLen = 128;
- static char word_buf[kMaxWordLen];
- int affix_count = word_iterator.Advance(word_buf, kMaxWordLen, affix_ids);
- return AffixIDsToHentry(word_buf, affix_ids, affix_count);
-*/
- return NULL;
+ static char word[kMaxWordLen];
+ int affix_count = word_iterator.Advance(word, kMaxWordLen, affix_ids);
+ if (affix_count == 0) {
+ delete hp;
+ return NULL;
+ }
+ short word_len = static_cast<short>(strlen(word));
+
+ // For now, just re-compute the |hp| and return it. No need to create linked
+ // lists for the extra affixes. If hp is NULL, create it here.
+ if (!hp)
+ hp = new hentry;
+ hp->word = word;
+ hp->wlen = word_len;
+ hp->alen = (short)const_cast<HashMgr*>(this)->get_aliasf(affix_ids[0],
+ &hp->astr);
+ hp->next = NULL;
+ hp->next_homonym = NULL;
+
+ return hp;
#else
//reset to start
if ((col < 0) || (hp == NULL)) {
@@ -765,7 +778,8 @@ int HashMgr::parse_aliasf(char * line, FILE * af) #ifdef HUNSPELL_CHROME_CLIENT
hentry* HashMgr::AffixIDsToHentry(char* word,
- int* affix_ids, int affix_count) const
+ int* affix_ids,
+ int affix_count) const
{
if (affix_count == 0)
return NULL;
@@ -808,6 +822,19 @@ hentry* HashMgr::AffixIDsToHentry(char* word, #endif
+#ifdef HUNSPELL_CHROME_CLIENT
+hentry* HashMgr::GetHentryFromHEntryCache(char* word) {
+ HEntryCache& cache = const_cast<HashMgr*>(this)->hentry_cache;
+ std::string std_word(word);
+ HEntryCache::iterator found = cache.find(std_word);
+ if (found != cache.end())
+ return found->second;
+ else
+ return NULL;
+}
+
+#endif
+
int HashMgr::is_aliasf() {
return (aliasf != NULL);
}
diff --git a/chrome/third_party/hunspell/src/hunspell/hashmgr.hxx b/chrome/third_party/hunspell/src/hunspell/hashmgr.hxx index a334d90..6b68988 100644 --- a/chrome/third_party/hunspell/src/hunspell/hashmgr.hxx +++ b/chrome/third_party/hunspell/src/hunspell/hashmgr.hxx @@ -41,6 +41,10 @@ class HashMgr public: #ifdef HUNSPELL_CHROME_CLIENT HashMgr(hunspell::BDictReader* reader); + + // Return the hentry corresponding to the given word. Returns NULL if the + // word is not there in the cache. + hentry* GetHentryFromHEntryCache(char* word); // Called before we do a new operation. This will empty the cache of pointers // to hentries that we have cached. In Chrome, we make these on-demand, but @@ -89,6 +93,7 @@ private: // followed by the homonym pointer. typedef std::map<std::string, hentry*> HEntryCache; HEntryCache hentry_cache; + #else int load_config(FILE* aff_handle); int parse_aliasf(char * line, FILE * af); diff --git a/chrome/third_party/hunspell/src/hunspell/suggestmgr.cxx b/chrome/third_party/hunspell/src/hunspell/suggestmgr.cxx index 345f687..18ae09b 100644 --- a/chrome/third_party/hunspell/src/hunspell/suggestmgr.cxx +++ b/chrome/third_party/hunspell/src/hunspell/suggestmgr.cxx @@ -1,1663 +1,1689 @@ -#include "license.hunspell" -#include "license.myspell" - -#ifndef MOZILLA_CLIENT -#include <cstdlib> -#include <cstring> -#include <cstdio> -#include <cctype> -#else -#include <stdlib.h> -#include <string.h> -#include <stdio.h> -#include <ctype.h> -#endif - -#include "suggestmgr.hxx" - -#ifndef MOZILLA_CLIENT -#ifndef W32 -using namespace std; -#endif -#endif - - -SuggestMgr::SuggestMgr(const char * tryme, int maxn, - AffixMgr * aptr) -{ - - // register affix manager and check in string of chars to - // try when building candidate suggestions - pAMgr = aptr; - - ctryl = 0; - ctry = NULL; - ctry_utf = NULL; - - maxSug = maxn; - nosplitsugs = 0; - maxngramsugs = MAXNGRAMSUGS; - - utf8 = 0; - complexprefixes = 0; - - if (pAMgr) { - char * enc = pAMgr->get_encoding(); - csconv = get_current_cs(enc); - free(enc); - nosplitsugs = pAMgr->get_nosplitsugs(); - if (pAMgr->get_maxngramsugs() >= 0) maxngramsugs = pAMgr->get_maxngramsugs(); - utf8 = pAMgr->get_utf8(); - complexprefixes = pAMgr->get_complexprefixes(); - } - - if (tryme) { - if (utf8) { - w_char t[MAXSWL]; - ctryl = u8_u16(t, MAXSWL, tryme); - ctry_utf = (w_char *) malloc(ctryl * sizeof(w_char)); - memcpy(ctry_utf, t, ctryl * sizeof(w_char)); - } else { - ctry = mystrdup(tryme); - ctryl = strlen(ctry); - } - } -} - - -SuggestMgr::~SuggestMgr() -{ - pAMgr = NULL; - if (ctry) free(ctry); - ctry = NULL; - if (ctry_utf) free(ctry_utf); - ctry_utf = NULL; - ctryl = 0; - maxSug = 0; -} - -int SuggestMgr::testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest, - int * timer, time_t * timelimit) { - int cwrd = 1; - if (ns == maxSug) return maxSug; - for (int k=0; k < ns; k++) { - if (strcmp(candidate,wlst[k]) == 0) cwrd = 0; - } - if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) { - wlst[ns] = mystrdup(candidate); - if (wlst[ns] == NULL) { - for (int j=0; j<ns; j++) free(wlst[j]); - return -1; - } - ns++; - } - return ns; -} - -// generate suggestions for a mispelled word -// pass in address of array of char * pointers - -int SuggestMgr::suggest(char*** slst, const char * w, int nsug) -{ - int nocompoundtwowords = 0; - char ** wlst; - w_char word_utf[MAXSWL]; - int wl = 0; - - char w2[MAXWORDUTF8LEN]; - const char * word = w; - - // word reversing wrapper for complex prefixes - if (complexprefixes) { - strcpy(w2, w); - if (utf8) reverseword_utf(w2); else reverseword(w2); - word = w2; - } - - if (*slst) { - wlst = *slst; - } else { - wlst = (char **) malloc(maxSug * sizeof(char *)); - if (wlst == NULL) return -1; - for (int i = 0; i < maxSug; i++) { - wlst[i] = NULL; - } - } - - if (utf8) { - wl = u8_u16(word_utf, MAXSWL, word); - } - - for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) { - - // suggestions for an uppercase word (html -> HTML) - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? capchars_utf(wlst, word_utf, wl, nsug, cpdsuggest) : - capchars(wlst, word, nsug, cpdsuggest); - } - - // perhaps we made a typical fault of spelling - if ((nsug < maxSug) && (nsug > -1)) - nsug = replchars(wlst, word, nsug, cpdsuggest); - - // perhaps we made chose the wrong char from a related set - if ((nsug < maxSug) && (nsug > -1) && (cpdsuggest == 0)) { - nsug = mapchars(wlst, word, nsug); - } - - // did we swap the order of chars by mistake - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? swapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : - swapchar(wlst, word, nsug, cpdsuggest); - } - - // did we swap the order of non adjacent chars by mistake - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? longswapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : - longswapchar(wlst, word, nsug, cpdsuggest); - } - - // did we forgot a char - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? forgotchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : - forgotchar(wlst, word, nsug, cpdsuggest); - } - - // did we move a char - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? movechar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : - movechar(wlst, word, nsug, cpdsuggest); - } - - // did we add a char that should not be there - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? extrachar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : - extrachar(wlst, word, nsug, cpdsuggest); - } - - // did we just hit the wrong key in place of a good char - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? badchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) : - badchar(wlst, word, nsug, cpdsuggest); - } - - // did we double two characters - if ((nsug < maxSug) && (nsug > -1)) { - nsug = (utf8) ? doubletwochars_utf(wlst, word_utf, wl, nsug, cpdsuggest) : - doubletwochars(wlst, word, nsug, cpdsuggest); - } - - - // only suggest compound words when no other suggestion - if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1; - - // perhaps we forgot to hit space and two words ran together - if ((!nosplitsugs) && (nsug < maxSug) && (nsug > -1)) { - nsug = twowords(wlst, word, nsug, cpdsuggest); - } - - } // repeating ``for'' statement compounding support - - if (nsug < 0) { - // we ran out of memory - we should free up as much as possible - for (int i = 0; i < maxSug; i++) - if (wlst[i] != NULL) free(wlst[i]); - free(wlst); - wlst = NULL; - } - - *slst = wlst; - return nsug; -} - -// generate suggestions for a word with typical mistake -// pass in address of array of char * pointers -#ifdef HUNSPELL_EXPERIMENTAL -int SuggestMgr::suggest_auto(char*** slst, const char * w, int nsug) -{ - int nocompoundtwowords = 0; - char ** wlst; - - char w2[MAXWORDUTF8LEN]; - const char * word = w; - - // word reversing wrapper for complex prefixes - if (complexprefixes) { - strcpy(w2, w); - if (utf8) reverseword_utf(w2); else reverseword(w2); - word = w2; - } - - if (*slst) { - wlst = *slst; - } else { - wlst = (char **) malloc(maxSug * sizeof(char *)); - if (wlst == NULL) return -1; - } - - for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) { - - // perhaps we made a typical fault of spelling - if ((nsug < maxSug) && (nsug > -1)) - nsug = replchars(wlst, word, nsug, cpdsuggest); - - // perhaps we made chose the wrong char from a related set - if ((nsug < maxSug) && (nsug > -1) && (cpdsuggest == 0)) - nsug = mapchars(wlst, word, nsug); - - if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1; - - // perhaps we forgot to hit space and two words ran together - - if ((nsug < maxSug) && (nsug > -1) && check_forbidden(word, strlen(word))) { - nsug = twowords(wlst, word, nsug, cpdsuggest); - } - - } // repeating ``for'' statement compounding support - - if (nsug < 0) { - for (int i=0;i<maxSug; i++) - if (wlst[i] != NULL) free(wlst[i]); - free(wlst); - return -1; - } - - *slst = wlst; - return nsug; -} -#endif // END OF HUNSPELL_EXPERIMENTAL CODE - -// suggestions for an uppercase word (html -> HTML) -int SuggestMgr::capchars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - w_char candidate_utf[MAXSWL]; - memcpy(candidate_utf, word, wl * sizeof(w_char)); - mkallcap_utf(candidate_utf, wl, pAMgr->get_langnum()); - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); - return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); -} - -// suggestions for an uppercase word (html -> HTML) -int SuggestMgr::capchars(char** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - strcpy(candidate, word); - mkallcap(candidate, csconv); - return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); -} - -// suggestions for when chose the wrong char out of a related set -int SuggestMgr::mapchars(char** wlst, const char * word, int ns) -{ - time_t timelimit; - int timer; - - int wl = strlen(word); - if (wl < 2 || ! pAMgr) return ns; - - int nummap = pAMgr->get_nummap(); - struct mapentry* maptable = pAMgr->get_maptable(); - if (maptable==NULL) return ns; - - timelimit = time(NULL); - timer = MINTIMER; - if (utf8) { - w_char w[MAXSWL]; - int len = u8_u16(w, MAXSWL, word); - ns = map_related_utf(w, len, 0, wlst, ns, maptable, nummap, &timer, &timelimit); - } else ns = map_related(word, 0, wlst, ns, maptable, nummap, &timer, &timelimit); - return ns; -} - -int SuggestMgr::map_related(const char * word, int i, char** wlst, int ns, - const mapentry* maptable, int nummap, int * timer, time_t * timelimit) -{ - char c = *(word + i); - if (c == 0) { - int cwrd = 1; - int wl = strlen(word); - for (int m=0; m < ns; m++) - if (strcmp(word,wlst[m]) == 0) cwrd = 0; - if ((cwrd) && (checkword(word, wl, 0, timer, timelimit) || - checkword(word, wl, 1, timer, timelimit))) { - if (ns < maxSug) { - wlst[ns] = mystrdup(word); - if (wlst[ns] == NULL) return -1; - ns++; - } - } - return ns; - } - int in_map = 0; - for (int j = 0; j < nummap; j++) { - if (strchr(maptable[j].set,c) != 0) { - in_map = 1; - char * newword = mystrdup(word); - for (int k = 0; k < maptable[j].len; k++) { - *(newword + i) = *(maptable[j].set + k); - ns = map_related(newword, (i+1), wlst, ns, maptable, nummap, timer, timelimit); - if (!(*timelimit)) return ns; - } - free(newword); - } - } - if (!in_map) { - i++; - ns = map_related(word, i, wlst, ns, maptable, nummap, timer, timelimit); - } - return ns; -} - -int SuggestMgr::map_related_utf(w_char * word, int len, int i, char** wlst, int ns, - const mapentry* maptable, int nummap, int * timer, time_t * timelimit) -{ - if (i == len) { - int cwrd = 1; - int wl; - char s[MAXSWUTF8L]; - u16_u8(s, MAXSWUTF8L, word, len); - wl = strlen(s); - for (int m=0; m < ns; m++) - if (strcmp(s,wlst[m]) == 0) cwrd = 0; - if ((cwrd) && (checkword(s, wl, 0, timer, timelimit) || - checkword(s, wl, 1, timer, timelimit))) { - if (ns < maxSug) { - wlst[ns] = mystrdup(s); - if (wlst[ns] == NULL) return -1; - ns++; - } - } - return ns; - } - int in_map = 0; - unsigned short c = *((unsigned short *) word + i); - for (int j = 0; j < nummap; j++) { - if (flag_bsearch((unsigned short *) maptable[j].set_utf16, c, maptable[j].len)) { - in_map = 1; - for (int k = 0; k < maptable[j].len; k++) { - *(word + i) = *(maptable[j].set_utf16 + k); - ns = map_related_utf(word, len, i + 1, wlst, ns, maptable, nummap, timer, timelimit); - if (!(*timelimit)) return ns; - } - *((unsigned short *) word + i) = c; - } - } - if (!in_map) { - i++; - ns = map_related_utf(word, len, i, wlst, ns, maptable, nummap, timer, timelimit); - } - return ns; -} - - - -// suggestions for a typical fault of spelling, that -// differs with more, than 1 letter from the right form. -int SuggestMgr::replchars(char** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - const char * r; - int lenr, lenp; - int wl = strlen(word); - if (wl < 2 || ! pAMgr) return ns; - int numrep = pAMgr->get_numrep(); - struct replentry* reptable = pAMgr->get_reptable(); - if (reptable==NULL) return ns; - for (int i=0; i < numrep; i++ ) { - r = word; - lenr = strlen(reptable[i].pattern2); - lenp = strlen(reptable[i].pattern); - // search every occurence of the pattern in the word - while ((r=strstr(r, reptable[i].pattern)) != NULL) { - strcpy(candidate, word); - if (r-word + lenr + strlen(r+lenp) >= MAXSWUTF8L) break; - strcpy(candidate+(r-word),reptable[i].pattern2); - strcpy(candidate+(r-word)+lenr, r+lenp); - ns = testsug(wlst, candidate, wl-lenp+lenr, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - r++; // search for the next letter - } - } - return ns; -} - -// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation) -int SuggestMgr::doubletwochars(char** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - int state=0; - int wl = strlen(word); - if (wl < 5 || ! pAMgr) return ns; - for (int i=2; i < wl; i++ ) { - if (word[i]==word[i-2]) { - state++; - if (state==3) { - strcpy(candidate,word); - strcpy(candidate+i-1,word+i+1); - ns = testsug(wlst, candidate, wl-2, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - state=0; - } - } else { - state=0; - } - } - return ns; -} - -// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation) -int SuggestMgr::doubletwochars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) -{ - w_char candidate_utf[MAXSWL]; - char candidate[MAXSWUTF8L]; - int state=0; - if (wl < 5 || ! pAMgr) return ns; - for (int i=2; i < wl; i++) { - if ((word[i].l==word[i-2].l) && (word[i].h==word[i-2].h)) { - state++; - if (state==3) { - memcpy(candidate_utf, word, (i - 1) * sizeof(w_char)); - memcpy(candidate_utf+i-1, word+i+1, (wl-i-1) * sizeof(w_char)); - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl-2); - ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - state=0; - } - } else { - state=0; - } - } - return ns; -} - -// error is wrong char in place of correct one -int SuggestMgr::badchar(char ** wlst, const char * word, int ns, int cpdsuggest) -{ - char tmpc; - char candidate[MAXSWUTF8L]; - time_t timelimit = time(NULL); - int timer = MINTIMER; - int wl = strlen(word); - strcpy(candidate, word); - // swap out each char one by one and try all the tryme - // chars in its place to see if that makes a good word - for (int i=0; i < wl; i++) { - tmpc = candidate[i]; - for (int j=0; j < ctryl; j++) { - if (ctry[j] == tmpc) continue; - candidate[i] = ctry[j]; - ns = testsug(wlst, candidate, wl, ns, cpdsuggest, &timer, &timelimit); - if (ns == -1) return -1; - if (!timelimit) return ns; - candidate[i] = tmpc; - } - } - return ns; -} - -// error is wrong char in place of correct one -int SuggestMgr::badchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) -{ - w_char tmpc; - w_char candidate_utf[MAXSWL]; - char candidate[MAXSWUTF8L]; - time_t timelimit = time(NULL); - int timer = MINTIMER; - memcpy(candidate_utf, word, wl * sizeof(w_char)); - // swap out each char one by one and try all the tryme - // chars in its place to see if that makes a good word - for (int i=0; i < wl; i++) { - tmpc = candidate_utf[i]; - for (int j=0; j < ctryl; j++) { - if ((ctry_utf[j].l == tmpc.l) && (ctry_utf[j].h == tmpc.h)) continue; - candidate_utf[i] = ctry_utf[j]; - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); - ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit); - if (ns == -1) return -1; - if (!timelimit) return ns; - candidate_utf[i] = tmpc; - } - } - return ns; -} - -// error is word has an extra letter it does not need -int SuggestMgr::extrachar_utf(char** wlst, const w_char * word, int wl, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - w_char candidate_utf[MAXSWL]; - const w_char * p; - w_char * r; - if (wl < 2) return ns; - // try omitting one char of word at a time - memcpy(candidate_utf, word + 1, (wl - 1) * sizeof(w_char)); - for (p = word, r = candidate_utf; p < word + wl; ) { - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl - 1); - ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - *r++ = *p++; - } - return ns; -} - -// error is word has an extra letter it does not need -int SuggestMgr::extrachar(char** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - const char * p; - char * r; - int wl = strlen(word); - if (wl < 2) return ns; - // try omitting one char of word at a time - strcpy (candidate, word + 1); - for (p = word, r = candidate; *p != 0; ) { - ns = testsug(wlst, candidate, wl-1, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - *r++ = *p++; - } - return ns; -} - - -// error is missing a letter it needs -int SuggestMgr::forgotchar(char ** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - const char * p; - char * q; - time_t timelimit = time(NULL); - int timer = MINTIMER; - int wl = strlen(word); - // try inserting a tryme character before every letter - strcpy(candidate + 1, word); - for (p = word, q = candidate; *p != 0; ) { - for (int i = 0; i < ctryl; i++) { - *q = ctry[i]; - ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, &timer, &timelimit); - if (ns == -1) return -1; - if (!timelimit) return ns; - } - *q++ = *p++; - } - // now try adding one to end */ - for (int i = 0; i < ctryl; i++) { - *q = ctry[i]; - ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - } - return ns; -} - -// error is missing a letter it needs -int SuggestMgr::forgotchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) -{ - w_char candidate_utf[MAXSWL]; - char candidate[MAXSWUTF8L]; - const w_char * p; - w_char * q; - int cwrd; - time_t timelimit = time(NULL); - int timer = MINTIMER; - // try inserting a tryme character before every letter - memcpy (candidate_utf + 1, word, wl * sizeof(w_char)); - for (p = word, q = candidate_utf; p < (word + wl); ) { - for (int i = 0; i < ctryl; i++) { - *q = ctry_utf[i]; - cwrd = 1; - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1); - ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit); - if (ns == -1) return -1; - if (!timelimit) return ns; - } - *q++ = *p++; - } - // now try adding one to end */ - for (int i = 0; i < ctryl; i++) { - *q = ctry_utf[i]; - cwrd = 1; - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1); - ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - } - return ns; -} - - -/* error is should have been two words */ -int SuggestMgr::twowords(char ** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - char * p; - int c1, c2; - int forbidden = 0; - int cwrd; - - int wl=strlen(word); - if (wl < 3) return ns; - - if (pAMgr->get_langnum() == LANG_hu) forbidden = check_forbidden(word, wl); - - strcpy(candidate + 1, word); - - // split the string into two pieces after every char - // if both pieces are good words make them a suggestion - for (p = candidate + 1; p[1] != '\0'; p++) { - p[-1] = *p; - // go to end of the UTF-8 character - while (utf8 && ((p[1] & 0xc0) == 0x80)) { - p++; - p[-1] = *p; - } - *p = '\0'; - c1 = checkword(candidate,strlen(candidate), cpdsuggest, NULL, NULL); - if (c1) { - c2 = checkword((p+1),strlen(p+1), cpdsuggest, NULL, NULL); - if (c2) { - *p = ' '; - - // spec. Hungarian code (need a better compound word support) - if ((pAMgr->get_langnum() == LANG_hu) && !forbidden && - // if 3 repeating letter, use - instead of space - (((p[-1] == p[1]) && (((p>candidate+1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) || - // or multiple compounding, with more, than 6 syllables - ((c1 == 3) && (c2 >= 2)))) *p = '-'; - - cwrd = 1; - for (int k=0; k < ns; k++) - if (strcmp(candidate,wlst[k]) == 0) cwrd = 0; - if (ns < maxSug) { - if (cwrd) { - wlst[ns] = mystrdup(candidate); - if (wlst[ns] == NULL) return -1; - ns++; - } - } else return ns; - } - } - } - return ns; -} - - -// error is adjacent letter were swapped -int SuggestMgr::swapchar(char ** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - char * p; - char tmpc; - int wl=strlen(word); - // try swapping adjacent chars one by one - strcpy(candidate, word); - for (p = candidate; p[1] != 0; p++) { - tmpc = *p; - *p = p[1]; - p[1] = tmpc; - ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - p[1] = *p; - *p = tmpc; - } - return ns; -} - -// error is adjacent letter were swapped -int SuggestMgr::swapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) -{ - w_char candidate_utf[MAXSWL]; - char candidate[MAXSWUTF8L]; - w_char * p; - w_char tmpc; - // try swapping adjacent chars one by one - memcpy (candidate_utf, word, wl * sizeof(w_char)); - for (p = candidate_utf; p < (candidate_utf + wl - 1); p++) { - tmpc = *p; - *p = p[1]; - p[1] = tmpc; - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); - ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - p[1] = *p; - *p = tmpc; - } - return ns; -} - -// error is not adjacent letter were swapped -int SuggestMgr::longswapchar(char ** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - char * p; - char * q; - char tmpc; - int wl=strlen(word); - // try swapping not adjacent chars one by one - strcpy(candidate, word); - for (p = candidate; *p != 0; p++) { - for (q = candidate; *q != 0; q++) { - if (abs(p-q) > 1) { - tmpc = *p; - *p = *q; - *q = tmpc; - ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - *q = *p; - *p = tmpc; - } - } - } - return ns; -} - - -// error is adjacent letter were swapped -int SuggestMgr::longswapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) -{ - w_char candidate_utf[MAXSWL]; - char candidate[MAXSWUTF8L]; - w_char * p; - w_char * q; - w_char tmpc; - // try swapping not adjacent chars - memcpy (candidate_utf, word, wl * sizeof(w_char)); - for (p = candidate_utf; p < (candidate_utf + wl); p++) { - for (q = candidate_utf; q < (candidate_utf + wl); q++) { - if (abs(p-q) > 1) { - tmpc = *p; - *p = *q; - *q = tmpc; - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); - ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - *q = *p; - *p = tmpc; - } - } - } - return ns; -} - -// error is a letter was moved -int SuggestMgr::movechar(char ** wlst, const char * word, int ns, int cpdsuggest) -{ - char candidate[MAXSWUTF8L]; - char * p; - char * q; - char tmpc; - - int wl=strlen(word); - // try moving a char - strcpy(candidate, word); - for (p = candidate; *p != 0; p++) { - for (q = p + 1; (*q != 0) && ((q - p) < 10); q++) { - tmpc = *(q-1); - *(q-1) = *q; - *q = tmpc; - if ((q-p) < 2) continue; // omit swap char - ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - } - strcpy(candidate, word); - } - for (p = candidate + wl - 1; p > candidate; p--) { - for (q = p - 1; (q >= candidate) && ((p - q) < 10); q--) { - tmpc = *(q+1); - *(q+1) = *q; - *q = tmpc; - if ((p-q) < 2) continue; // omit swap char - ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - } - strcpy(candidate, word); - } - return ns; -} - -// error is a letter was moved -int SuggestMgr::movechar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest) -{ - w_char candidate_utf[MAXSWL]; - char candidate[MAXSWUTF8L]; - w_char * p; - w_char * q; - w_char tmpc; - // try moving a char - memcpy (candidate_utf, word, wl * sizeof(w_char)); - for (p = candidate_utf; p < (candidate_utf + wl); p++) { - for (q = p + 1; (q < (candidate_utf + wl)) && ((q - p) < 10); q++) { - tmpc = *(q-1); - *(q-1) = *q; - *q = tmpc; - if ((q-p) < 2) continue; // omit swap char - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); - ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - } - memcpy (candidate_utf, word, wl * sizeof(w_char)); - } - for (p = candidate_utf + wl - 1; p > candidate_utf; p--) { - for (q = p - 1; (q >= candidate_utf) && ((p - q) < 10); q--) { - tmpc = *(q+1); - *(q+1) = *q; - *q = tmpc; - if ((p-q) < 2) continue; // omit swap char - u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl); - ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL); - if (ns == -1) return -1; - } - memcpy (candidate_utf, word, wl * sizeof(w_char)); - } - return ns; -} - -// generate a set of suggestions for very poorly spelled words -int SuggestMgr::ngsuggest(char** wlst, char * w, HashMgr* pHMgr) -{ - - int i, j; - int lval; - int sc; - int lp; - int nonbmp = 0; - - if (!pHMgr) return 0; - - // exhaustively search through all root words - // keeping track of the MAX_ROOTS most similar root words - struct hentry * roots[MAX_ROOTS]; - int scores[MAX_ROOTS]; - for (i = 0; i < MAX_ROOTS; i++) { - roots[i] = NULL; - scores[i] = -100 * i; - } - lp = MAX_ROOTS - 1; - - char w2[MAXWORDUTF8LEN]; - char * word = w; - - // word reversing wrapper for complex prefixes - if (complexprefixes) { - strcpy(w2, w); - if (utf8) reverseword_utf(w2); else reverseword(w2); - word = w2; - } - - char mw[MAXSWUTF8L]; - w_char u8[MAXSWL]; - int nc = strlen(word); - int n = (utf8) ? u8_u16(u8, MAXSWL, word) : nc; - - // set character based ngram suggestion for words with non-BMP Unicode characters - if (n == -1) { - utf8 = 0; - n = nc; - nonbmp = 1; - } - - struct hentry* hp = NULL; - int col = -1; - while ((hp = pHMgr->walk_hashtable(col, hp))) { - if ((hp->astr) && (pAMgr) && - (TESTAFF(hp->astr, pAMgr->get_forbiddenword(), hp->alen) || - TESTAFF(hp->astr, pAMgr->get_nosuggest(), hp->alen) || - TESTAFF(hp->astr, pAMgr->get_onlyincompound(), hp->alen))) continue; - sc = ngram(3, word, hp->word, NGRAM_LONGER_WORSE); - if (sc > scores[lp]) { - scores[lp] = sc; - roots[lp] = hp; - lval = sc; - for (j=0; j < MAX_ROOTS; j++) - if (scores[j] < lval) { - lp = j; - lval = scores[j]; - } - } - } - - // find minimum threshhold for a passable suggestion - // mangle original word three differnt ways - // and score them to generate a minimum acceptable score - int thresh = 0; - for (int sp = 1; sp < 4; sp++) { - if (utf8) { - for (int k=sp; k < n; k+=4) *((unsigned short *) u8 + k) = '*'; - u16_u8(mw, MAXSWUTF8L, u8, n); - thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH); - } else { - strcpy(mw, word); - for (int k=sp; k < n; k+=4) *(mw + k) = '*'; - thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH); - } - } - thresh = thresh / 3; - thresh--; - - // now expand affixes on each of these root words and - // and use length adjusted ngram scores to select - // possible suggestions - char * guess[MAX_GUESS]; - int gscore[MAX_GUESS]; - for(i=0;i<MAX_GUESS;i++) { - guess[i] = NULL; - gscore[i] = -100 * i; - } - - lp = MAX_GUESS - 1; - - struct guessword * glst; - glst = (struct guessword *) calloc(MAX_WORDS,sizeof(struct guessword)); - if (! glst) { - if (nonbmp) utf8 = 1; - return 0; - } - - for (i = 0; i < MAX_ROOTS; i++) { - - if (roots[i]) { - struct hentry * rp = roots[i]; - int nw = pAMgr->expand_rootword(glst, MAX_WORDS, rp->word, rp->wlen, - rp->astr, rp->alen, word, nc); - - for (int k = 0; k < nw ; k++) { - sc = ngram(n, word, glst[k].word, NGRAM_ANY_MISMATCH); - if ((sc > thresh)) { - if (sc > gscore[lp]) { - if (guess[lp]) free (guess[lp]); - gscore[lp] = sc; - guess[lp] = glst[k].word; - lval = sc; - for (j=0; j < MAX_GUESS; j++) - if (gscore[j] < lval) { - lp = j; - lval = gscore[j]; - } - } else free (glst[k].word); - } else free(glst[k].word); - } - } - } - free(glst); - - // now we are done generating guesses - // sort in order of decreasing score - - bubblesort(&guess[0], &gscore[0], MAX_GUESS); - - // weight suggestions with a similarity index, based on - // the longest common subsequent algorithm and resort - - int is_swap; - for (i=0; i < MAX_GUESS; i++) { - if (guess[i]) { - // lowering guess[i] - char gl[MAXSWUTF8L]; - int len; - if (utf8) { - w_char _w[MAXSWL]; - len = u8_u16(_w, MAXSWL, guess[i]); - mkallsmall_utf(_w, len, pAMgr->get_langnum()); - u16_u8(gl, MAXSWUTF8L, _w, len); - } else { - strcpy(gl, guess[i]); - mkallsmall(gl, csconv); - len = strlen(guess[i]); - } - - int _lcs = lcslen(word, gl); - - // same characters with different casing - if ((n == len) && (n == _lcs)) { - gscore[i] += 2000; - break; - } - - // heuristic weigthing of ngram scores - gscore[i] += - // length of longest common subsequent minus lenght difference - 2 * _lcs - abs((int) (n - len)) + - // weight equal first letter - equalfirstletter(word, gl) + - // weight equal character positions - ((_lcs == commoncharacterpositions(word, gl, &is_swap)) ? 1: 0) + - // swap character (not neighboring) - ((is_swap) ? 1000 : 0); - } - } - - bubblesort(&guess[0], &gscore[0], MAX_GUESS); - - // copy over - - int ns = 0; - int same = 0; - for (i=0; i < MAX_GUESS; i++) { - if (guess[i]) { - if ((ns < maxngramsugs) && (ns < maxSug) && (!same || (gscore[i] > 1000))) { - int unique = 1; - // we have excellent suggestion(s) - if (gscore[i] > 1000) same = 1; - for (j=0; j < ns; j++) - // don't suggest previous suggestions or a previous suggestion with prefixes or affixes - if (strstr(guess[i], wlst[j]) || - // check forbidden words - !checkword(guess[i], strlen(guess[i]), 0, NULL, NULL)) unique = 0; - if (unique) wlst[ns++] = guess[i]; else free(guess[i]); - } else free(guess[i]); - } - } - - if (nonbmp) utf8 = 1; - return ns; -} - - -// see if a candidate suggestion is spelled correctly -// needs to check both root words and words with affixes - -// obsolote MySpell-HU modifications: -// return value 2 and 3 marks compounding with hyphen (-) -// `3' marks roots without suffix -int SuggestMgr::checkword(const char * word, int len, int cpdsuggest, int * timer, time_t * timelimit) -{ - struct hentry * rv=NULL; - int nosuffix = 0; - - // check time limit - if (timer) { - (*timer)--; - if (!(*timer) && timelimit) { - if (time(NULL) > *timelimit) { - *timelimit = 0; - return 0; - } - *timer = MAXPLUSTIMER; - } - } - - if (pAMgr) { - if (cpdsuggest==1) { - if (pAMgr->get_compound()) { - rv = pAMgr->compound_check(word,len,0,0,0,0,NULL,0,NULL,NULL,1); - if (rv) return 3; // XXX obsolote categorisation - } - return 0; - } - - rv = pAMgr->lookup(word); - - if (rv) { - if ((rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen) - || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen))) return 0; - if (rv->astr && (TESTAFF(rv->astr,pAMgr->get_pseudoroot(),rv->alen) || - TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL; - } else rv = pAMgr->prefix_check(word, len, 0); // only prefix, and prefix + suffix XXX - - if (rv) { - nosuffix=1; - } else { - rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, NULL); // only suffix - } - - if (!rv && pAMgr->have_contclass()) { - rv = pAMgr->suffix_check_twosfx(word, len, 0, NULL, FLAG_NULL); - if (!rv) rv = pAMgr->prefix_check_twosfx(word, len, 1, FLAG_NULL); - } - - // check forbidden words - if ((rv) && (rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen) - || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen) || - TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) return 0; - - if (rv) { // XXX obsolote - if ((pAMgr->get_compoundflag()) && - TESTAFF(rv->astr, pAMgr->get_compoundflag(), rv->alen)) return 2 + nosuffix; - return 1; - } - } - return 0; -} - -int SuggestMgr::check_forbidden(const char * word, int len) -{ - struct hentry * rv = NULL; - - if (pAMgr) { - rv = pAMgr->lookup(word); - if (rv && rv->astr && (TESTAFF(rv->astr,pAMgr->get_pseudoroot(),rv->alen) || - TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL; - if (!(pAMgr->prefix_check(word,len,1))) - rv = pAMgr->suffix_check(word,len, 0, NULL, NULL, 0, NULL); // prefix+suffix, suffix - // check forbidden words - if ((rv) && (rv->astr) && TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)) return 1; - } - return 0; -} - -#ifdef HUNSPELL_EXPERIMENTAL -// suggest stems, XXX experimental code -int SuggestMgr::suggest_stems(char*** slst, const char * w, int nsug) -{ - char buf[MAXSWUTF8L]; - char ** wlst; - int prevnsug = nsug; - - char w2[MAXWORDUTF8LEN]; - const char * word = w; - - // word reversing wrapper for complex prefixes - if (complexprefixes) { - strcpy(w2, w); - if (utf8) reverseword_utf(w2); else reverseword(w2); - word = w2; - } - - if (*slst) { - wlst = *slst; - } else { - wlst = (char **) calloc(maxSug, sizeof(char *)); - if (wlst == NULL) return -1; - } - // perhaps there are a fix stem in the dictionary - if ((nsug < maxSug) && (nsug > -1)) { - - nsug = fixstems(wlst, word, nsug); - if (nsug == prevnsug) { - char * s = mystrdup(word); - char * p = s + strlen(s); - while ((*p != '-') && (p != s)) p--; - if (*p == '-') { - *p = '\0'; - nsug = fixstems(wlst, s, nsug); - if ((nsug == prevnsug) && (nsug < maxSug) && (nsug >= 0)) { - char * t; - buf[0] = '\0'; - for (t = s; (t[0] != '\0') && ((t[0] >= '0') || (t[0] <= '9')); t++); // is a number? - if (*t != '\0') strcpy(buf, "# "); - strcat(buf, s); - wlst[nsug] = mystrdup(buf); - if (wlst[nsug] == NULL) return -1; - nsug++; - } - p++; - nsug = fixstems(wlst, p, nsug); - } - - free(s); - } - } - - if (nsug < 0) { - for (int i=0;i<maxSug; i++) - if (wlst[i] != NULL) free(wlst[i]); - free(wlst); - return -1; - } - - *slst = wlst; - return nsug; -} - - -// there are fix stems in dictionary -int SuggestMgr::fixstems(char ** wlst, const char * word, int ns) -{ - char buf[MAXSWUTF8L]; - char prefix[MAXSWUTF8L] = ""; - - int dicstem = 1; // 0 = lookup, 1= affix, 2 = compound - int cpdindex = 0; - struct hentry * rv = NULL; - - int wl = strlen(word); - int cmpdstemnum; - int cmpdstem[MAXCOMPOUND]; - - if (pAMgr) { - rv = pAMgr->lookup(word); - if (rv) { - dicstem = 0; - } else { - // try stripping off affixes - rv = pAMgr->affix_check(word, wl); - - // else try check compound word - if (!rv && pAMgr->get_compound()) { - rv = pAMgr->compound_check(word, wl, - 0, 0, 100, 0, NULL, 0, &cmpdstemnum, cmpdstem,1); - - if (rv) { - dicstem = 2; - for (int j = 0; j < cmpdstemnum; j++) { - cpdindex += cmpdstem[j]; - } - if(! (pAMgr->lookup(word + cpdindex))) - pAMgr->affix_check(word + cpdindex, wl - cpdindex); // for prefix - } - } - - - if (pAMgr->get_prefix()) { - strcpy(prefix, pAMgr->get_prefix()); - } - - // XXX obsolete, will be a general solution for stemming - if ((prefix) && (strncmp(prefix, "leg", 3)==0)) prefix[0] = '\0'; // (HU) - } - - } - - - - if ((rv) && (ns < maxSug)) { - - // check fixstem flag and not_valid_stem flag - // first word - if ((ns < maxSug) && (dicstem < 2)) { - strcpy(buf, prefix); - if ((dicstem > 0) && pAMgr->get_derived()) { - // XXX obsolote - if (strlen(prefix) == 1) { - strcat(buf, (pAMgr->get_derived()) + 1); - } else { - strcat(buf, pAMgr->get_derived()); - } - } else { - // special stem in affix description - const char * wordchars = pAMgr->get_wordchars(); - if (rv->description && - (strchr(wordchars, *(rv->description)))) { - char * desc = (rv->description) + 1; - while (strchr(wordchars, *desc)) desc++; - strncat(buf, rv->description, desc - (rv->description)); - } else { - strcat(buf, rv->word); - } - } - wlst[ns] = mystrdup(buf); - if (wlst[ns] == NULL) return -1; - ns++; - } - - if (dicstem == 2) { - - // compound stem - -// if (rv->astr && (strchr(rv->astr, '0') == NULL)) { - if (rv->astr) { - strcpy(buf, word); - buf[cpdindex] = '\0'; - if (prefix) strcat(buf, prefix); - if (pAMgr->get_derived()) { - strcat(buf, pAMgr->get_derived()); - } else { - // special stem in affix description - const char * wordchars = pAMgr->get_wordchars(); - if (rv->description && - (strchr(wordchars, *(rv->description)))) { - char * desc = (rv->description) + 1; - while (strchr(wordchars, *desc)) desc++; - strncat(buf, rv->description, desc - (rv->description)); - } else { - strcat(buf, rv->word); - } - } - if (ns < maxSug) { - wlst[ns] = mystrdup(buf); - if (wlst[ns] == NULL) return -1; - ns++; - } - } - } - } - return ns; -} - -// suggest possible stems -int SuggestMgr::suggest_pos_stems(char*** slst, const char * w, int nsug) -{ - char ** wlst; - - struct hentry * rv = NULL; - - char w2[MAXSWUTF8L]; - const char * word = w; - - // word reversing wrapper for complex prefixes - if (complexprefixes) { - strcpy(w2, w); - if (utf8) reverseword_utf(w2); else reverseword(w2); - word = w2; - } - - int wl = strlen(word); - - - if (*slst) { - wlst = *slst; - } else { - wlst = (char **) calloc(maxSug, sizeof(char *)); - if (wlst == NULL) return -1; - } - - rv = pAMgr->suffix_check(word, wl, 0, NULL, wlst, maxSug, &nsug); - - // delete dash from end of word - if (nsug > 0) { - for (int j=0; j < nsug; j++) { - if (wlst[j][strlen(wlst[j]) - 1] == '-') wlst[j][strlen(wlst[j]) - 1] = '\0'; - } - } - - *slst = wlst; - return nsug; -} - - -char * SuggestMgr::suggest_morph(const char * w) -{ - char result[MAXLNLEN]; - char * r = (char *) result; - char * st; - - struct hentry * rv = NULL; - - *result = '\0'; - - if (! pAMgr) return NULL; - - char w2[MAXSWUTF8L]; - const char * word = w; - - // word reversing wrapper for complex prefixes - if (complexprefixes) { - strcpy(w2, w); - if (utf8) reverseword_utf(w2); else reverseword(w2); - word = w2; - } - - rv = pAMgr->lookup(word); - - while (rv) { - if ((!rv->astr) || !(TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen) || - TESTAFF(rv->astr, pAMgr->get_pseudoroot(), rv->alen) || - TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) { - if (rv->description && ((!rv->astr) || - !TESTAFF(rv->astr, pAMgr->get_lemma_present(), rv->alen))) - strcat(result, word); - if (rv->description) strcat(result, rv->description); - strcat(result, "\n"); - } - rv = rv->next_homonym; - } - - st = pAMgr->affix_check_morph(word,strlen(word)); - if (st) { - strcat(result, st); - free(st); - } - - if (pAMgr->get_compound() && (*result == '\0')) - pAMgr->compound_check_morph(word, strlen(word), - 0, 0, 100, 0,NULL, 0, &r, NULL); - - return (*result) ? mystrdup(line_uniq(delete_zeros(result))) : NULL; -} - -char * SuggestMgr::suggest_morph_for_spelling_error(const char * word) -{ - char * p = NULL; - char ** wlst = (char **) calloc(maxSug, sizeof(char *)); - // we will use only the first suggestion - for (int i = 0; i < maxSug - 1; i++) wlst[i] = ""; - int ns = suggest(&wlst, word, maxSug - 1); - if (ns == maxSug) { - p = suggest_morph(wlst[maxSug - 1]); - free(wlst[maxSug - 1]); - } - if (wlst) free(wlst); - return p; -} -#endif // END OF HUNSPELL_EXPERIMENTAL CODE - - -// generate an n-gram score comparing s1 and s2 -int SuggestMgr::ngram(int n, char * s1, const char * s2, int uselen) -{ - int nscore = 0; - int ns; - int l1; - int l2; - - if (utf8) { - w_char su1[MAXSWL]; - w_char su2[MAXSWL]; - l1 = u8_u16(su1, MAXSWL, s1); - l2 = u8_u16(su2, MAXSWL, s2); - if (!l2 || (l1==-1) || (l2==-1)) return 0; - // decapitalize dictionary word - if (complexprefixes) { - mkallsmall_utf(su2+l2-1, 1, pAMgr->get_langnum()); - } else { - mkallsmall_utf(su2, 1, pAMgr->get_langnum()); - } - for (int j = 1; j <= n; j++) { - ns = 0; - for (int i = 0; i <= (l1-j); i++) { - for (int l = 0; l <= (l2-j); l++) { - int k; - for (k = 0; (k < j); k++) { - w_char * c1 = su1 + i + k; - w_char * c2 = su2 + l + k; - if ((c1->l != c2->l) || (c1->h != c2->h)) break; - } - if (k == j) { - ns++; - break; - } - } - } - nscore = nscore + ns; - if (ns < 2) break; - } - } else { - char t[MAXSWUTF8L]; - l1 = strlen(s1); - l2 = strlen(s2); - if (!l2) return 0; - strcpy(t, s2); - if (complexprefixes) { - *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower; - } else { - mkallsmall(t, csconv); - } - for (int j = 1; j <= n; j++) { - ns = 0; - for (int i = 0; i <= (l1-j); i++) { - char c = *(s1 + i + j); - *(s1 + i + j) = '\0'; - if (strstr(t,(s1+i))) ns++; - *(s1 + i + j ) = c; - } - nscore = nscore + ns; - if (ns < 2) break; - } - } - - ns = 0; - if (uselen == NGRAM_LONGER_WORSE) ns = (l2-l1)-2; - if (uselen == NGRAM_ANY_MISMATCH) ns = abs(l2-l1)-2; - ns = (nscore - ((ns > 0) ? ns : 0)); - return ns; -} - -int SuggestMgr::equalfirstletter(char * s1, const char * s2) { - if (utf8) { - w_char su1[MAXSWL]; - w_char su2[MAXSWL]; - // decapitalize dictionary word - if (complexprefixes) { - int l1 = u8_u16(su1, MAXSWL, s1); - int l2 = u8_u16(su2, MAXSWL, s2); - if (*((short *)su1+l1-1) == *((short *)su2+l2-1)) return 1; - } else { - u8_u16(su1, 1, s1); - u8_u16(su2, 1, s2); - if (*((short *)su1) == *((short *)su2)) return 1; - } - } else { - if (complexprefixes) { - int l1 = strlen(s1); - int l2 = strlen(s2); - if (*(s2+l1-1) == *(s2+l2-1)) return 1; - } else { - if (*s1 == *s2) return 1; - } - } - return 0; -} - -int SuggestMgr::commoncharacterpositions(char * s1, const char * s2, int * is_swap) { - int num = 0; - int diff = 0; - int diffpos[2]; - *is_swap = 0; - if (utf8) { - w_char su1[MAXSWL]; - w_char su2[MAXSWL]; - int l1 = u8_u16(su1, MAXSWL, s1); - int l2 = u8_u16(su2, MAXSWL, s2); - // decapitalize dictionary word - if (complexprefixes) { - mkallsmall_utf(su2+l2-1, 1, pAMgr->get_langnum()); - } else { - mkallsmall_utf(su2, 1, pAMgr->get_langnum()); - } - for (int i = 0; (i < l1) && (i < l2); i++) { - if (((short *) su1)[i] == ((short *) su2)[i]) { - num++; - } else { - if (diff < 2) diffpos[diff] = i; - diff++; - } - } - if ((diff == 2) && (l1 == l2) && - (((short *) su1)[diffpos[0]] == ((short *) su2)[diffpos[1]]) && - (((short *) su1)[diffpos[1]] == ((short *) su2)[diffpos[0]])) *is_swap = 1; - } else { - int i; - char t[MAXSWUTF8L]; - strcpy(t, s2); - // decapitalize dictionary word - if (complexprefixes) { - int l2 = strlen(t); - *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower; - } else { - mkallsmall(t, csconv); - } - for (i = 0; (*(s1+i) != 0) && (*(t+i) != 0); i++) { - if (*(s1+i) == *(t+i)) { - num++; - } else { - if (diff < 2) diffpos[diff] = i; - diff++; - } - } - if ((diff == 2) && (*(s1+i) == 0) && (*(t+i) == 0) && - (*(s1+diffpos[0]) == *(t+diffpos[1])) && - (*(s1+diffpos[1]) == *(t+diffpos[0]))) *is_swap = 1; - } - return num; -} - -int SuggestMgr::mystrlen(const char * word) { - if (utf8) { - w_char w[MAXSWL]; - return u8_u16(w, MAXSWL, word); - } else return strlen(word); -} - -// sort in decreasing order of score -void SuggestMgr::bubblesort(char** rword, int* rsc, int n ) -{ - int m = 1; - while (m < n) { - int j = m; - while (j > 0) { - if (rsc[j-1] < rsc[j]) { - int sctmp = rsc[j-1]; - char * wdtmp = rword[j-1]; - rsc[j-1] = rsc[j]; - rword[j-1] = rword[j]; - rsc[j] = sctmp; - rword[j] = wdtmp; - j--; - } else break; - } - m++; - } - return; -} - -// longest common subsequence -void SuggestMgr::lcs(const char * s, const char * s2, int * l1, int * l2, char ** result) { - int n, m; - w_char su[MAXSWL]; - w_char su2[MAXSWL]; - char * b; - char * c; - int i; - int j; - if (utf8) { - m = u8_u16(su, MAXSWL, s); - n = u8_u16(su2, MAXSWL, s2); - } else { - m = strlen(s); - n = strlen(s2); - } - c = (char *) malloc((m + 1) * (n + 1)); - b = (char *) malloc((m + 1) * (n + 1)); - for (i = 1; i <= m; i++) c[i*(n+1)] = 0; - for (j = 0; j <= n; j++) c[j] = 0; - for (i = 1; i <= m; i++) { - for (j = 1; j <= n; j++) { - if ((utf8) && (*((short *) su+i-1) == *((short *)su2+j-1)) - || (!utf8) && ((*(s+i-1)) == (*(s2+j-1)))) { - c[i*(n+1) + j] = c[(i-1)*(n+1) + j-1]+1; - b[i*(n+1) + j] = LCS_UPLEFT; - } else if (c[(i-1)*(n+1) + j] >= c[i*(n+1) + j-1]) { - c[i*(n+1) + j] = c[(i-1)*(n+1) + j]; - b[i*(n+1) + j] = LCS_UP; - } else { - c[i*(n+1) + j] = c[i*(n+1) + j-1]; - b[i*(n+1) + j] = LCS_LEFT; - } - } - } - *result = b; - free(c); - *l1 = m; - *l2 = n; -} - -int SuggestMgr::lcslen(const char * s, const char* s2) { - int m; - int n; - int i; - int j; - char * result; - int len = 0; - lcs(s, s2, &m, &n, &result); - i = m; - j = n; - while ((i != 0) && (j != 0)) { - if (result[i*(n+1) + j] == LCS_UPLEFT) { - len++; - i--; - j--; - } else if (result[i*(n+1) + j] == LCS_UP) { - i--; - } else j--; - } - if (result) free(result); - return len; -} +#include "license.hunspell"
+#include "license.myspell"
+
+#ifndef MOZILLA_CLIENT
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+#include <cctype>
+#else
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+#endif
+
+#include "suggestmgr.hxx"
+
+#ifndef MOZILLA_CLIENT
+#ifndef W32
+using namespace std;
+#endif
+#endif
+
+
+SuggestMgr::SuggestMgr(const char * tryme, int maxn,
+ AffixMgr * aptr)
+{
+
+ // register affix manager and check in string of chars to
+ // try when building candidate suggestions
+ pAMgr = aptr;
+
+ ctryl = 0;
+ ctry = NULL;
+ ctry_utf = NULL;
+
+ maxSug = maxn;
+ nosplitsugs = 0;
+ maxngramsugs = MAXNGRAMSUGS;
+
+ utf8 = 0;
+ complexprefixes = 0;
+
+ if (pAMgr) {
+ char * enc = pAMgr->get_encoding();
+ csconv = get_current_cs(enc);
+ free(enc);
+ nosplitsugs = pAMgr->get_nosplitsugs();
+ if (pAMgr->get_maxngramsugs() >= 0) maxngramsugs = pAMgr->get_maxngramsugs();
+ utf8 = pAMgr->get_utf8();
+ complexprefixes = pAMgr->get_complexprefixes();
+ }
+
+ if (tryme) {
+ if (utf8) {
+ w_char t[MAXSWL];
+ ctryl = u8_u16(t, MAXSWL, tryme);
+ ctry_utf = (w_char *) malloc(ctryl * sizeof(w_char));
+ memcpy(ctry_utf, t, ctryl * sizeof(w_char));
+ } else {
+ ctry = mystrdup(tryme);
+ ctryl = strlen(ctry);
+ }
+ }
+}
+
+
+SuggestMgr::~SuggestMgr()
+{
+ pAMgr = NULL;
+ if (ctry) free(ctry);
+ ctry = NULL;
+ if (ctry_utf) free(ctry_utf);
+ ctry_utf = NULL;
+ ctryl = 0;
+ maxSug = 0;
+}
+
+int SuggestMgr::testsug(char** wlst, const char * candidate, int wl, int ns, int cpdsuggest,
+ int * timer, time_t * timelimit) {
+ int cwrd = 1;
+ if (ns == maxSug) return maxSug;
+ for (int k=0; k < ns; k++) {
+ if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;
+ }
+ if ((cwrd) && checkword(candidate, wl, cpdsuggest, timer, timelimit)) {
+ wlst[ns] = mystrdup(candidate);
+ if (wlst[ns] == NULL) {
+ for (int j=0; j<ns; j++) free(wlst[j]);
+ return -1;
+ }
+ ns++;
+ }
+ return ns;
+}
+
+// generate suggestions for a mispelled word
+// pass in address of array of char * pointers
+
+int SuggestMgr::suggest(char*** slst, const char * w, int nsug)
+{
+ int nocompoundtwowords = 0;
+ char ** wlst;
+ w_char word_utf[MAXSWL];
+ int wl = 0;
+
+ char w2[MAXWORDUTF8LEN];
+ const char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ if (*slst) {
+ wlst = *slst;
+ } else {
+ wlst = (char **) malloc(maxSug * sizeof(char *));
+ if (wlst == NULL) return -1;
+ for (int i = 0; i < maxSug; i++) {
+ wlst[i] = NULL;
+ }
+ }
+
+ if (utf8) {
+ wl = u8_u16(word_utf, MAXSWL, word);
+ }
+
+ for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {
+
+ // suggestions for an uppercase word (html -> HTML)
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? capchars_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ capchars(wlst, word, nsug, cpdsuggest);
+ }
+
+ // perhaps we made a typical fault of spelling
+ if ((nsug < maxSug) && (nsug > -1))
+ nsug = replchars(wlst, word, nsug, cpdsuggest);
+
+ // perhaps we made chose the wrong char from a related set
+ if ((nsug < maxSug) && (nsug > -1) && (cpdsuggest == 0)) {
+ nsug = mapchars(wlst, word, nsug);
+ }
+
+ // did we swap the order of chars by mistake
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? swapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ swapchar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we swap the order of non adjacent chars by mistake
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? longswapchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ longswapchar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we forgot a char
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? forgotchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ forgotchar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we move a char
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? movechar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ movechar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we add a char that should not be there
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? extrachar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ extrachar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we just hit the wrong key in place of a good char
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? badchar_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ badchar(wlst, word, nsug, cpdsuggest);
+ }
+
+ // did we double two characters
+ if ((nsug < maxSug) && (nsug > -1)) {
+ nsug = (utf8) ? doubletwochars_utf(wlst, word_utf, wl, nsug, cpdsuggest) :
+ doubletwochars(wlst, word, nsug, cpdsuggest);
+ }
+
+
+ // only suggest compound words when no other suggestion
+ if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1;
+
+ // perhaps we forgot to hit space and two words ran together
+ if ((!nosplitsugs) && (nsug < maxSug) && (nsug > -1)) {
+ nsug = twowords(wlst, word, nsug, cpdsuggest);
+ }
+
+ } // repeating ``for'' statement compounding support
+
+ if (nsug < 0) {
+ // we ran out of memory - we should free up as much as possible
+ for (int i = 0; i < maxSug; i++)
+ if (wlst[i] != NULL) free(wlst[i]);
+ free(wlst);
+ wlst = NULL;
+ }
+
+ *slst = wlst;
+ return nsug;
+}
+
+// generate suggestions for a word with typical mistake
+// pass in address of array of char * pointers
+#ifdef HUNSPELL_EXPERIMENTAL
+int SuggestMgr::suggest_auto(char*** slst, const char * w, int nsug)
+{
+ int nocompoundtwowords = 0;
+ char ** wlst;
+
+ char w2[MAXWORDUTF8LEN];
+ const char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ if (*slst) {
+ wlst = *slst;
+ } else {
+ wlst = (char **) malloc(maxSug * sizeof(char *));
+ if (wlst == NULL) return -1;
+ }
+
+ for (int cpdsuggest=0; (cpdsuggest<2) && (nocompoundtwowords==0); cpdsuggest++) {
+
+ // perhaps we made a typical fault of spelling
+ if ((nsug < maxSug) && (nsug > -1))
+ nsug = replchars(wlst, word, nsug, cpdsuggest);
+
+ // perhaps we made chose the wrong char from a related set
+ if ((nsug < maxSug) && (nsug > -1) && (cpdsuggest == 0))
+ nsug = mapchars(wlst, word, nsug);
+
+ if ((cpdsuggest==0) && (nsug>0)) nocompoundtwowords=1;
+
+ // perhaps we forgot to hit space and two words ran together
+
+ if ((nsug < maxSug) && (nsug > -1) && check_forbidden(word, strlen(word))) {
+ nsug = twowords(wlst, word, nsug, cpdsuggest);
+ }
+
+ } // repeating ``for'' statement compounding support
+
+ if (nsug < 0) {
+ for (int i=0;i<maxSug; i++)
+ if (wlst[i] != NULL) free(wlst[i]);
+ free(wlst);
+ return -1;
+ }
+
+ *slst = wlst;
+ return nsug;
+}
+#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+// suggestions for an uppercase word (html -> HTML)
+int SuggestMgr::capchars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ w_char candidate_utf[MAXSWL];
+ memcpy(candidate_utf, word, wl * sizeof(w_char));
+ mkallcap_utf(candidate_utf, wl, pAMgr->get_langnum());
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+}
+
+// suggestions for an uppercase word (html -> HTML)
+int SuggestMgr::capchars(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ strcpy(candidate, word);
+ mkallcap(candidate, csconv);
+ return testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+}
+
+// suggestions for when chose the wrong char out of a related set
+int SuggestMgr::mapchars(char** wlst, const char * word, int ns)
+{
+ time_t timelimit;
+ int timer;
+
+ int wl = strlen(word);
+ if (wl < 2 || ! pAMgr) return ns;
+
+ int nummap = pAMgr->get_nummap();
+ struct mapentry* maptable = pAMgr->get_maptable();
+ if (maptable==NULL) return ns;
+
+ timelimit = time(NULL);
+ timer = MINTIMER;
+ if (utf8) {
+ w_char w[MAXSWL];
+ int len = u8_u16(w, MAXSWL, word);
+ ns = map_related_utf(w, len, 0, wlst, ns, maptable, nummap, &timer, &timelimit);
+ } else ns = map_related(word, 0, wlst, ns, maptable, nummap, &timer, &timelimit);
+ return ns;
+}
+
+int SuggestMgr::map_related(const char * word, int i, char** wlst, int ns,
+ const mapentry* maptable, int nummap, int * timer, time_t * timelimit)
+{
+ char c = *(word + i);
+ if (c == 0) {
+ int cwrd = 1;
+ int wl = strlen(word);
+ for (int m=0; m < ns; m++)
+ if (strcmp(word,wlst[m]) == 0) cwrd = 0;
+ if ((cwrd) && (checkword(word, wl, 0, timer, timelimit) ||
+ checkword(word, wl, 1, timer, timelimit))) {
+ if (ns < maxSug) {
+ wlst[ns] = mystrdup(word);
+ if (wlst[ns] == NULL) return -1;
+ ns++;
+ }
+ }
+ return ns;
+ }
+ int in_map = 0;
+ for (int j = 0; j < nummap; j++) {
+ if (strchr(maptable[j].set,c) != 0) {
+ in_map = 1;
+ char * newword = mystrdup(word);
+ for (int k = 0; k < maptable[j].len; k++) {
+ *(newword + i) = *(maptable[j].set + k);
+ ns = map_related(newword, (i+1), wlst, ns, maptable, nummap, timer, timelimit);
+ if (!(*timelimit)) return ns;
+ }
+ free(newword);
+ }
+ }
+ if (!in_map) {
+ i++;
+ ns = map_related(word, i, wlst, ns, maptable, nummap, timer, timelimit);
+ }
+ return ns;
+}
+
+int SuggestMgr::map_related_utf(w_char * word, int len, int i, char** wlst, int ns,
+ const mapentry* maptable, int nummap, int * timer, time_t * timelimit)
+{
+ if (i == len) {
+ int cwrd = 1;
+ int wl;
+ char s[MAXSWUTF8L];
+ u16_u8(s, MAXSWUTF8L, word, len);
+ wl = strlen(s);
+ for (int m=0; m < ns; m++)
+ if (strcmp(s,wlst[m]) == 0) cwrd = 0;
+ if ((cwrd) && (checkword(s, wl, 0, timer, timelimit) ||
+ checkword(s, wl, 1, timer, timelimit))) {
+ if (ns < maxSug) {
+ wlst[ns] = mystrdup(s);
+ if (wlst[ns] == NULL) return -1;
+ ns++;
+ }
+ }
+ return ns;
+ }
+ int in_map = 0;
+ unsigned short c = *((unsigned short *) word + i);
+ for (int j = 0; j < nummap; j++) {
+ if (flag_bsearch((unsigned short *) maptable[j].set_utf16, c, maptable[j].len)) {
+ in_map = 1;
+ for (int k = 0; k < maptable[j].len; k++) {
+ *(word + i) = *(maptable[j].set_utf16 + k);
+ ns = map_related_utf(word, len, i + 1, wlst, ns, maptable, nummap, timer, timelimit);
+ if (!(*timelimit)) return ns;
+ }
+ *((unsigned short *) word + i) = c;
+ }
+ }
+ if (!in_map) {
+ i++;
+ ns = map_related_utf(word, len, i, wlst, ns, maptable, nummap, timer, timelimit);
+ }
+ return ns;
+}
+
+
+
+// suggestions for a typical fault of spelling, that
+// differs with more, than 1 letter from the right form.
+int SuggestMgr::replchars(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ const char * r;
+ int lenr, lenp;
+ int wl = strlen(word);
+ if (wl < 2 || ! pAMgr) return ns;
+ int numrep = pAMgr->get_numrep();
+ struct replentry* reptable = pAMgr->get_reptable();
+ if (reptable==NULL) return ns;
+ for (int i=0; i < numrep; i++ ) {
+ r = word;
+ lenr = strlen(reptable[i].pattern2);
+ lenp = strlen(reptable[i].pattern);
+ // search every occurence of the pattern in the word
+ while ((r=strstr(r, reptable[i].pattern)) != NULL) {
+ strcpy(candidate, word);
+ if (r-word + lenr + strlen(r+lenp) >= MAXSWUTF8L) break;
+ strcpy(candidate+(r-word),reptable[i].pattern2);
+ strcpy(candidate+(r-word)+lenr, r+lenp);
+ ns = testsug(wlst, candidate, wl-lenp+lenr, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ r++; // search for the next letter
+ }
+ }
+ return ns;
+}
+
+// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation)
+int SuggestMgr::doubletwochars(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ int state=0;
+ int wl = strlen(word);
+ if (wl < 5 || ! pAMgr) return ns;
+ for (int i=2; i < wl; i++ ) {
+ if (word[i]==word[i-2]) {
+ state++;
+ if (state==3) {
+ strcpy(candidate,word);
+ strcpy(candidate+i-1,word+i+1);
+ ns = testsug(wlst, candidate, wl-2, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ state=0;
+ }
+ } else {
+ state=0;
+ }
+ }
+ return ns;
+}
+
+// perhaps we doubled two characters (pattern aba -> ababa, for example vacation -> vacacation)
+int SuggestMgr::doubletwochars_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ int state=0;
+ if (wl < 5 || ! pAMgr) return ns;
+ for (int i=2; i < wl; i++) {
+ if ((word[i].l==word[i-2].l) && (word[i].h==word[i-2].h)) {
+ state++;
+ if (state==3) {
+ memcpy(candidate_utf, word, (i - 1) * sizeof(w_char));
+ memcpy(candidate_utf+i-1, word+i+1, (wl-i-1) * sizeof(w_char));
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl-2);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ state=0;
+ }
+ } else {
+ state=0;
+ }
+ }
+ return ns;
+}
+
+// error is wrong char in place of correct one
+int SuggestMgr::badchar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char tmpc;
+ char candidate[MAXSWUTF8L];
+ time_t timelimit = time(NULL);
+ int timer = MINTIMER;
+ int wl = strlen(word);
+ strcpy(candidate, word);
+ // swap out each char one by one and try all the tryme
+ // chars in its place to see if that makes a good word
+ for (int i=0; i < wl; i++) {
+ tmpc = candidate[i];
+ for (int j=0; j < ctryl; j++) {
+ if (ctry[j] == tmpc) continue;
+ candidate[i] = ctry[j];
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, &timer, &timelimit);
+ if (ns == -1) return -1;
+ if (!timelimit) return ns;
+ candidate[i] = tmpc;
+ }
+ }
+ return ns;
+}
+
+// error is wrong char in place of correct one
+int SuggestMgr::badchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char tmpc;
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ time_t timelimit = time(NULL);
+ int timer = MINTIMER;
+ memcpy(candidate_utf, word, wl * sizeof(w_char));
+ // swap out each char one by one and try all the tryme
+ // chars in its place to see if that makes a good word
+ for (int i=0; i < wl; i++) {
+ tmpc = candidate_utf[i];
+ for (int j=0; j < ctryl; j++) {
+ if ((ctry_utf[j].l == tmpc.l) && (ctry_utf[j].h == tmpc.h)) continue;
+ candidate_utf[i] = ctry_utf[j];
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);
+ if (ns == -1) return -1;
+ if (!timelimit) return ns;
+ candidate_utf[i] = tmpc;
+ }
+ }
+ return ns;
+}
+
+// error is word has an extra letter it does not need
+int SuggestMgr::extrachar_utf(char** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ w_char candidate_utf[MAXSWL];
+ const w_char * p;
+ w_char * r;
+ if (wl < 2) return ns;
+ // try omitting one char of word at a time
+ memcpy(candidate_utf, word + 1, (wl - 1) * sizeof(w_char));
+ for (p = word, r = candidate_utf; p < word + wl; ) {
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl - 1);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ *r++ = *p++;
+ }
+ return ns;
+}
+
+// error is word has an extra letter it does not need
+int SuggestMgr::extrachar(char** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ const char * p;
+ char * r;
+ int wl = strlen(word);
+ if (wl < 2) return ns;
+ // try omitting one char of word at a time
+ strcpy (candidate, word + 1);
+ for (p = word, r = candidate; *p != 0; ) {
+ ns = testsug(wlst, candidate, wl-1, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ *r++ = *p++;
+ }
+ return ns;
+}
+
+
+// error is missing a letter it needs
+int SuggestMgr::forgotchar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ const char * p;
+ char * q;
+ time_t timelimit = time(NULL);
+ int timer = MINTIMER;
+ int wl = strlen(word);
+ // try inserting a tryme character before every letter
+ strcpy(candidate + 1, word);
+ for (p = word, q = candidate; *p != 0; ) {
+ for (int i = 0; i < ctryl; i++) {
+ *q = ctry[i];
+ ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, &timer, &timelimit);
+ if (ns == -1) return -1;
+ if (!timelimit) return ns;
+ }
+ *q++ = *p++;
+ }
+ // now try adding one to end */
+ for (int i = 0; i < ctryl; i++) {
+ *q = ctry[i];
+ ns = testsug(wlst, candidate, wl+1, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ return ns;
+}
+
+// error is missing a letter it needs
+int SuggestMgr::forgotchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ const w_char * p;
+ w_char * q;
+ int cwrd;
+ time_t timelimit = time(NULL);
+ int timer = MINTIMER;
+ // try inserting a tryme character before every letter
+ memcpy (candidate_utf + 1, word, wl * sizeof(w_char));
+ for (p = word, q = candidate_utf; p < (word + wl); ) {
+ for (int i = 0; i < ctryl; i++) {
+ *q = ctry_utf[i];
+ cwrd = 1;
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, &timer, &timelimit);
+ if (ns == -1) return -1;
+ if (!timelimit) return ns;
+ }
+ *q++ = *p++;
+ }
+ // now try adding one to end */
+ for (int i = 0; i < ctryl; i++) {
+ *q = ctry_utf[i];
+ cwrd = 1;
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl + 1);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ return ns;
+}
+
+
+/* error is should have been two words */
+int SuggestMgr::twowords(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ int c1, c2;
+ int forbidden = 0;
+ int cwrd;
+
+ int wl=strlen(word);
+ if (wl < 3) return ns;
+
+ if (pAMgr->get_langnum() == LANG_hu) forbidden = check_forbidden(word, wl);
+
+ strcpy(candidate + 1, word);
+
+ // split the string into two pieces after every char
+ // if both pieces are good words make them a suggestion
+ for (p = candidate + 1; p[1] != '\0'; p++) {
+ p[-1] = *p;
+ // go to end of the UTF-8 character
+ while (utf8 && ((p[1] & 0xc0) == 0x80)) {
+ p++;
+ p[-1] = *p;
+ }
+ *p = '\0';
+ c1 = checkword(candidate,strlen(candidate), cpdsuggest, NULL, NULL);
+ if (c1) {
+ c2 = checkword((p+1),strlen(p+1), cpdsuggest, NULL, NULL);
+ if (c2) {
+ *p = ' ';
+
+ // spec. Hungarian code (need a better compound word support)
+ if ((pAMgr->get_langnum() == LANG_hu) && !forbidden &&
+ // if 3 repeating letter, use - instead of space
+ (((p[-1] == p[1]) && (((p>candidate+1) && (p[-1] == p[-2])) || (p[-1] == p[2]))) ||
+ // or multiple compounding, with more, than 6 syllables
+ ((c1 == 3) && (c2 >= 2)))) *p = '-';
+
+ cwrd = 1;
+ for (int k=0; k < ns; k++)
+ if (strcmp(candidate,wlst[k]) == 0) cwrd = 0;
+ if (ns < maxSug) {
+ if (cwrd) {
+ wlst[ns] = mystrdup(candidate);
+ if (wlst[ns] == NULL) return -1;
+ ns++;
+ }
+ } else return ns;
+ }
+ }
+ }
+ return ns;
+}
+
+
+// error is adjacent letter were swapped
+int SuggestMgr::swapchar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ char tmpc;
+ int wl=strlen(word);
+ // try swapping adjacent chars one by one
+ strcpy(candidate, word);
+ for (p = candidate; p[1] != 0; p++) {
+ tmpc = *p;
+ *p = p[1];
+ p[1] = tmpc;
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ p[1] = *p;
+ *p = tmpc;
+ }
+ return ns;
+}
+
+// error is adjacent letter were swapped
+int SuggestMgr::swapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ w_char * p;
+ w_char tmpc;
+ // try swapping adjacent chars one by one
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ for (p = candidate_utf; p < (candidate_utf + wl - 1); p++) {
+ tmpc = *p;
+ *p = p[1];
+ p[1] = tmpc;
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ p[1] = *p;
+ *p = tmpc;
+ }
+ return ns;
+}
+
+// error is not adjacent letter were swapped
+int SuggestMgr::longswapchar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ char * q;
+ char tmpc;
+ int wl=strlen(word);
+ // try swapping not adjacent chars one by one
+ strcpy(candidate, word);
+ for (p = candidate; *p != 0; p++) {
+ for (q = candidate; *q != 0; q++) {
+ if (abs(p-q) > 1) {
+ tmpc = *p;
+ *p = *q;
+ *q = tmpc;
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ *q = *p;
+ *p = tmpc;
+ }
+ }
+ }
+ return ns;
+}
+
+
+// error is adjacent letter were swapped
+int SuggestMgr::longswapchar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ w_char * p;
+ w_char * q;
+ w_char tmpc;
+ // try swapping not adjacent chars
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ for (p = candidate_utf; p < (candidate_utf + wl); p++) {
+ for (q = candidate_utf; q < (candidate_utf + wl); q++) {
+ if (abs(p-q) > 1) {
+ tmpc = *p;
+ *p = *q;
+ *q = tmpc;
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ *q = *p;
+ *p = tmpc;
+ }
+ }
+ }
+ return ns;
+}
+
+// error is a letter was moved
+int SuggestMgr::movechar(char ** wlst, const char * word, int ns, int cpdsuggest)
+{
+ char candidate[MAXSWUTF8L];
+ char * p;
+ char * q;
+ char tmpc;
+
+ int wl=strlen(word);
+ // try moving a char
+ strcpy(candidate, word);
+ for (p = candidate; *p != 0; p++) {
+ for (q = p + 1; (*q != 0) && ((q - p) < 10); q++) {
+ tmpc = *(q-1);
+ *(q-1) = *q;
+ *q = tmpc;
+ if ((q-p) < 2) continue; // omit swap char
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ strcpy(candidate, word);
+ }
+ for (p = candidate + wl - 1; p > candidate; p--) {
+ for (q = p - 1; (q >= candidate) && ((p - q) < 10); q--) {
+ tmpc = *(q+1);
+ *(q+1) = *q;
+ *q = tmpc;
+ if ((p-q) < 2) continue; // omit swap char
+ ns = testsug(wlst, candidate, strlen(candidate), ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ strcpy(candidate, word);
+ }
+ return ns;
+}
+
+// error is a letter was moved
+int SuggestMgr::movechar_utf(char ** wlst, const w_char * word, int wl, int ns, int cpdsuggest)
+{
+ w_char candidate_utf[MAXSWL];
+ char candidate[MAXSWUTF8L];
+ w_char * p;
+ w_char * q;
+ w_char tmpc;
+ // try moving a char
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ for (p = candidate_utf; p < (candidate_utf + wl); p++) {
+ for (q = p + 1; (q < (candidate_utf + wl)) && ((q - p) < 10); q++) {
+ tmpc = *(q-1);
+ *(q-1) = *q;
+ *q = tmpc;
+ if ((q-p) < 2) continue; // omit swap char
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ }
+ for (p = candidate_utf + wl - 1; p > candidate_utf; p--) {
+ for (q = p - 1; (q >= candidate_utf) && ((p - q) < 10); q--) {
+ tmpc = *(q+1);
+ *(q+1) = *q;
+ *q = tmpc;
+ if ((p-q) < 2) continue; // omit swap char
+ u16_u8(candidate, MAXSWUTF8L, candidate_utf, wl);
+ ns = testsug(wlst, candidate, wl, ns, cpdsuggest, NULL, NULL);
+ if (ns == -1) return -1;
+ }
+ memcpy (candidate_utf, word, wl * sizeof(w_char));
+ }
+ return ns;
+}
+
+// generate a set of suggestions for very poorly spelled words
+int SuggestMgr::ngsuggest(char** wlst, char * w, HashMgr* pHMgr)
+{
+
+ int i, j;
+ int lval;
+ int sc;
+ int lp;
+ int nonbmp = 0;
+
+ if (!pHMgr) return 0;
+
+ // exhaustively search through all root words
+ // keeping track of the MAX_ROOTS most similar root words
+ struct hentry * roots[MAX_ROOTS];
+ int scores[MAX_ROOTS];
+ for (i = 0; i < MAX_ROOTS; i++) {
+ roots[i] = NULL;
+ scores[i] = -100 * i;
+ }
+ lp = MAX_ROOTS - 1;
+
+ char w2[MAXWORDUTF8LEN];
+ char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ char mw[MAXSWUTF8L];
+ w_char u8[MAXSWL];
+ int nc = strlen(word);
+ int n = (utf8) ? u8_u16(u8, MAXSWL, word) : nc;
+
+ // set character based ngram suggestion for words with non-BMP Unicode characters
+ if (n == -1) {
+ utf8 = 0;
+ n = nc;
+ nonbmp = 1;
+ }
+
+ struct hentry* hp = NULL;
+ int col = -1;
+
+ #ifdef HUNSPELL_CHROME_CLIENT
+ // A static array of hentries required for walking the hash table.
+ struct hentry static_hentry[MAX_ROOTS];
+
+ // Static arrays to store the |word| and |astr| elements pointed to by the
+ // hentry elements on static_hentry.
+ static const int kMaxWordLen = 128;
+ char hentry_word[MAX_ROOTS][kMaxWordLen];
+ unsigned short hentry_astr[MAX_ROOTS];
+ #endif
+
+ while ((hp = pHMgr->walk_hashtable(col, hp))) {
+ if ((hp->astr) && (pAMgr) &&
+ (TESTAFF(hp->astr, pAMgr->get_forbiddenword(), hp->alen) ||
+ TESTAFF(hp->astr, pAMgr->get_nosuggest(), hp->alen) ||
+ TESTAFF(hp->astr, pAMgr->get_onlyincompound(), hp->alen))) continue;
+ sc = ngram(3, word, hp->word, NGRAM_LONGER_WORSE);
+ if (sc > scores[lp]) {
+ scores[lp] = sc;
+ #ifdef HUNSPELL_CHROME_CLIENT
+ roots[lp] = &static_hentry[lp];
+ roots[lp]->alen = hp->alen;
+ if (hp->astr)
+ hentry_astr[lp] = *hp->astr;
+ roots[lp]->astr = &hentry_astr[lp];
+ roots[lp]->wlen = hp->wlen;
+ strcpy(&hentry_word[lp][0], hp->word);
+ roots[lp]->word = &hentry_word[lp][0];
+ roots[lp]->next = NULL;
+ roots[lp]->next_homonym = NULL;
+ #else
+ roots[lp] = hp;
+ #endif
+
+ lval = sc;
+ for (j=0; j < MAX_ROOTS; j++)
+ if (scores[j] < lval) {
+ lp = j;
+ lval = scores[j];
+ }
+ }
+ }
+
+ // find minimum threshhold for a passable suggestion
+ // mangle original word three differnt ways
+ // and score them to generate a minimum acceptable score
+ int thresh = 0;
+ for (int sp = 1; sp < 4; sp++) {
+ if (utf8) {
+ for (int k=sp; k < n; k+=4) *((unsigned short *) u8 + k) = '*';
+ u16_u8(mw, MAXSWUTF8L, u8, n);
+ thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH);
+ } else {
+ strcpy(mw, word);
+ for (int k=sp; k < n; k+=4) *(mw + k) = '*';
+ thresh = thresh + ngram(n, word, mw, NGRAM_ANY_MISMATCH);
+ }
+ }
+ thresh = thresh / 3;
+ thresh--;
+
+ // now expand affixes on each of these root words and
+ // and use length adjusted ngram scores to select
+ // possible suggestions
+ char * guess[MAX_GUESS];
+ int gscore[MAX_GUESS];
+ for(i=0;i<MAX_GUESS;i++) {
+ guess[i] = NULL;
+ gscore[i] = -100 * i;
+ }
+
+ lp = MAX_GUESS - 1;
+
+ struct guessword * glst;
+ glst = (struct guessword *) calloc(MAX_WORDS,sizeof(struct guessword));
+ if (! glst) {
+ if (nonbmp) utf8 = 1;
+ return 0;
+ }
+
+ for (i = 0; i < MAX_ROOTS; i++) {
+
+ if (roots[i]) {
+ struct hentry * rp = roots[i];
+ int nw = pAMgr->expand_rootword(glst, MAX_WORDS, rp->word, rp->wlen,
+ rp->astr, rp->alen, word, nc);
+
+ for (int k = 0; k < nw ; k++) {
+ sc = ngram(n, word, glst[k].word, NGRAM_ANY_MISMATCH);
+ if ((sc > thresh)) {
+ if (sc > gscore[lp]) {
+ if (guess[lp]) free (guess[lp]);
+ gscore[lp] = sc;
+ guess[lp] = glst[k].word;
+ lval = sc;
+ for (j=0; j < MAX_GUESS; j++)
+ if (gscore[j] < lval) {
+ lp = j;
+ lval = gscore[j];
+ }
+ } else free (glst[k].word);
+ } else free(glst[k].word);
+ }
+ }
+ }
+ free(glst);
+
+ // now we are done generating guesses
+ // sort in order of decreasing score
+
+ bubblesort(&guess[0], &gscore[0], MAX_GUESS);
+
+ // weight suggestions with a similarity index, based on
+ // the longest common subsequent algorithm and resort
+
+ int is_swap;
+ for (i=0; i < MAX_GUESS; i++) {
+ if (guess[i]) {
+ // lowering guess[i]
+ char gl[MAXSWUTF8L];
+ int len;
+ if (utf8) {
+ w_char _w[MAXSWL];
+ len = u8_u16(_w, MAXSWL, guess[i]);
+ mkallsmall_utf(_w, len, pAMgr->get_langnum());
+ u16_u8(gl, MAXSWUTF8L, _w, len);
+ } else {
+ strcpy(gl, guess[i]);
+ mkallsmall(gl, csconv);
+ len = strlen(guess[i]);
+ }
+
+ int _lcs = lcslen(word, gl);
+
+ // same characters with different casing
+ if ((n == len) && (n == _lcs)) {
+ gscore[i] += 2000;
+ break;
+ }
+
+ // heuristic weigthing of ngram scores
+ gscore[i] +=
+ // length of longest common subsequent minus lenght difference
+ 2 * _lcs - abs((int) (n - len)) +
+ // weight equal first letter
+ equalfirstletter(word, gl) +
+ // weight equal character positions
+ ((_lcs == commoncharacterpositions(word, gl, &is_swap)) ? 1: 0) +
+ // swap character (not neighboring)
+ ((is_swap) ? 1000 : 0);
+ }
+ }
+
+ bubblesort(&guess[0], &gscore[0], MAX_GUESS);
+
+ // copy over
+
+ int ns = 0;
+ int same = 0;
+ for (i=0; i < MAX_GUESS; i++) {
+ if (guess[i]) {
+ if ((ns < maxngramsugs) && (ns < maxSug) && (!same || (gscore[i] > 1000))) {
+ int unique = 1;
+ // we have excellent suggestion(s)
+ if (gscore[i] > 1000) same = 1;
+ for (j=0; j < ns; j++)
+ // don't suggest previous suggestions or a previous suggestion with prefixes or affixes
+ if (strstr(guess[i], wlst[j]) ||
+ // check forbidden words
+ !checkword(guess[i], strlen(guess[i]), 0, NULL, NULL)) unique = 0;
+ if (unique) wlst[ns++] = guess[i]; else free(guess[i]);
+ } else free(guess[i]);
+ }
+ }
+
+ if (nonbmp) utf8 = 1;
+ return ns;
+}
+
+
+// see if a candidate suggestion is spelled correctly
+// needs to check both root words and words with affixes
+
+// obsolote MySpell-HU modifications:
+// return value 2 and 3 marks compounding with hyphen (-)
+// `3' marks roots without suffix
+int SuggestMgr::checkword(const char * word, int len, int cpdsuggest, int * timer, time_t * timelimit)
+{
+ struct hentry * rv=NULL;
+ int nosuffix = 0;
+
+ // check time limit
+ if (timer) {
+ (*timer)--;
+ if (!(*timer) && timelimit) {
+ if (time(NULL) > *timelimit) {
+ *timelimit = 0;
+ return 0;
+ }
+ *timer = MAXPLUSTIMER;
+ }
+ }
+
+ if (pAMgr) {
+ if (cpdsuggest==1) {
+ if (pAMgr->get_compound()) {
+ rv = pAMgr->compound_check(word,len,0,0,0,0,NULL,0,NULL,NULL,1);
+ if (rv) return 3; // XXX obsolote categorisation
+ }
+ return 0;
+ }
+
+ rv = pAMgr->lookup(word);
+
+ if (rv) {
+ if ((rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)
+ || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen))) return 0;
+ if (rv->astr && (TESTAFF(rv->astr,pAMgr->get_pseudoroot(),rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL;
+ } else rv = pAMgr->prefix_check(word, len, 0); // only prefix, and prefix + suffix XXX
+
+ if (rv) {
+ nosuffix=1;
+ } else {
+ rv = pAMgr->suffix_check(word, len, 0, NULL, NULL, 0, NULL); // only suffix
+ }
+
+ if (!rv && pAMgr->have_contclass()) {
+ rv = pAMgr->suffix_check_twosfx(word, len, 0, NULL, FLAG_NULL);
+ if (!rv) rv = pAMgr->prefix_check_twosfx(word, len, 1, FLAG_NULL);
+ }
+
+ // check forbidden words
+ if ((rv) && (rv->astr) && (TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)
+ || TESTAFF(rv->astr,pAMgr->get_nosuggest(),rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) return 0;
+
+ if (rv) { // XXX obsolote
+ if ((pAMgr->get_compoundflag()) &&
+ TESTAFF(rv->astr, pAMgr->get_compoundflag(), rv->alen)) return 2 + nosuffix;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int SuggestMgr::check_forbidden(const char * word, int len)
+{
+ struct hentry * rv = NULL;
+
+ if (pAMgr) {
+ rv = pAMgr->lookup(word);
+ if (rv && rv->astr && (TESTAFF(rv->astr,pAMgr->get_pseudoroot(),rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) rv = NULL;
+ if (!(pAMgr->prefix_check(word,len,1)))
+ rv = pAMgr->suffix_check(word,len, 0, NULL, NULL, 0, NULL); // prefix+suffix, suffix
+ // check forbidden words
+ if ((rv) && (rv->astr) && TESTAFF(rv->astr,pAMgr->get_forbiddenword(),rv->alen)) return 1;
+ }
+ return 0;
+}
+
+#ifdef HUNSPELL_EXPERIMENTAL
+// suggest stems, XXX experimental code
+int SuggestMgr::suggest_stems(char*** slst, const char * w, int nsug)
+{
+ char buf[MAXSWUTF8L];
+ char ** wlst;
+ int prevnsug = nsug;
+
+ char w2[MAXWORDUTF8LEN];
+ const char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ if (*slst) {
+ wlst = *slst;
+ } else {
+ wlst = (char **) calloc(maxSug, sizeof(char *));
+ if (wlst == NULL) return -1;
+ }
+ // perhaps there are a fix stem in the dictionary
+ if ((nsug < maxSug) && (nsug > -1)) {
+
+ nsug = fixstems(wlst, word, nsug);
+ if (nsug == prevnsug) {
+ char * s = mystrdup(word);
+ char * p = s + strlen(s);
+ while ((*p != '-') && (p != s)) p--;
+ if (*p == '-') {
+ *p = '\0';
+ nsug = fixstems(wlst, s, nsug);
+ if ((nsug == prevnsug) && (nsug < maxSug) && (nsug >= 0)) {
+ char * t;
+ buf[0] = '\0';
+ for (t = s; (t[0] != '\0') && ((t[0] >= '0') || (t[0] <= '9')); t++); // is a number?
+ if (*t != '\0') strcpy(buf, "# ");
+ strcat(buf, s);
+ wlst[nsug] = mystrdup(buf);
+ if (wlst[nsug] == NULL) return -1;
+ nsug++;
+ }
+ p++;
+ nsug = fixstems(wlst, p, nsug);
+ }
+
+ free(s);
+ }
+ }
+
+ if (nsug < 0) {
+ for (int i=0;i<maxSug; i++)
+ if (wlst[i] != NULL) free(wlst[i]);
+ free(wlst);
+ return -1;
+ }
+
+ *slst = wlst;
+ return nsug;
+}
+
+
+// there are fix stems in dictionary
+int SuggestMgr::fixstems(char ** wlst, const char * word, int ns)
+{
+ char buf[MAXSWUTF8L];
+ char prefix[MAXSWUTF8L] = "";
+
+ int dicstem = 1; // 0 = lookup, 1= affix, 2 = compound
+ int cpdindex = 0;
+ struct hentry * rv = NULL;
+
+ int wl = strlen(word);
+ int cmpdstemnum;
+ int cmpdstem[MAXCOMPOUND];
+
+ if (pAMgr) {
+ rv = pAMgr->lookup(word);
+ if (rv) {
+ dicstem = 0;
+ } else {
+ // try stripping off affixes
+ rv = pAMgr->affix_check(word, wl);
+
+ // else try check compound word
+ if (!rv && pAMgr->get_compound()) {
+ rv = pAMgr->compound_check(word, wl,
+ 0, 0, 100, 0, NULL, 0, &cmpdstemnum, cmpdstem,1);
+
+ if (rv) {
+ dicstem = 2;
+ for (int j = 0; j < cmpdstemnum; j++) {
+ cpdindex += cmpdstem[j];
+ }
+ if(! (pAMgr->lookup(word + cpdindex)))
+ pAMgr->affix_check(word + cpdindex, wl - cpdindex); // for prefix
+ }
+ }
+
+
+ if (pAMgr->get_prefix()) {
+ strcpy(prefix, pAMgr->get_prefix());
+ }
+
+ // XXX obsolete, will be a general solution for stemming
+ if ((prefix) && (strncmp(prefix, "leg", 3)==0)) prefix[0] = '\0'; // (HU)
+ }
+
+ }
+
+
+
+ if ((rv) && (ns < maxSug)) {
+
+ // check fixstem flag and not_valid_stem flag
+ // first word
+ if ((ns < maxSug) && (dicstem < 2)) {
+ strcpy(buf, prefix);
+ if ((dicstem > 0) && pAMgr->get_derived()) {
+ // XXX obsolote
+ if (strlen(prefix) == 1) {
+ strcat(buf, (pAMgr->get_derived()) + 1);
+ } else {
+ strcat(buf, pAMgr->get_derived());
+ }
+ } else {
+ // special stem in affix description
+ const char * wordchars = pAMgr->get_wordchars();
+ if (rv->description &&
+ (strchr(wordchars, *(rv->description)))) {
+ char * desc = (rv->description) + 1;
+ while (strchr(wordchars, *desc)) desc++;
+ strncat(buf, rv->description, desc - (rv->description));
+ } else {
+ strcat(buf, rv->word);
+ }
+ }
+ wlst[ns] = mystrdup(buf);
+ if (wlst[ns] == NULL) return -1;
+ ns++;
+ }
+
+ if (dicstem == 2) {
+
+ // compound stem
+
+// if (rv->astr && (strchr(rv->astr, '0') == NULL)) {
+ if (rv->astr) {
+ strcpy(buf, word);
+ buf[cpdindex] = '\0';
+ if (prefix) strcat(buf, prefix);
+ if (pAMgr->get_derived()) {
+ strcat(buf, pAMgr->get_derived());
+ } else {
+ // special stem in affix description
+ const char * wordchars = pAMgr->get_wordchars();
+ if (rv->description &&
+ (strchr(wordchars, *(rv->description)))) {
+ char * desc = (rv->description) + 1;
+ while (strchr(wordchars, *desc)) desc++;
+ strncat(buf, rv->description, desc - (rv->description));
+ } else {
+ strcat(buf, rv->word);
+ }
+ }
+ if (ns < maxSug) {
+ wlst[ns] = mystrdup(buf);
+ if (wlst[ns] == NULL) return -1;
+ ns++;
+ }
+ }
+ }
+ }
+ return ns;
+}
+
+// suggest possible stems
+int SuggestMgr::suggest_pos_stems(char*** slst, const char * w, int nsug)
+{
+ char ** wlst;
+
+ struct hentry * rv = NULL;
+
+ char w2[MAXSWUTF8L];
+ const char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ int wl = strlen(word);
+
+
+ if (*slst) {
+ wlst = *slst;
+ } else {
+ wlst = (char **) calloc(maxSug, sizeof(char *));
+ if (wlst == NULL) return -1;
+ }
+
+ rv = pAMgr->suffix_check(word, wl, 0, NULL, wlst, maxSug, &nsug);
+
+ // delete dash from end of word
+ if (nsug > 0) {
+ for (int j=0; j < nsug; j++) {
+ if (wlst[j][strlen(wlst[j]) - 1] == '-') wlst[j][strlen(wlst[j]) - 1] = '\0';
+ }
+ }
+
+ *slst = wlst;
+ return nsug;
+}
+
+
+char * SuggestMgr::suggest_morph(const char * w)
+{
+ char result[MAXLNLEN];
+ char * r = (char *) result;
+ char * st;
+
+ struct hentry * rv = NULL;
+
+ *result = '\0';
+
+ if (! pAMgr) return NULL;
+
+ char w2[MAXSWUTF8L];
+ const char * word = w;
+
+ // word reversing wrapper for complex prefixes
+ if (complexprefixes) {
+ strcpy(w2, w);
+ if (utf8) reverseword_utf(w2); else reverseword(w2);
+ word = w2;
+ }
+
+ rv = pAMgr->lookup(word);
+
+ while (rv) {
+ if ((!rv->astr) || !(TESTAFF(rv->astr, pAMgr->get_forbiddenword(), rv->alen) ||
+ TESTAFF(rv->astr, pAMgr->get_pseudoroot(), rv->alen) ||
+ TESTAFF(rv->astr,pAMgr->get_onlyincompound(),rv->alen))) {
+ if (rv->description && ((!rv->astr) ||
+ !TESTAFF(rv->astr, pAMgr->get_lemma_present(), rv->alen)))
+ strcat(result, word);
+ if (rv->description) strcat(result, rv->description);
+ strcat(result, "\n");
+ }
+ rv = rv->next_homonym;
+ }
+
+ st = pAMgr->affix_check_morph(word,strlen(word));
+ if (st) {
+ strcat(result, st);
+ free(st);
+ }
+
+ if (pAMgr->get_compound() && (*result == '\0'))
+ pAMgr->compound_check_morph(word, strlen(word),
+ 0, 0, 100, 0,NULL, 0, &r, NULL);
+
+ return (*result) ? mystrdup(line_uniq(delete_zeros(result))) : NULL;
+}
+
+char * SuggestMgr::suggest_morph_for_spelling_error(const char * word)
+{
+ char * p = NULL;
+ char ** wlst = (char **) calloc(maxSug, sizeof(char *));
+ // we will use only the first suggestion
+ for (int i = 0; i < maxSug - 1; i++) wlst[i] = "";
+ int ns = suggest(&wlst, word, maxSug - 1);
+ if (ns == maxSug) {
+ p = suggest_morph(wlst[maxSug - 1]);
+ free(wlst[maxSug - 1]);
+ }
+ if (wlst) free(wlst);
+ return p;
+}
+#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+
+// generate an n-gram score comparing s1 and s2
+int SuggestMgr::ngram(int n, char * s1, const char * s2, int uselen)
+{
+ int nscore = 0;
+ int ns;
+ int l1;
+ int l2;
+
+ if (utf8) {
+ w_char su1[MAXSWL];
+ w_char su2[MAXSWL];
+ l1 = u8_u16(su1, MAXSWL, s1);
+ l2 = u8_u16(su2, MAXSWL, s2);
+ if (!l2 || (l1==-1) || (l2==-1)) return 0;
+ // decapitalize dictionary word
+ if (complexprefixes) {
+ mkallsmall_utf(su2+l2-1, 1, pAMgr->get_langnum());
+ } else {
+ mkallsmall_utf(su2, 1, pAMgr->get_langnum());
+ }
+ for (int j = 1; j <= n; j++) {
+ ns = 0;
+ for (int i = 0; i <= (l1-j); i++) {
+ for (int l = 0; l <= (l2-j); l++) {
+ int k;
+ for (k = 0; (k < j); k++) {
+ w_char * c1 = su1 + i + k;
+ w_char * c2 = su2 + l + k;
+ if ((c1->l != c2->l) || (c1->h != c2->h)) break;
+ }
+ if (k == j) {
+ ns++;
+ break;
+ }
+ }
+ }
+ nscore = nscore + ns;
+ if (ns < 2) break;
+ }
+ } else {
+ char t[MAXSWUTF8L];
+ l1 = strlen(s1);
+ l2 = strlen(s2);
+ if (!l2) return 0;
+ strcpy(t, s2);
+ if (complexprefixes) {
+ *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower;
+ } else {
+ mkallsmall(t, csconv);
+ }
+ for (int j = 1; j <= n; j++) {
+ ns = 0;
+ for (int i = 0; i <= (l1-j); i++) {
+ char c = *(s1 + i + j);
+ *(s1 + i + j) = '\0';
+ if (strstr(t,(s1+i))) ns++;
+ *(s1 + i + j ) = c;
+ }
+ nscore = nscore + ns;
+ if (ns < 2) break;
+ }
+ }
+
+ ns = 0;
+ if (uselen == NGRAM_LONGER_WORSE) ns = (l2-l1)-2;
+ if (uselen == NGRAM_ANY_MISMATCH) ns = abs(l2-l1)-2;
+ ns = (nscore - ((ns > 0) ? ns : 0));
+ return ns;
+}
+
+int SuggestMgr::equalfirstletter(char * s1, const char * s2) {
+ if (utf8) {
+ w_char su1[MAXSWL];
+ w_char su2[MAXSWL];
+ // decapitalize dictionary word
+ if (complexprefixes) {
+ int l1 = u8_u16(su1, MAXSWL, s1);
+ int l2 = u8_u16(su2, MAXSWL, s2);
+ if (*((short *)su1+l1-1) == *((short *)su2+l2-1)) return 1;
+ } else {
+ u8_u16(su1, 1, s1);
+ u8_u16(su2, 1, s2);
+ if (*((short *)su1) == *((short *)su2)) return 1;
+ }
+ } else {
+ if (complexprefixes) {
+ int l1 = strlen(s1);
+ int l2 = strlen(s2);
+ if (*(s2+l1-1) == *(s2+l2-1)) return 1;
+ } else {
+ if (*s1 == *s2) return 1;
+ }
+ }
+ return 0;
+}
+
+int SuggestMgr::commoncharacterpositions(char * s1, const char * s2, int * is_swap) {
+ int num = 0;
+ int diff = 0;
+ int diffpos[2];
+ *is_swap = 0;
+ if (utf8) {
+ w_char su1[MAXSWL];
+ w_char su2[MAXSWL];
+ int l1 = u8_u16(su1, MAXSWL, s1);
+ int l2 = u8_u16(su2, MAXSWL, s2);
+ // decapitalize dictionary word
+ if (complexprefixes) {
+ mkallsmall_utf(su2+l2-1, 1, pAMgr->get_langnum());
+ } else {
+ mkallsmall_utf(su2, 1, pAMgr->get_langnum());
+ }
+ for (int i = 0; (i < l1) && (i < l2); i++) {
+ if (((short *) su1)[i] == ((short *) su2)[i]) {
+ num++;
+ } else {
+ if (diff < 2) diffpos[diff] = i;
+ diff++;
+ }
+ }
+ if ((diff == 2) && (l1 == l2) &&
+ (((short *) su1)[diffpos[0]] == ((short *) su2)[diffpos[1]]) &&
+ (((short *) su1)[diffpos[1]] == ((short *) su2)[diffpos[0]])) *is_swap = 1;
+ } else {
+ int i;
+ char t[MAXSWUTF8L];
+ strcpy(t, s2);
+ // decapitalize dictionary word
+ if (complexprefixes) {
+ int l2 = strlen(t);
+ *(t+l2-1) = csconv[((unsigned char)*(t+l2-1))].clower;
+ } else {
+ mkallsmall(t, csconv);
+ }
+ for (i = 0; (*(s1+i) != 0) && (*(t+i) != 0); i++) {
+ if (*(s1+i) == *(t+i)) {
+ num++;
+ } else {
+ if (diff < 2) diffpos[diff] = i;
+ diff++;
+ }
+ }
+ if ((diff == 2) && (*(s1+i) == 0) && (*(t+i) == 0) &&
+ (*(s1+diffpos[0]) == *(t+diffpos[1])) &&
+ (*(s1+diffpos[1]) == *(t+diffpos[0]))) *is_swap = 1;
+ }
+ return num;
+}
+
+int SuggestMgr::mystrlen(const char * word) {
+ if (utf8) {
+ w_char w[MAXSWL];
+ return u8_u16(w, MAXSWL, word);
+ } else return strlen(word);
+}
+
+// sort in decreasing order of score
+void SuggestMgr::bubblesort(char** rword, int* rsc, int n )
+{
+ int m = 1;
+ while (m < n) {
+ int j = m;
+ while (j > 0) {
+ if (rsc[j-1] < rsc[j]) {
+ int sctmp = rsc[j-1];
+ char * wdtmp = rword[j-1];
+ rsc[j-1] = rsc[j];
+ rword[j-1] = rword[j];
+ rsc[j] = sctmp;
+ rword[j] = wdtmp;
+ j--;
+ } else break;
+ }
+ m++;
+ }
+ return;
+}
+
+// longest common subsequence
+void SuggestMgr::lcs(const char * s, const char * s2, int * l1, int * l2, char ** result) {
+ int n, m;
+ w_char su[MAXSWL];
+ w_char su2[MAXSWL];
+ char * b;
+ char * c;
+ int i;
+ int j;
+ if (utf8) {
+ m = u8_u16(su, MAXSWL, s);
+ n = u8_u16(su2, MAXSWL, s2);
+ } else {
+ m = strlen(s);
+ n = strlen(s2);
+ }
+ c = (char *) malloc((m + 1) * (n + 1));
+ b = (char *) malloc((m + 1) * (n + 1));
+ for (i = 1; i <= m; i++) c[i*(n+1)] = 0;
+ for (j = 0; j <= n; j++) c[j] = 0;
+ for (i = 1; i <= m; i++) {
+ for (j = 1; j <= n; j++) {
+ if ((utf8) && (*((short *) su+i-1) == *((short *)su2+j-1))
+ || (!utf8) && ((*(s+i-1)) == (*(s2+j-1)))) {
+ c[i*(n+1) + j] = c[(i-1)*(n+1) + j-1]+1;
+ b[i*(n+1) + j] = LCS_UPLEFT;
+ } else if (c[(i-1)*(n+1) + j] >= c[i*(n+1) + j-1]) {
+ c[i*(n+1) + j] = c[(i-1)*(n+1) + j];
+ b[i*(n+1) + j] = LCS_UP;
+ } else {
+ c[i*(n+1) + j] = c[i*(n+1) + j-1];
+ b[i*(n+1) + j] = LCS_LEFT;
+ }
+ }
+ }
+ *result = b;
+ free(c);
+ *l1 = m;
+ *l2 = n;
+}
+
+int SuggestMgr::lcslen(const char * s, const char* s2) {
+ int m;
+ int n;
+ int i;
+ int j;
+ char * result;
+ int len = 0;
+ lcs(s, s2, &m, &n, &result);
+ i = m;
+ j = n;
+ while ((i != 0) && (j != 0)) {
+ if (result[i*(n+1) + j] == LCS_UPLEFT) {
+ len++;
+ i--;
+ j--;
+ } else if (result[i*(n+1) + j] == LCS_UP) {
+ i--;
+ } else j--;
+ }
+ if (result) free(result);
+ return len;
+}
|