Bug 454108 - 'Update Hunspell to version 1.2.8'. r=smaug.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 04 Nov 2008 14:05:31 -0800
changeset 21326 6c4d0ddbe9979845209f714719de22fa70d1ab19
parent 21325 b282e708accc376116ac3ee4a6951e21c3016dfa
child 21327 3afda1e3f55a366b48da9681fa9e4ce07648d56e
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs454108
milestone1.9.1b2pre
Bug 454108 - 'Update Hunspell to version 1.2.8'. r=smaug.
extensions/spellcheck/hunspell/src/Makefile.in
extensions/spellcheck/hunspell/src/README.hunspell
extensions/spellcheck/hunspell/src/affentry.cpp
extensions/spellcheck/hunspell/src/affentry.hxx
extensions/spellcheck/hunspell/src/affixmgr.cpp
extensions/spellcheck/hunspell/src/affixmgr.hxx
extensions/spellcheck/hunspell/src/atypes.hxx
extensions/spellcheck/hunspell/src/baseaffix.hxx
extensions/spellcheck/hunspell/src/csutil.cpp
extensions/spellcheck/hunspell/src/csutil.hxx
extensions/spellcheck/hunspell/src/dictmgr.cpp
extensions/spellcheck/hunspell/src/dictmgr.hxx
extensions/spellcheck/hunspell/src/filemgr.cpp
extensions/spellcheck/hunspell/src/filemgr.hxx
extensions/spellcheck/hunspell/src/hashmgr.cpp
extensions/spellcheck/hunspell/src/hashmgr.hxx
extensions/spellcheck/hunspell/src/htypes.hxx
extensions/spellcheck/hunspell/src/hunspell.cpp
extensions/spellcheck/hunspell/src/hunspell.h
extensions/spellcheck/hunspell/src/hunspell.hxx
extensions/spellcheck/hunspell/src/hunzip.cpp
extensions/spellcheck/hunspell/src/hunzip.hxx
extensions/spellcheck/hunspell/src/phonet.cpp
extensions/spellcheck/hunspell/src/phonet.hxx
extensions/spellcheck/hunspell/src/replist.cpp
extensions/spellcheck/hunspell/src/replist.hxx
extensions/spellcheck/hunspell/src/suggestmgr.cpp
extensions/spellcheck/hunspell/src/suggestmgr.hxx
extensions/spellcheck/hunspell/src/w_char.hxx
--- a/extensions/spellcheck/hunspell/src/Makefile.in
+++ b/extensions/spellcheck/hunspell/src/Makefile.in
@@ -56,19 +56,23 @@ REQUIRES         = xpcom \
 CPPSRCS          = mozHunspell.cpp \
                    mozHunspellDirProvider.cpp \
                    $(NULL)
 
 ifndef MOZ_NATIVE_HUNSPELL
 CPPSRCS         += affentry.cpp \
                    affixmgr.cpp \
                    csutil.cpp \
+                   dictmgr.cpp \
+                   filemgr.cpp \
                    hashmgr.cpp \
                    hunspell.cpp \
+                   hunzip.cpp \
                    phonet.cpp \
+                   replist.cpp \
                    suggestmgr.cpp \
                    $(NULL)
 endif
 
 EXTRA_DSO_LDOPTS = \
                    $(LIBS_DIR) \
                    $(XPCOM_LIBS) \
                    $(NSPR_LIBS) \
--- a/extensions/spellcheck/hunspell/src/README.hunspell
+++ b/extensions/spellcheck/hunspell/src/README.hunspell
@@ -29,17 +29,17 @@
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 ******* END LICENSE BLOCK *******
 
-Hunspell Version: 1.1.12
+Hunspell Version: 1.2.8
 
 Hunspell Author: László Németh
 MySpell Author: Kevin Hendricks & David Einstein
 
 Hunspell is a spell checker and morphological analyser library. Hunspell
 is based on OpenOffice.org's Myspell. Documentation, tests, and examples
 are available at http://hunspell.sourceforge.net.
 
--- a/extensions/spellcheck/hunspell/src/affentry.cpp
+++ b/extensions/spellcheck/hunspell/src/affentry.cpp
@@ -55,19 +55,19 @@
  ******* END LICENSE BLOCK *******/
 
 #ifndef MOZILLA_CLIENT
 #include <cstdlib>
 #include <cstring>
 #include <cctype>
 #include <cstdio>
 #else
-#include <stdlib.h> 
+#include <stdlib.h>
 #include <string.h>
-#include <stdio.h> 
+#include <stdio.h>
 #include <ctype.h>
 #endif
 
 #include "affentry.hxx"
 #include "csutil.hxx"
 
 #ifndef MOZILLA_CLIENT
 #ifndef W32
@@ -77,132 +77,164 @@ using namespace std;
 
 
 PfxEntry::PfxEntry(AffixMgr* pmgr, affentry* dp)
 {
   // register affix manager
   pmyMgr = pmgr;
 
   // set up its intial values
- 
-  aflag = dp->aflag;         // flag 
+
+  aflag = dp->aflag;         // flag
   strip = dp->strip;         // string to strip
   appnd = dp->appnd;         // string to append
   stripl = dp->stripl;       // length of strip string
   appndl = dp->appndl;       // length of append string
-  numconds = dp->numconds;   // number of conditions to match
-  opts = dp->opts;         // cross product flag
+  numconds = dp->numconds;   // length of the condition
+  opts = dp->opts;           // cross product flag
   // then copy over all of the conditions
-  memcpy(&conds.base[0],&dp->conds.base[0],SETSIZE*sizeof(conds.base[0]));
+  if (opts & aeLONGCOND) {
+    memcpy(c.conds, dp->c.l.conds1, MAXCONDLEN_1);
+    c.l.conds2 = dp->c.l.conds2;
+  } else memcpy(c.conds, dp->c.conds, MAXCONDLEN);
   next = NULL;
   nextne = NULL;
   nexteq = NULL;
-#ifdef HUNSPELL_EXPERIMENTAL
   morphcode = dp->morphcode;
-#endif
   contclass = dp->contclass;
   contclasslen = dp->contclasslen;
 }
 
 
 PfxEntry::~PfxEntry()
 {
     aflag = 0;
     if (appnd) free(appnd);
     if (strip) free(strip);
     pmyMgr = NULL;
     appnd = NULL;
     strip = NULL;
-    if (opts & aeUTF8) {
-        for (int i = 0; i < numconds; i++) {
-            if (conds.utf8.wchars[i]) free(conds.utf8.wchars[i]);
-        }
-    }
-#ifdef HUNSPELL_EXPERIMENTAL
+    if (opts & aeLONGCOND) free(c.l.conds2);
     if (morphcode && !(opts & aeALIASM)) free(morphcode);
-#endif
     if (contclass && !(opts & aeALIASF)) free(contclass);
 }
 
 // add prefix to this word assuming conditions hold
 char * PfxEntry::add(const char * word, int len)
 {
     char tword[MAXWORDUTF8LEN + 4];
 
-    if ((len > stripl) && (len >= numconds) && test_condition(word) &&
-       (!stripl || (strncmp(word, strip, stripl) == 0)) && 
+    if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) && 
+       (len >= numconds) && test_condition(word) &&
+       (!stripl || (strncmp(word, strip, stripl) == 0)) &&
        ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) {
     /* we have a match so add prefix */
               char * pp = tword;
               if (appndl) {
                   strcpy(tword,appnd);
                   pp += appndl;
                }
                strcpy(pp, (word + stripl));
                return mystrdup(tword);
      }
-     return NULL;    
+     return NULL;
 }
 
+inline char * PfxEntry::nextchar(char * p) {
+    if (p) {
+        p++;
+        if (opts & aeLONGCOND) {
+            // jump to the 2nd part of the condition
+            if (p == c.conds + MAXCONDLEN_1) return c.l.conds2;
+        // end of the MAXCONDLEN length condition
+        } else if (p == c.conds + MAXCONDLEN) return NULL;
+	return *p ? p : NULL;
+    }
+    return NULL;
+}
 
 inline int PfxEntry::test_condition(const char * st)
 {
-    int cond;
-    unsigned char * cp = (unsigned char *)st;
-    if (!(opts & aeUTF8)) { // 256-character codepage
-        for (cond = 0;  cond < numconds;  cond++) {
-            if ((conds.base[*cp++] & (1 << cond)) == 0) return 0;
-        }
-    } else { // UTF-8 encoding
-      unsigned short wc;
-      for (cond = 0;  cond < numconds;  cond++) {
-        // a simple 7-bit ASCII character in UTF-8
-        if ((*cp >> 7) == 0) {
-            // also check limit (end of word)
-            if ((!*cp) || ((conds.utf8.ascii[*cp++] & (1 << cond)) == 0)) return 0;
-        // UTF-8 multibyte character
-        } else {
-            // not dot wildcard in rule
-            if (!conds.utf8.all[cond]) {
-                if (conds.utf8.neg[cond]) {
-                    u8_u16((w_char *) &wc, 1, (char *) cp);
-                    if (conds.utf8.wchars[cond] && 
-                        flag_bsearch((unsigned short *)conds.utf8.wchars[cond],
-                            wc, (short) conds.utf8.wlen[cond])) return 0;
-                } else {
-                    if (!conds.utf8.wchars[cond]) return 0;
-                    u8_u16((w_char *) &wc, 1, (char *) cp);
-                    if (!flag_bsearch((unsigned short *)conds.utf8.wchars[cond],
-                         wc, (short)conds.utf8.wlen[cond])) return 0;
-                }
+    const char * pos = NULL; // group with pos input position
+    bool neg = false;        // complementer
+    bool ingroup = false;    // character in the group
+    if (numconds == 0) return 1;
+    char * p = c.conds;
+    while (1) {
+      switch (*p) {
+        case '\0': return 1;
+        case '[': { 
+                neg = false;
+                ingroup = false;
+                p = nextchar(p);
+                pos = st; break;
+            }
+        case '^': { p = nextchar(p); neg = true; break; }
+        case ']': { 
+                if ((neg && ingroup) || (!neg && !ingroup)) return 0;
+                pos = NULL;
+                p = nextchar(p);
+                // skip the next character
+                if (!ingroup) for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++);
+                if (*st == '\0' && p) return 0; // word <= condition
+                break;
             }
-            // jump to next UTF-8 character
-            for(cp++; (*cp & 0xc0) == 0x80; cp++);
-        }
+         case '.': if (!pos) { // dots are not metacharacters in groups: [.]
+                p = nextchar(p);
+                // skip the next character
+                for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++);
+                if (*st == '\0' && p) return 0; // word <= condition
+                break;
+            }
+    default: {
+                if (*st == *p) {
+                    st++;
+                    p = nextchar(p);
+                    if ((opts & aeUTF8) && (*(st - 1) & 0x80)) { // multibyte
+                        while (p && (*p & 0xc0) == 0x80) {       // character
+                            if (*p != *st) {
+                                if (!pos) return 0;
+                                st = pos;
+                                break;
+                            }
+                            p = nextchar(p);
+                            st++;
+                        }
+                        if (pos && st != pos) {
+                            ingroup = true;
+                            while (p && *p != ']' && (p = nextchar(p)));
+                        }
+                    } else if (pos) {
+                        ingroup = true;
+                        while (p && *p != ']' && (p = nextchar(p)));
+                    }
+                } else if (pos) { // group
+                    p = nextchar(p);
+                } else return 0;
+            }
       }
+      if (!p) return 1;
     }
-    return 1;
 }
 
-
-// check if this prefix entry matches 
+// check if this prefix entry matches
 struct hentry * PfxEntry::checkword(const char * word, int len, char in_compound, const FLAG needflag)
 {
     int                 tmpl;   // length of tmpword
     struct hentry *     he;     // hash entry of root word or NULL
     char                tmpword[MAXWORDUTF8LEN + 4];
 
     // on entry prefix is 0 length or already matches the beginning of the word.
     // So if the remaining root word has positive length
     // and if there are enough chars in root word and added back strip chars
     // to meet the number of characters conditions, then test it
 
      tmpl = len - appndl;
 
-     if ((tmpl > 0) &&  (tmpl + stripl >= numconds)) {
+     if (tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) {
 
             // generate new root word by removing prefix and adding
             // back any characters that would have been stripped
 
             if (stripl) strcpy (tmpword, strip);
             strcpy ((tmpword + stripl), (word + appndl));
 
             // now make sure all of the conditions on characters
@@ -213,57 +245,58 @@ struct hentry * PfxEntry::checkword(cons
             // if all conditions are met then check if resulting
             // root word in the dictionary
 
             if (test_condition(tmpword)) {
                 tmpl += stripl;
                 if ((he = pmyMgr->lookup(tmpword)) != NULL) {
                    do {
                       if (TESTAFF(he->astr, aflag, he->alen) &&
-                        // forbid single prefixes with pseudoroot flag
-                        ! TESTAFF(contclass, pmyMgr->get_pseudoroot(), contclasslen) &&
+                        // forbid single prefixes with needaffix flag
+                        ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) &&
                         // needflag
                         ((!needflag) || TESTAFF(he->astr, needflag, he->alen) ||
                          (contclass && TESTAFF(contclass, needflag, contclasslen))))
                             return he;
                       he = he->next_homonym; // check homonyms
                    } while (he);
                 }
-                
-                // prefix matched but no root word was found 
-                // if aeXPRODUCT is allowed, try again but now 
+
+                // prefix matched but no root word was found
+                // if aeXPRODUCT is allowed, try again but now
                 // ross checked combined with a suffix
 
                 //if ((opts & aeXPRODUCT) && in_compound) {
                 if ((opts & aeXPRODUCT)) {
-                   he = pmyMgr->suffix_check(tmpword, tmpl, aeXPRODUCT, (AffEntry *)this, NULL, 
+                   he = pmyMgr->suffix_check(tmpword, tmpl, aeXPRODUCT, (AffEntry *)this, NULL,
                         0, NULL, FLAG_NULL, needflag, in_compound);
                    if (he) return he;
                 }
             }
      }
     return NULL;
 }
 
-// check if this prefix entry matches 
+// check if this prefix entry matches
 struct hentry * PfxEntry::check_twosfx(const char * word, int len,
     char in_compound, const FLAG needflag)
 {
     int                 tmpl;   // length of tmpword
     struct hentry *     he;     // hash entry of root word or NULL
     char                tmpword[MAXWORDUTF8LEN + 4];
 
     // on entry prefix is 0 length or already matches the beginning of the word.
     // So if the remaining root word has positive length
     // and if there are enough chars in root word and added back strip chars
     // to meet the number of characters conditions, then test it
 
      tmpl = len - appndl;
 
-     if ((tmpl > 0) &&  (tmpl + stripl >= numconds)) {
+     if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+        (tmpl + stripl >= numconds)) {
 
             // generate new root word by removing prefix and adding
             // back any characters that would have been stripped
 
             if (stripl) strcpy (tmpword, strip);
             strcpy ((tmpword + stripl), (word + appndl));
 
             // now make sure all of the conditions on characters
@@ -272,45 +305,45 @@ struct hentry * PfxEntry::check_twosfx(c
             // tested
 
             // if all conditions are met then check if resulting
             // root word in the dictionary
 
             if (test_condition(tmpword)) {
                 tmpl += stripl;
 
-                // prefix matched but no root word was found 
-                // if aeXPRODUCT is allowed, try again but now 
+                // prefix matched but no root word was found
+                // if aeXPRODUCT is allowed, try again but now
                 // cross checked combined with a suffix
 
                 if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
                    he = pmyMgr->suffix_check_twosfx(tmpword, tmpl, aeXPRODUCT, (AffEntry *)this, needflag);
                    if (he) return he;
                 }
             }
      }
     return NULL;
 }
 
-#ifdef HUNSPELL_EXPERIMENTAL
-// check if this prefix entry matches 
+// check if this prefix entry matches
 char * PfxEntry::check_twosfx_morph(const char * word, int len,
          char in_compound, const FLAG needflag)
 {
     int                 tmpl;   // length of tmpword
     char                tmpword[MAXWORDUTF8LEN + 4];
 
     // on entry prefix is 0 length or already matches the beginning of the word.
     // So if the remaining root word has positive length
     // and if there are enough chars in root word and added back strip chars
     // to meet the number of characters conditions, then test it
 
      tmpl = len - appndl;
 
-     if ((tmpl > 0) &&  (tmpl + stripl >= numconds)) {
+     if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+        (tmpl + stripl >= numconds)) {
 
             // generate new root word by removing prefix and adding
             // back any characters that would have been stripped
 
             if (stripl) strcpy (tmpword, strip);
             strcpy ((tmpword + stripl), (word + appndl));
 
             // now make sure all of the conditions on characters
@@ -319,48 +352,49 @@ char * PfxEntry::check_twosfx_morph(cons
             // tested
 
             // if all conditions are met then check if resulting
             // root word in the dictionary
 
             if (test_condition(tmpword)) {
                 tmpl += stripl;
 
-                // prefix matched but no root word was found 
-                // if aeXPRODUCT is allowed, try again but now 
+                // prefix matched but no root word was found
+                // if aeXPRODUCT is allowed, try again but now
                 // ross checked combined with a suffix
 
                 if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
                     return pmyMgr->suffix_check_twosfx_morph(tmpword, tmpl,
                              aeXPRODUCT, (AffEntry *)this, needflag);
                 }
             }
      }
     return NULL;
 }
 
-// check if this prefix entry matches 
+// check if this prefix entry matches
 char * PfxEntry::check_morph(const char * word, int len, char in_compound, const FLAG needflag)
 {
     int                 tmpl;   // length of tmpword
     struct hentry *     he;     // hash entry of root word or NULL
     char                tmpword[MAXWORDUTF8LEN + 4];
     char                result[MAXLNLEN];
     char * st;
-    
+
     *result = '\0';
 
     // on entry prefix is 0 length or already matches the beginning of the word.
     // So if the remaining root word has positive length
     // and if there are enough chars in root word and added back strip chars
     // to meet the number of characters conditions, then test it
 
      tmpl = len - appndl;
 
-     if ((tmpl > 0) &&  (tmpl + stripl >= numconds)) {
+     if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+        (tmpl + stripl >= numconds)) {
 
             // generate new root word by removing prefix and adding
             // back any characters that would have been stripped
 
             if (stripl) strcpy (tmpword, strip);
             strcpy ((tmpword + stripl), (word + appndl));
 
             // now make sure all of the conditions on characters
@@ -371,170 +405,238 @@ char * PfxEntry::check_morph(const char 
             // if all conditions are met then check if resulting
             // root word in the dictionary
 
             if (test_condition(tmpword)) {
                 tmpl += stripl;
                 if ((he = pmyMgr->lookup(tmpword)) != NULL) {
                     do {
                       if (TESTAFF(he->astr, aflag, he->alen) &&
-                        // forbid single prefixes with pseudoroot flag
-                        ! TESTAFF(contclass, pmyMgr->get_pseudoroot(), contclasslen) &&
+                        // forbid single prefixes with needaffix flag
+                        ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) &&
                         // needflag
                         ((!needflag) || TESTAFF(he->astr, needflag, he->alen) ||
                          (contclass && TESTAFF(contclass, needflag, contclasslen)))) {
-                            if (morphcode) strcat(result, morphcode); else strcat(result,getKey());
-                            if (he->description) {
-                                if ((*(he->description)=='[')||(*(he->description)=='<')) strcat(result,he->word);
-                                strcat(result,he->description);
+                            if (morphcode) {
+                                mystrcat(result, " ", MAXLNLEN);
+                                mystrcat(result, morphcode, MAXLNLEN);
+                            } else mystrcat(result,getKey(), MAXLNLEN);
+                            if (!HENTRY_FIND(he, MORPH_STEM)) {
+                                mystrcat(result, " ", MAXLNLEN);
+                                mystrcat(result, MORPH_STEM, MAXLNLEN);
+                                mystrcat(result, HENTRY_WORD(he), MAXLNLEN);
                             }
-                            strcat(result, "\n");
+                            // store the pointer of the hash entry
+                            if (HENTRY_DATA(he)) {
+                                mystrcat(result, " ", MAXLNLEN);
+                                mystrcat(result, HENTRY_DATA2(he), MAXLNLEN);
+                            } else {
+                                // return with debug information
+                                char * flag = pmyMgr->encode_flag(getFlag());
+                                mystrcat(result, " ", MAXLNLEN);
+                                mystrcat(result, MORPH_FLAG, MAXLNLEN);
+                                mystrcat(result, flag, MAXLNLEN);
+                                free(flag);
+                            }
+                            mystrcat(result, "\n", MAXLNLEN);
                       }
                       he = he->next_homonym;
                     } while (he);
                 }
 
-                // prefix matched but no root word was found 
-                // if aeXPRODUCT is allowed, try again but now 
+                // prefix matched but no root word was found
+                // if aeXPRODUCT is allowed, try again but now
                 // ross checked combined with a suffix
 
                 if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
-                   st = pmyMgr->suffix_check_morph(tmpword, tmpl, aeXPRODUCT, (AffEntry *)this, 
+                   st = pmyMgr->suffix_check_morph(tmpword, tmpl, aeXPRODUCT, (AffEntry *)this,
                      FLAG_NULL, needflag);
                    if (st) {
-                        strcat(result, st);
+                        mystrcat(result, st, MAXLNLEN);
                         free(st);
                    }
                 }
             }
      }
-     
+    
     if (*result) return mystrdup(result);
     return NULL;
 }
-#endif // END OF HUNSPELL_EXPERIMENTAL CODE
 
 SfxEntry::SfxEntry(AffixMgr * pmgr, affentry* dp)
 {
   // register affix manager
   pmyMgr = pmgr;
 
   // set up its intial values
-  aflag = dp->aflag;         // char flag 
+  aflag = dp->aflag;         // char flag
   strip = dp->strip;         // string to strip
   appnd = dp->appnd;         // string to append
   stripl = dp->stripl;       // length of strip string
   appndl = dp->appndl;       // length of append string
-  numconds = dp->numconds;   // number of conditions to match
-  opts = dp->opts;         // cross product flag
+  numconds = dp->numconds;   // length of the condition
+  opts = dp->opts;           // cross product flag
 
   // then copy over all of the conditions
-  memcpy(&conds.base[0],&dp->conds.base[0],SETSIZE*sizeof(conds.base[0]));
+  if (opts & aeLONGCOND) {
+    memcpy(c.l.conds1, dp->c.l.conds1, MAXCONDLEN_1);
+    c.l.conds2 = dp->c.l.conds2;
+  } else memcpy(c.conds, dp->c.conds, MAXCONDLEN);
 
   rappnd = myrevstrdup(appnd);
-
-#ifdef HUNSPELL_EXPERIMENTAL
   morphcode = dp->morphcode;
-#endif
   contclass = dp->contclass;
   contclasslen = dp->contclasslen;
 }
 
 
 SfxEntry::~SfxEntry()
 {
     aflag = 0;
     if (appnd) free(appnd);
     if (rappnd) free(rappnd);
     if (strip) free(strip);
     pmyMgr = NULL;
     appnd = NULL;
-    strip = NULL;    
-    if (opts & aeUTF8) {
-        for (int i = 0; i < numconds; i++) {
-            if (conds.utf8.wchars[i]) free(conds.utf8.wchars[i]);  
-        }
-    }
-#ifdef HUNSPELL_EXPERIMENTAL
+    strip = NULL;
+    if (opts & aeLONGCOND) free(c.l.conds2);
     if (morphcode && !(opts & aeALIASM)) free(morphcode);
-#endif
     if (contclass && !(opts & aeALIASF)) free(contclass);
 }
 
 // add suffix to this word assuming conditions hold
 char * SfxEntry::add(const char * word, int len)
 {
     char                tword[MAXWORDUTF8LEN + 4];
 
      /* make sure all conditions match */
-     if ((len > stripl) && (len >= numconds) && test_condition(word + len, word) &&
+     if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) &&
+        (len >= numconds) && test_condition(word + len, word) &&
         (!stripl || (strcmp(word + len - stripl, strip) == 0)) &&
         ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) {
               /* we have a match so add suffix */
               strcpy(tword,word);
               if (appndl) {
                   strcpy(tword + len - stripl, appnd);
               } else {
                   *(tword + len - stripl) = '\0';
               }
               return mystrdup(tword);
      }
      return NULL;
 }
 
+inline char * SfxEntry::nextchar(char * p) {
+    if (p) {
+	p++;
+	if (opts & aeLONGCOND) {
+    	    // jump to the 2nd part of the condition
+    	    if (p == c.l.conds1 + MAXCONDLEN_1) return c.l.conds2;
+	// end of the MAXCONDLEN length condition
+	} else if (p == c.conds + MAXCONDLEN) return NULL;
+	return *p ? p : NULL;
+    }
+    return NULL;
+}
 
 inline int SfxEntry::test_condition(const char * st, const char * beg)
 {
-    int cond;
-    unsigned char * cp = (unsigned char *) st;
-    if (!(opts & aeUTF8)) { // 256-character codepage
-        // Domolki affix algorithm
-        for (cond = numconds;  --cond >= 0; ) {
-            if ((conds.base[*--cp] & (1 << cond)) == 0) return 0;
-        }
-    } else { // UTF-8 encoding
-      unsigned short wc;
-      for (cond = numconds;  --cond >= 0; ) {
-        // go to next character position and check limit
-        if ((char *) --cp < beg) return 0;
-        // a simple 7-bit ASCII character in UTF-8
-        if ((*cp >> 7) == 0) {
-            if ((conds.utf8.ascii[*cp] & (1 << cond)) == 0) return 0;
-        // UTF-8 multibyte character
-        } else {
-            // go to first character of UTF-8 multibyte character
-            for (; (*cp & 0xc0) == 0x80; cp--);
-            // not dot wildcard in rule
-            if (!conds.utf8.all[cond]) {
-                if (conds.utf8.neg[cond]) {
-                    u8_u16((w_char *) &wc, 1, (char *) cp);
-                    if (conds.utf8.wchars[cond] && 
-                        flag_bsearch((unsigned short *)conds.utf8.wchars[cond],
-                            wc, (short) conds.utf8.wlen[cond])) return 0;
-                } else {
-                    if (!conds.utf8.wchars[cond]) return 0;
-                    u8_u16((w_char *) &wc, 1, (char *) cp);
-                    if (!flag_bsearch((unsigned short *)conds.utf8.wchars[cond],
-                         wc, (short)conds.utf8.wlen[cond])) return 0;
+    const char * pos = NULL;    // group with pos input position
+    bool neg = false;           // complementer
+    bool ingroup = false;       // character in the group
+    if (numconds == 0) return 1;
+    char * p = c.conds;
+    st--;
+    int i = 1;
+    while (1) {
+      switch (*p) {
+        case '\0': return 1;
+        case '[': { p = nextchar(p); pos = st; break; }
+        case '^': { p = nextchar(p); neg = true; break; }
+        case ']': { if (!neg && !ingroup) return 0;
+                i++;
+                // skip the next character
+                if (!ingroup) {
+                    for (; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--);
+                    st--;
+                }                    
+                pos = NULL;
+                neg = false;
+                ingroup = false;
+                p = nextchar(p);
+                if (st < beg && p) return 0; // word <= condition
+                break;
+            }
+        case '.': if (!pos) { // dots are not metacharacters in groups: [.]
+                p = nextchar(p);
+                // skip the next character
+                for (st--; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--);
+                if (st < beg) { // word <= condition
+		    if (p) return 0; else return 1;
+		}
+                if ((opts & aeUTF8) && (*st & 0x80)) { // head of the UTF-8 character
+                    st--;
+                    if (st < beg) { // word <= condition
+			if (p) return 0; else return 1;
+		    }
                 }
+                break;
             }
-        }
+    default: {
+                if (*st == *p) {
+                    p = nextchar(p);
+                    if ((opts & aeUTF8) && (*st & 0x80)) {
+                        st--;
+                        while (p && (st >= beg)) {
+                            if (*p != *st) {
+                                if (!pos) return 0;
+                                st = pos;
+                                break;
+                            }
+                            // first byte of the UTF-8 multibyte character
+                            if ((*p & 0xc0) != 0x80) break;
+                            p = nextchar(p);
+                            st--;
+                        }
+                        if (pos && st != pos) {
+                            if (neg) return 0;
+                            else if (i == numconds) return 1;
+                            ingroup = true;
+                            while (p && *p != ']' && (p = nextchar(p)));
+			    st--;
+                        }
+                        if (p && *p != ']') p = nextchar(p);
+                    } else if (pos) {
+                        if (neg) return 0;
+                        else if (i == numconds) return 1;
+                        ingroup = true;
+			while (p && *p != ']' && (p = nextchar(p)));
+//			if (p && *p != ']') p = nextchar(p);
+                        st--;
+                    }
+                    if (!pos) {
+                        i++;
+                        st--;
+                    }
+                    if (st < beg && p && *p != ']') return 0; // word <= condition
+                } else if (pos) { // group
+                    p = nextchar(p);
+                } else return 0;
+            }
       }
+      if (!p) return 1;
     }
-    return 1;
 }
 
-
-
-// see if this suffix is present in the word 
+// see if this suffix is present in the word
 struct hentry * SfxEntry::checkword(const char * word, int len, int optflags,
     AffEntry* ppfx, char ** wlst, int maxSug, int * ns, const FLAG cclass, const FLAG needflag,
     const FLAG badflag)
 {
-    int                 tmpl;            // length of tmpword 
+    int                 tmpl;            // length of tmpword
     struct hentry *     he;              // hash entry pointer
     unsigned char *     cp;
     char                tmpword[MAXWORDUTF8LEN + 4];
     PfxEntry* ep = (PfxEntry *) ppfx;
 
     // if this suffix is being cross checked with a prefix
     // but it does not support cross products skip it
 
@@ -544,96 +646,98 @@ struct hentry * SfxEntry::checkword(cons
     // upon entry suffix is 0 length or already matches the end of the word.
     // So if the remaining root word has positive length
     // and if there are enough chars in root word and added back strip chars
     // to meet the number of characters conditions, then test it
 
     tmpl = len - appndl;
     // the second condition is not enough for UTF-8 strings
     // it checked in test_condition()
-    
-    if ((tmpl > 0)  &&  (tmpl + stripl >= numconds)) {
+
+    if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+        (tmpl + stripl >= numconds)) {
 
             // generate new root word by removing suffix and adding
             // back any characters that would have been stripped or
             // or null terminating the shorter string
 
             strcpy (tmpword, word);
             cp = (unsigned char *)(tmpword + tmpl);
             if (stripl) {
                 strcpy ((char *)cp, strip);
                 tmpl += stripl;
                 cp = (unsigned char *)(tmpword + tmpl);
             } else *cp = '\0';
 
             // now make sure all of the conditions on characters
             // are met.  Please see the appendix at the end of
-            // this file for more info on exactly what is being            // tested
+            // this file for more info on exactly what is being
+            // tested
 
             // if all conditions are met then check if resulting
             // root word in the dictionary
 
             if (test_condition((char *) cp, (char *) tmpword)) {
 
 #ifdef SZOSZABLYA_POSSIBLE_ROOTS
                 fprintf(stdout,"%s %s %c\n", word, tmpword, aflag);
 #endif
                 if ((he = pmyMgr->lookup(tmpword)) != NULL) {
                     do {
                         // check conditional suffix (enabled by prefix)
                         if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() &&
-                                    TESTAFF(ep->getCont(), aflag, ep->getContLen()))) && 
-                            (((optflags & aeXPRODUCT) == 0) || 
+                                    TESTAFF(ep->getCont(), aflag, ep->getContLen()))) &&
+                            (((optflags & aeXPRODUCT) == 0) ||
                             TESTAFF(he->astr, ep->getFlag(), he->alen) ||
                              // enabled by prefix
                             ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen))
                             ) &&
                             // handle cont. class
-                            ((!cclass) || 
+                            ((!cclass) ||
                                 ((contclass) && TESTAFF(contclass, cclass, contclasslen))
                             ) &&
                             // check only in compound homonyms (bad flags)
                             (!badflag || !TESTAFF(he->astr, badflag, he->alen)
-                            ) &&                            
+                            ) &&
                             // handle required flag
-                            ((!needflag) || 
+                            ((!needflag) ||
                               (TESTAFF(he->astr, needflag, he->alen) ||
                               ((contclass) && TESTAFF(contclass, needflag, contclasslen)))
                             )
                         ) return he;
                         he = he->next_homonym; // check homonyms
                     } while (he);
 
-                // obsolote stemming code (used only by the 
+                // obsolote stemming code (used only by the
                 // experimental SuffixMgr:suggest_pos_stems)
                 // store resulting root in wlst
                 } else if (wlst && (*ns < maxSug)) {
                     int cwrd = 1;
-                    for (int k=0; k < *ns; k++) 
+                    for (int k=0; k < *ns; k++)
                         if (strcmp(tmpword, wlst[k]) == 0) cwrd = 0;
                     if (cwrd) {
                         wlst[*ns] = mystrdup(tmpword);
                         if (wlst[*ns] == NULL) {
                             for (int j=0; j<*ns; j++) free(wlst[j]);
                             *ns = -1;
                             return NULL;
                         }
                         (*ns)++;
                     }
                 }
             }
     }
     return NULL;
 }
 
-// see if two-level suffix is present in the word 
+// see if two-level suffix is present in the word
 struct hentry * SfxEntry::check_twosfx(const char * word, int len, int optflags,
     AffEntry* ppfx, const FLAG needflag)
 {
-    int                 tmpl;            // length of tmpword 
+    int                 tmpl;            // length of tmpword
     struct hentry *     he;              // hash entry pointer
     unsigned char *     cp;
     char                tmpword[MAXWORDUTF8LEN + 4];
     PfxEntry* ep = (PfxEntry *) ppfx;
 
 
     // if this suffix is being cross checked with a prefix
     // but it does not support cross products skip it
@@ -643,17 +747,18 @@ struct hentry * SfxEntry::check_twosfx(c
 
     // upon entry suffix is 0 length or already matches the end of the word.
     // So if the remaining root word has positive length
     // and if there are enough chars in root word and added back strip chars
     // to meet the number of characters conditions, then test it
 
     tmpl = len - appndl;
 
-    if ((tmpl > 0)  &&  (tmpl + stripl >= numconds)) {
+    if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+       (tmpl + stripl >= numconds)) {
 
             // generate new root word by removing suffix and adding
             // back any characters that would have been stripped or
             // or null terminating the shorter string
 
             strcpy (tmpword, word);
             cp = (unsigned char *)(tmpword + tmpl);
             if (stripl) {
@@ -667,58 +772,58 @@ struct hentry * SfxEntry::check_twosfx(c
             // this file for more info on exactly what is being
             // tested
 
             // if all conditions are met then recall suffix_check
 
             if (test_condition((char *) cp, (char *) tmpword)) {
                 if (ppfx) {
                     // handle conditional suffix
-                    if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) 
+                    if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen))
                         he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag);
                     else
                         he = pmyMgr->suffix_check(tmpword, tmpl, optflags, ppfx, NULL, 0, NULL, (FLAG) aflag, needflag);
                 } else {
                     he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag);
                 }
                 if (he) return he;
             }
     }
     return NULL;
 }
 
-#ifdef HUNSPELL_EXPERIMENTAL
-// see if two-level suffix is present in the word 
+// see if two-level suffix is present in the word
 char * SfxEntry::check_twosfx_morph(const char * word, int len, int optflags,
     AffEntry* ppfx, const FLAG needflag)
 {
-    int                 tmpl;            // length of tmpword 
+    int                 tmpl;            // length of tmpword
     unsigned char *     cp;
     char                tmpword[MAXWORDUTF8LEN + 4];
     PfxEntry* ep = (PfxEntry *) ppfx;
     char * st;
 
     char result[MAXLNLEN];
-    
+
     *result = '\0';
 
     // if this suffix is being cross checked with a prefix
     // but it does not support cross products skip it
 
     if ((optflags & aeXPRODUCT) != 0 &&  (opts & aeXPRODUCT) == 0)
         return NULL;
 
     // upon entry suffix is 0 length or already matches the end of the word.
     // So if the remaining root word has positive length
     // and if there are enough chars in root word and added back strip chars
     // to meet the number of characters conditions, then test it
 
     tmpl = len - appndl;
 
-    if ((tmpl > 0)  &&  (tmpl + stripl >= numconds)) {
+    if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
+       (tmpl + stripl >= numconds)) {
 
             // generate new root word by removing suffix and adding
             // back any characters that would have been stripped or
             // or null terminating the shorter string
 
             strcpy (tmpword, word);
             cp = (unsigned char *)(tmpword + tmpl);
             if (stripl) {
@@ -736,66 +841,66 @@ char * SfxEntry::check_twosfx_morph(cons
 
             if (test_condition((char *) cp, (char *) tmpword)) {
                 if (ppfx) {
                     // handle conditional suffix
                     if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) {
                         st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag);
                         if (st) {
                             if (((PfxEntry *) ppfx)->getMorph()) {
-                                strcat(result, ((PfxEntry *) ppfx)->getMorph());
+                                mystrcat(result, ((PfxEntry *) ppfx)->getMorph(), MAXLNLEN);
+                                mystrcat(result, " ", MAXLNLEN);
                             }
-                            strcat(result,st);
+                            mystrcat(result,st, MAXLNLEN);
                             free(st);
                             mychomp(result);
                         }
                     } else {
                         st = pmyMgr->suffix_check_morph(tmpword, tmpl, optflags, ppfx, aflag, needflag);
                         if (st) {
-                            strcat(result, st);
+                            mystrcat(result, st, MAXLNLEN);
                             free(st);
                             mychomp(result);
                         }
                     }
                 } else {
                         st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag);
                         if (st) {
-                            strcat(result, st);
+                            mystrcat(result, st, MAXLNLEN);
                             free(st);
                             mychomp(result);
                         }
                 }
                 if (*result) return mystrdup(result);
             }
     }
     return NULL;
 }
-#endif // END OF HUNSPELL_EXPERIMENTAL CODE
 
 // get next homonym with same affix
-struct hentry * SfxEntry::get_next_homonym(struct hentry * he, int optflags, AffEntry* ppfx, 
+struct hentry * SfxEntry::get_next_homonym(struct hentry * he, int optflags, AffEntry* ppfx,
     const FLAG cclass, const FLAG needflag)
 {
     PfxEntry* ep = (PfxEntry *) ppfx;
     FLAG eFlag = ep ? ep->getFlag() : FLAG_NULL;
 
     while (he->next_homonym) {
         he = he->next_homonym;
-        if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() && TESTAFF(ep->getCont(), aflag, ep->getContLen()))) && 
-                            ((optflags & aeXPRODUCT) == 0 || 
+        if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() && TESTAFF(ep->getCont(), aflag, ep->getContLen()))) &&
+                            ((optflags & aeXPRODUCT) == 0 ||
                             TESTAFF(he->astr, eFlag, he->alen) ||
                              // handle conditional suffix
                             ((contclass) && TESTAFF(contclass, eFlag, contclasslen))
                             ) &&
                             // handle cont. class
-                            ((!cclass) || 
+                            ((!cclass) ||
                                 ((contclass) && TESTAFF(contclass, cclass, contclasslen))
                             ) &&
                             // handle required flag
-                            ((!needflag) || 
+                            ((!needflag) ||
                               (TESTAFF(he->astr, needflag, he->alen) ||
                               ((contclass) && TESTAFF(contclass, needflag, contclasslen)))
                             )
                         ) return he;
     }
     return NULL;
 }
 
@@ -916,9 +1021,8 @@ first two affentries for the suffix D de
          where X is all characters *but* a, e, i, o, or u
          
 
      conds['y'] = (1 << 1)     (the last char must be a y)
      all other bits for all other entries in the conds array are zero
 
 
 #endif
-
--- a/extensions/spellcheck/hunspell/src/affentry.hxx
+++ b/extensions/spellcheck/hunspell/src/affentry.hxx
@@ -52,19 +52,19 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  ******* END LICENSE BLOCK *******/
 
 #ifndef _AFFIX_HXX_
 #define _AFFIX_HXX_
 
+#include "affixmgr.hxx"
 #include "atypes.hxx"
 #include "baseaffix.hxx"
-#include "affixmgr.hxx"
 
 /* A Prefix Entry  */
 
 class PfxEntry : public AffEntry
 {
        AffixMgr*    pmyMgr;
 
        PfxEntry * next;
@@ -105,16 +105,17 @@ public:
   inline PfxEntry *    getNextEQ() { return nexteq; }
   inline PfxEntry *    getFlgNxt() { return flgnxt; }
 
   inline void   setNext(PfxEntry * ptr)   { next = ptr;   }
   inline void   setNextNE(PfxEntry * ptr) { nextne = ptr; }
   inline void   setNextEQ(PfxEntry * ptr) { nexteq = ptr; }
   inline void   setFlgNxt(PfxEntry * ptr) { flgnxt = ptr; }
   
+  inline char * nextchar(char * p);
   inline int    test_condition(const char * st);
 };
 
 
 
 
 /* A Suffix Entry */
 
@@ -174,14 +175,14 @@ public:
   inline SfxEntry *    getEQM() { return eq_morph; }
   inline SfxEntry *    getFlgNxt() { return flgnxt; }
 
   inline void   setNext(SfxEntry * ptr)   { next = ptr;   }
   inline void   setNextNE(SfxEntry * ptr) { nextne = ptr; }
   inline void   setNextEQ(SfxEntry * ptr) { nexteq = ptr; }
   inline void   setFlgNxt(SfxEntry * ptr) { flgnxt = ptr; }
 
+  inline char * nextchar(char * p);
   inline int    test_condition(const char * st, const char * begin);
+
 };
 
 #endif
-
-
--- a/extensions/spellcheck/hunspell/src/affixmgr.cpp
+++ b/extensions/spellcheck/hunspell/src/affixmgr.cpp
@@ -55,70 +55,76 @@
  ******* END LICENSE BLOCK *******/
 
 #ifndef MOZILLA_CLIENT
 #include <cstdlib>
 #include <cstring>
 #include <cctype>
 #include <cstdio>
 #else
-#include <stdlib.h> 
+#include <stdlib.h>
 #include <string.h>
-#include <stdio.h> 
+#include <stdio.h>
 #include <ctype.h>
 #endif
 
+#include "affentry.hxx"
 #include "affixmgr.hxx"
-#include "affentry.hxx"
+#include "csutil.hxx"
 #include "langnum.hxx"
 
-#include "csutil.hxx"
-
 #ifndef MOZILLA_CLIENT
 #ifndef W32
 using namespace std;
 #endif
 #endif
 
-AffixMgr::AffixMgr(const char * affpath, HashMgr* ptr) 
+AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key) 
 {
   // register hash manager and load affix data from aff file
-  pHMgr = ptr;
+  pHMgr = ptr[0];
+  alldic = ptr;
+  maxdic = md;
   keystring = NULL;
   trystring = NULL;
   encoding=NULL;
   utf8 = 0;
   complexprefixes = 0;
   maptable = NULL;
   nummap = 0;
   breaktable = NULL;
   numbreak = 0;
   reptable = NULL;
   numrep = 0;
+  iconvtable = NULL;
+  oconvtable = NULL;
   checkcpdtable = NULL;
+  // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN)
+  simplifiedcpd = 0;
   numcheckcpd = 0;
   defcpdtable = NULL;
   numdefcpd = 0;
   phone = NULL;
   compoundflag = FLAG_NULL; // permits word in compound forms
   compoundbegin = FLAG_NULL; // may be first word in compound forms
   compoundmiddle = FLAG_NULL; // may be middle word in compound forms
   compoundend = FLAG_NULL; // may be last word in compound forms
   compoundroot = FLAG_NULL; // compound word signing flag
   compoundpermitflag = FLAG_NULL; // compound permitting flag for suffixed word
   compoundforbidflag = FLAG_NULL; // compound fordidden flag for suffixed word
   checkcompounddup = 0; // forbid double words in compounds
   checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution)
   checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds
   checkcompoundtriple = 0; // forbid compounds with triple letters
-  forbiddenword = FLAG_NULL; // forbidden word signing flag
+  simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt)
+  forbiddenword = FORBIDDENWORD; // forbidden word signing flag
   nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag
   lang = NULL; // language
   langnum = 0; // language code (see http://l10n.openoffice.org/languages.html)
-  pseudoroot = FLAG_NULL; // forbidden root, allowed only with suffixes
+  needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes
   cpdwordmax = -1; // default: unlimited wordcount in compound words
   cpdmin = -1;  // undefined
   cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words
   cpdvowels=NULL; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX)
   cpdvowels_utf16=NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search)
   cpdvowels_utf16_len=0; // vowels
   pfxappnd=NULL; // previous prefix for counting the syllables of prefix BUG
   sfxappnd=NULL; // previous suffix for counting a special syllables BUG
@@ -132,39 +138,39 @@ AffixMgr::AffixMgr(const char * affpath,
   ignorechars_utf16_len=0; // letters + spec. word characters
   version=NULL; // affix and dictionary file version string
   havecontclass=0; // flags of possible continuing classes (double affix)
   // LEMMA_PRESENT: not put root into the morphological output. Lemma presents
   // in morhological description in dictionary file. It's often combined with PSEUDOROOT.
   lemma_present = FLAG_NULL; 
   circumfix = FLAG_NULL; 
   onlyincompound = FLAG_NULL; 
-  flag_mode = FLAG_CHAR; // default one-character flags in affix and dic file
   maxngramsugs = -1; // undefined
   nosplitsugs = 0;
   sugswithdots = 0;
   keepcase = 0;
   checksharps = 0;
-
-  derived = NULL; // XXX not threadsafe variable for experimental stemming
+  substandard = FLAG_NULL;
+  fullstrip = 0;
+
   sfx = NULL;
   pfx = NULL;
 
   for (int i=0; i < SETSIZE; i++) {
      pStart[i] = NULL;
      sStart[i] = NULL;
      pFlag[i] = NULL;
      sFlag[i] = NULL;
   }
 
   for (int j=0; j < CONTSIZE; j++) {
     contclasses[j] = 0;
   }
 
-  if (parse_file(affpath)) {
+  if (parse_file(affpath, key)) {
      HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath);
   }
   
   if (cpdmin == -1) cpdmin = MINCPDLEN;
 
 }
 
 
@@ -227,16 +233,18 @@ AffixMgr::~AffixMgr()
   if (reptable) {
      for (int j=0; j < numrep; j++) {
         free(reptable[j].pattern);
         free(reptable[j].pattern2);
      }
      free(reptable);  
      reptable = NULL;
   }
+  if (iconvtable) delete iconvtable;
+  if (oconvtable) delete oconvtable;
   if (phone && phone->rules) {
      for (int j=0; j < phone->num + 1; j++) {
         free(phone->rules[j * 2]);
         free(phone->rules[j * 2 + 1]);
      }
      free(phone->rules);
      free(phone);  
      phone = NULL;
@@ -250,33 +258,35 @@ AffixMgr::~AffixMgr()
      free(defcpdtable);  
      defcpdtable = NULL;
   }
   numrep = 0;
   if (checkcpdtable) {  
      for (int j=0; j < numcheckcpd; j++) {
         free(checkcpdtable[j].pattern);
         free(checkcpdtable[j].pattern2);
+        free(checkcpdtable[j].pattern3);
         checkcpdtable[j].pattern = NULL;
         checkcpdtable[j].pattern2 = NULL;
+        checkcpdtable[j].pattern3 = NULL;
      }
      free(checkcpdtable);  
      checkcpdtable = NULL;
   }
   numcheckcpd = 0;
   FREE_FLAG(compoundflag);
   FREE_FLAG(compoundbegin);
   FREE_FLAG(compoundmiddle);
   FREE_FLAG(compoundend);
   FREE_FLAG(compoundpermitflag);
   FREE_FLAG(compoundforbidflag);
   FREE_FLAG(compoundroot);
   FREE_FLAG(forbiddenword);
   FREE_FLAG(nosuggest);
-  FREE_FLAG(pseudoroot);
+  FREE_FLAG(needaffix);
   FREE_FLAG(lemma_present);
   FREE_FLAG(circumfix);
   FREE_FLAG(onlyincompound);
   
   cpdwordmax = 0;
   pHMgr = NULL;
   cpdmin = 0;
   cpdmaxsyllable = 0;
@@ -285,84 +295,77 @@ AffixMgr::~AffixMgr()
   if (cpdsyllablenum) free(cpdsyllablenum);
   free_utf_tbl();
   if (lang) free(lang);
   if (wordchars) free(wordchars);
   if (wordchars_utf16) free(wordchars_utf16);
   if (ignorechars) free(ignorechars);
   if (ignorechars_utf16) free(ignorechars_utf16);
   if (version) free(version);
-  if (derived) free(derived);
   checknum=0;
 }
 
 
 // read in aff file and build up prefix and suffix entry objects 
-int  AffixMgr::parse_file(const char * affpath)
+int  AffixMgr::parse_file(const char * affpath, const char * key)
 {
-
-  // io buffers
-  char line[MAXLNLEN+1];
- 
-  // affix type
-  char ft;
+  char * line; // io buffers
+  char ft;     // affix type
   
   // checking flag duplication
   char dupflags[CONTSIZE];
   char dupflags_ini = 1;
 
   // first line indicator for removing byte order mark
   int firstline = 1;
   
   // open the affix file
-  FILE * afflst;
-  afflst = fopen(affpath,"r");
+  FileMgr * afflst = new FileMgr(affpath, key);
   if (!afflst) {
     HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath);
     return 1;
   }
 
   // step one is to parse the affix file building up the internal
   // affix data structures
 
-
     // read in each line ignoring any that do not
     // start with a known line type indicator
-    while (fgets(line,MAXLNLEN,afflst)) {
+    while ((line = afflst->getline())) {
        mychomp(line);
 
        /* remove byte order mark */
        if (firstline) {
          firstline = 0;
          if (strncmp(line,"\xEF\xBB\xBF",3) == 0) {
             memmove(line, line+3, strlen(line+3)+1);
             HUNSPELL_WARNING(stderr, "warning: affix file begins with byte order mark: possible incompatibility with old Hunspell versions\n");
          }
        }
 
        /* parse in the keyboard string */
        if (strncmp(line,"KEY",3) == 0) {
-          if (parse_string(line, &keystring, "KEY")) {
-             fclose(afflst);
+          if (parse_string(line, &keystring, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the try string */
        if (strncmp(line,"TRY",3) == 0) {
-          if (parse_string(line, &trystring, "TRY")) {
-             fclose(afflst);
+          if (parse_string(line, &trystring, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the name of the character set used by the .dict and .aff */
        if (strncmp(line,"SET",3) == 0) {
-          if (parse_string(line, &encoding, "SET")) {
-             fclose(afflst);
+          if (parse_string(line, &encoding, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
           if (strcmp(encoding, "UTF-8") == 0) {
              utf8 = 1;
 #ifndef OPENOFFICEORG
 #ifndef MOZILLA_CLIENT
              if (initialize_utf_tbl()) return 1;
 #endif
@@ -371,291 +374,321 @@ int  AffixMgr::parse_file(const char * a
        }
 
        /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */
        if (strncmp(line,"COMPLEXPREFIXES",15) == 0)
                    complexprefixes = 1;
 
        /* parse in the flag used by the controlled compound words */
        if (strncmp(line,"COMPOUNDFLAG",12) == 0) {
-          if (parse_flag(line, &compoundflag, "COMPOUNDFLAG")) {
-             fclose(afflst);
+          if (parse_flag(line, &compoundflag, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by compound words */
        if (strncmp(line,"COMPOUNDBEGIN",13) == 0) {
           if (complexprefixes) {
-            if (parse_flag(line, &compoundend, "COMPOUNDBEGIN")) {
-              fclose(afflst);
+            if (parse_flag(line, &compoundend, afflst)) {
+              delete afflst;
               return 1;
             }
           } else {
-            if (parse_flag(line, &compoundbegin, "COMPOUNDBEGIN")) {
-              fclose(afflst);
+            if (parse_flag(line, &compoundbegin, afflst)) {
+              delete afflst;
               return 1;
             }
           }
        }
 
        /* parse in the flag used by compound words */
        if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) {
-          if (parse_flag(line, &compoundmiddle, "COMPOUNDMIDDLE")) {
-             fclose(afflst);
+          if (parse_flag(line, &compoundmiddle, afflst)) {
+             delete afflst;
              return 1;
           }
        }
        /* parse in the flag used by compound words */
        if (strncmp(line,"COMPOUNDEND",11) == 0) {
           if (complexprefixes) {
-            if (parse_flag(line, &compoundbegin, "COMPOUNDEND")) {
-              fclose(afflst);
+            if (parse_flag(line, &compoundbegin, afflst)) {
+              delete afflst;
               return 1;
             }
           } else {
-            if (parse_flag(line, &compoundend, "COMPOUNDEND")) {
-              fclose(afflst);
+            if (parse_flag(line, &compoundend, afflst)) {
+              delete afflst;
               return 1;
             }
           }
        }
 
        /* parse in the data used by compound_check() method */
        if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) {
-          if (parse_num(line, &cpdwordmax, "COMPOUNDWORDMAX")) {
-             fclose(afflst);
+          if (parse_num(line, &cpdwordmax, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag sign compounds in dictionary */
        if (strncmp(line,"COMPOUNDROOT",12) == 0) {
-          if (parse_flag(line, &compoundroot, "COMPOUNDROOT")) {
-             fclose(afflst);
+          if (parse_flag(line, &compoundroot, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by compound_check() method */
        if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) {
-          if (parse_flag(line, &compoundpermitflag, "COMPOUNDPERMITFLAG")) {
-             fclose(afflst);
+          if (parse_flag(line, &compoundpermitflag, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by compound_check() method */
        if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) {
-          if (parse_flag(line, &compoundforbidflag, "COMPOUNDFORBIDFLAG")) {
-             fclose(afflst);
+          if (parse_flag(line, &compoundforbidflag, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) {
                    checkcompounddup = 1;
        }
 
        if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) {
                    checkcompoundrep = 1;
        }
 
        if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) {
                    checkcompoundtriple = 1;
        }
 
+       if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) {
+                   simplifiedtriple = 1;
+       }
+
        if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) {
                    checkcompoundcase = 1;
        }
 
        if (strncmp(line,"NOSUGGEST",9) == 0) {
-          if (parse_flag(line, &nosuggest, "NOSUGGEST")) {
-             fclose(afflst);
+          if (parse_flag(line, &nosuggest, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by forbidden words */
        if (strncmp(line,"FORBIDDENWORD",13) == 0) {
-          if (parse_flag(line, &forbiddenword, "FORBIDDENWORD")) {
-             fclose(afflst);
+          if (parse_flag(line, &forbiddenword, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by forbidden words */
        if (strncmp(line,"LEMMA_PRESENT",13) == 0) {
-          if (parse_flag(line, &lemma_present, "LEMMA_PRESENT")) {
-             fclose(afflst);
+          if (parse_flag(line, &lemma_present, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by circumfixes */
        if (strncmp(line,"CIRCUMFIX",9) == 0) {
-          if (parse_flag(line, &circumfix, "CIRCUMFIX")) {
-             fclose(afflst);
+          if (parse_flag(line, &circumfix, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by fogemorphemes */
        if (strncmp(line,"ONLYINCOMPOUND",14) == 0) {
-          if (parse_flag(line, &onlyincompound, "ONLYINCOMPOUND")) {
-             fclose(afflst);
+          if (parse_flag(line, &onlyincompound, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
-       /* parse in the flag used by `pseudoroots' */
+       /* parse in the flag used by `needaffixs' */
        if (strncmp(line,"PSEUDOROOT",10) == 0) {
-          if (parse_flag(line, &pseudoroot, "PSEUDOROOT")) {
-             fclose(afflst);
+          if (parse_flag(line, &needaffix, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
-       /* parse in the flag used by `pseudoroots' */
+       /* parse in the flag used by `needaffixs' */
        if (strncmp(line,"NEEDAFFIX",9) == 0) {
-          if (parse_flag(line, &pseudoroot, "NEEDAFFIX")) {
-             fclose(afflst);
+          if (parse_flag(line, &needaffix, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the minimal length for words in compounds */
        if (strncmp(line,"COMPOUNDMIN",11) == 0) {
-          if (parse_num(line, &cpdmin, "COMPOUNDMIN")) {
-             fclose(afflst);
+          if (parse_num(line, &cpdmin, afflst)) {
+             delete afflst;
              return 1;
           }
           if (cpdmin < 1) cpdmin = 1;
        }
 
        /* parse in the max. words and syllables in compounds */
        if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) {
-          if (parse_cpdsyllable(line)) {
-             fclose(afflst);
+          if (parse_cpdsyllable(line, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by compound_check() method */
        if (strncmp(line,"SYLLABLENUM",11) == 0) {
-          if (parse_string(line, &cpdsyllablenum, "SYLLABLENUM")) {
-             fclose(afflst);
+          if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the flag used by the controlled compound words */
        if (strncmp(line,"CHECKNUM",8) == 0) {
            checknum=1;
        }
 
        /* parse in the extra word characters */
        if (strncmp(line,"WORDCHARS",9) == 0) {
-          if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, "WORDCHARS", utf8)) {
-             fclose(afflst);
+          if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the ignored characters (for example, Arabic optional diacretics charachters */
        if (strncmp(line,"IGNORE",6) == 0) {
-          if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, "IGNORE", utf8)) {
-             fclose(afflst);
+          if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the typical fault correcting table */
        if (strncmp(line,"REP",3) == 0) {
           if (parse_reptable(line, afflst)) {
-             fclose(afflst);
+             delete afflst;
+             return 1;
+          }
+       }
+
+       /* parse in the input conversion table */
+       if (strncmp(line,"ICONV",5) == 0) {
+          if (parse_convtable(line, afflst, &iconvtable, "ICONV")) {
+             delete afflst;
+             return 1;
+          }
+       }
+
+       /* parse in the input conversion table */
+       if (strncmp(line,"OCONV",5) == 0) {
+          if (parse_convtable(line, afflst, &oconvtable, "OCONV")) {
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the phonetic translation table */
        if (strncmp(line,"PHONE",5) == 0) {
           if (parse_phonetable(line, afflst)) {
-             fclose(afflst);
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the checkcompoundpattern table */
        if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) {
           if (parse_checkcpdtable(line, afflst)) {
-             fclose(afflst);
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the defcompound table */
        if (strncmp(line,"COMPOUNDRULE",12) == 0) {
           if (parse_defcpdtable(line, afflst)) {
-             fclose(afflst);
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the related character map table */
        if (strncmp(line,"MAP",3) == 0) {
           if (parse_maptable(line, afflst)) {
-             fclose(afflst);
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the word breakpoints table */
        if (strncmp(line,"BREAK",5) == 0) {
           if (parse_breaktable(line, afflst)) {
-             fclose(afflst);
+             delete afflst;
              return 1;
           }
        }
 
        /* parse in the language for language specific codes */
        if (strncmp(line,"LANG",4) == 0) {
-          if (parse_string(line, &lang, "LANG")) {
-             fclose(afflst);
+          if (parse_string(line, &lang, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
           langnum = get_lang_num(lang);
        }
 
        if (strncmp(line,"VERSION",7) == 0) {
-          if (parse_string(line, &version, "VERSION")) {
-             fclose(afflst);
-             return 1;
-          }
+          for(line = line + 7; *line == ' ' || *line == '\t'; line++);
+          version = mystrdup(line);
        }
 
        if (strncmp(line,"MAXNGRAMSUGS",12) == 0) {
-          if (parse_num(line, &maxngramsugs, "MAXNGRAMSUGS")) {
-             fclose(afflst);
+          if (parse_num(line, &maxngramsugs, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        if (strncmp(line,"NOSPLITSUGS",11) == 0) {
                    nosplitsugs=1;
        }
 
+       if (strncmp(line,"FULLSTRIP",9) == 0) {
+                   fullstrip=1;
+       }
+
        if (strncmp(line,"SUGSWITHDOTS",12) == 0) {
                    sugswithdots=1;
        }
 
        /* parse in the flag used by forbidden words */
        if (strncmp(line,"KEEPCASE",8) == 0) {
-          if (parse_flag(line, &keepcase, "KEEPCASE")) {
-             fclose(afflst);
+          if (parse_flag(line, &keepcase, afflst)) {
+             delete afflst;
+             return 1;
+          }
+       }
+
+       /* parse in the flag used by the affix generator */
+       if (strncmp(line,"SUBSTANDARD",11) == 0) {
+          if (parse_flag(line, &substandard, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
        if (strncmp(line,"CHECKSHARPS",11) == 0) {
                    checksharps=1;
        }
 
@@ -664,25 +697,25 @@ int  AffixMgr::parse_file(const char * a
        if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P';
        if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S';
        if (ft != ' ') {
           if (dupflags_ini) {
             for (int i = 0; i < CONTSIZE; i++) dupflags[i] = 0;
             dupflags_ini = 0;
           }
           if (parse_affix(line, ft, afflst, dupflags)) {
-             fclose(afflst);
+             delete afflst;
              process_pfx_tree_to_list();
              process_sfx_tree_to_list();
              return 1;
           }
        }
 
     }
-    fclose(afflst);
+    delete afflst;
 
     // convert affix trees to sorted list
     process_pfx_tree_to_list();
     process_sfx_tree_to_list();
 
     // now we can speed up performance greatly taking advantage of the 
     // relationship between the affixes and the idea of "subsets".
 
@@ -705,27 +738,47 @@ int  AffixMgr::parse_file(const char * a
 
     // Since we have built ordered lists, all that remains is to properly intialize 
     // the nextne and nexteq pointers that relate them
 
     process_pfx_order();
     process_sfx_order();
 
     /* get encoding for CHECKCOMPOUNDCASE */
+    if (!utf8) {
     char * enc = get_encoding();
     csconv = get_current_cs(enc);
     free(enc);
     enc = NULL;
 
-    // temporary BREAK definition for German dash handling (OOo issue 64400)
-    if ((langnum == LANG_de) && (!breaktable)) {
-        breaktable = (char **) malloc(sizeof(char *));
+    char expw[MAXLNLEN];
+    if (wordchars) {
+        strcpy(expw, wordchars);
+        free(wordchars);
+    } else *expw = '\0';
+
+    for (int i = 0; i <= 255; i++) {
+        if ( (csconv[i].cupper != csconv[i].clower) &&
+            (! strchr(expw, (char) i))) {
+                *(expw + strlen(expw) + 1) = '\0';
+                *(expw + strlen(expw)) = (char) i;
+        }
+    }
+
+    wordchars = mystrdup(expw);
+    }
+
+    // default BREAK definition
+    if (!breaktable) {
+        breaktable = (char **) malloc(sizeof(char *) * 3);
         if (!breaktable) return 1;
         breaktable[0] = mystrdup("-");
-        numbreak = 1;
+        breaktable[1] = mystrdup("^-");
+        breaktable[2] = mystrdup("-$");
+        if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3;
     }
     return 0;
 }
 
 
 // we want to be able to quickly access prefix information
 // both by prefix flag, and sorted by prefix string itself 
 // so we need to set up two indexes
@@ -989,201 +1042,62 @@ int AffixMgr::process_sfx_order()
                  mptr = nptr;
              }
              if (mptr) mptr->setNextNE(NULL);
          }
     }
     return 0;
 }
 
-
-
-// takes aff file condition string and creates the
-// conds array - please see the appendix at the end of the
-// file affentry.cpp which describes what is going on here
-// in much more detail
+// add flags to the result for dictionary debugging
+void AffixMgr::debugflag(char * result, unsigned short flag) {
+    char * st = encode_flag(flag);
+    mystrcat(result, " ", MAXLNLEN);
+    mystrcat(result, MORPH_FLAG, MAXLNLEN);
+    if (st) {
+        mystrcat(result, st, MAXLNLEN);
+        free(st);
+    }
+}
+
+// calculate the character length of the condition
+int AffixMgr::condlen(char * st)
+{
+  int l = 0;
+  bool group = false;
+  for(; *st; st++) {
+    if (*st == '[') {
+        group = true;
+        l++;
+    } else if (*st == ']') group = false;
+    else if (!group && (!utf8 ||
+        (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++;
+  }
+  return l;
+}
 
 int AffixMgr::encodeit(struct affentry * ptr, char * cs)
 {
-  unsigned char c;
-  int i, j, k;
-  unsigned char mbr[MAXLNLEN];
-  w_char wmbr[MAXLNLEN];
-  w_char * wpos = wmbr;
-
-  // now clear the conditions array */
-  for (i=0;i<SETSIZE;i++) ptr->conds.base[i] = (unsigned char) 0;
-
-  // now parse the string to create the conds array */
-  int nc = strlen(cs);
-  unsigned char neg = 0;   // complement indicator
-  int grp = 0;   // group indicator
-  unsigned char n = 0;     // number of conditions
-  int ec = 0;    // end condition indicator
-  int nm = 0;    // number of member in group
-
-  // if no condition just return
-  if (strcmp(cs,".")==0) {
-    ptr->numconds = 0;
-    return 0;
-  }
-
-  i = 0;
-  while (i < nc) {
-    c = *((unsigned char *)(cs + i));
-
-    // start group indicator
-    if (c == '[') {
-       grp = 1;
-       c = 0;
-    }
-
-    // complement flag
-    if ((grp == 1) && (c == '^')) {
-       neg = 1;
-       c = 0;
-    }
-
-    // end goup indicator
-    if (c == ']') {
-       ec = 1;
-       c = 0;
-    }
-
-    // add character of group to list
-    if ((grp == 1) && (c != 0)) {
-      *(mbr + nm) = c;
-      nm++;
-      c = 0;
-    }
-
-    // end of condition 
-    if (c != 0) {
-       ec = 1;
+  if (strcmp(cs,".") != 0) {
+    ptr->numconds = (char) condlen(cs);
+    strncpy(ptr->c.conds, cs, MAXCONDLEN);
+    // long condition (end of conds padded by strncpy)
+    if (ptr->c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) {
+      ptr->opts += aeLONGCOND;
+      ptr->c.l.conds2 = mystrdup(cs + MAXCONDLEN_1);
+      if (!ptr->c.l.conds2) return 1;
     }
-
-  if (ec) {    
-    if (!utf8) {
-      if (grp == 1) {
-        if (neg == 0) {
-          // set the proper bits in the condition array vals for those chars
-          for (j=0;j<nm;j++) {
-             k = (unsigned int) mbr[j];
-             ptr->conds.base[k] = ptr->conds.base[k] | ((unsigned char)1 << n);
-          }
-        } else {
-          // complement so set all of them and then unset indicated ones
-           for (j=0;j<SETSIZE;j++) ptr->conds.base[j] = ptr->conds.base[j] | ((unsigned char)1 << n);
-           for (j=0;j<nm;j++) {
-             k = (unsigned int) mbr[j];
-             ptr->conds.base[k] = ptr->conds.base[k] & ~((unsigned char)1 << n);
-           }
-        }
-        neg = 0;
-        grp = 0;   
-        nm = 0;
-      } else {
-         // not a group so just set the proper bit for this char
-         // but first handle special case of . inside condition
-         if (c == '.') {
-            // wild card character so set them all
-            for (j=0;j<SETSIZE;j++) ptr->conds.base[j] = ptr->conds.base[j] | ((unsigned char)1 << n);
-         } else {  
-            ptr->conds.base[(unsigned int) c] = ptr->conds.base[(unsigned int)c] | ((unsigned char)1 << n);
-         }
-      }
-      n++;
-      ec = 0;
-    } else { // UTF-8 character set
-      if (grp == 1) {
-        ptr->conds.utf8.neg[n] = neg;
-        if (neg == 0) {
-          // set the proper bits in the condition array vals for those chars
-          for (j=0;j<nm;j++) {
-             k = (unsigned int) mbr[j];
-             if (k >> 7) {
-                u8_u16(wpos, 1, (char *) mbr + j);
-                wpos++;
-                if ((k & 0xe0) == 0xe0) j+=2; else j++; // 3-byte UTF-8 character
-             } else {
-                ptr->conds.utf8.ascii[k] = ptr->conds.utf8.ascii[k] | ((unsigned char)1 << n);
-             }
-          }
-        } else { // neg == 1
-          // complement so set all of them and then unset indicated ones
-           for (j=0;j<(SETSIZE/2);j++) ptr->conds.utf8.ascii[j] = ptr->conds.utf8.ascii[j] | ((unsigned char)1 << n);
-           for (j=0;j<nm;j++) {
-             k = (unsigned int) mbr[j];
-             if (k >> 7) {
-                u8_u16(wpos, 1, (char *) mbr + j);
-                wpos++;
-                if ((k & 0xe0) == 0xe0) j+=2; else j++; // 3-byte UTF-8 character
-             } else {
-                ptr->conds.utf8.ascii[k] = ptr->conds.utf8.ascii[k] & ~((unsigned char)1 << n);
-             }
-           }
-        }
-        neg = 0;
-        grp = 0;   
-        nm = 0;
-        ptr->conds.utf8.wlen[n] = wpos - wmbr;
-        if ((wpos - wmbr) != 0) {
-            ptr->conds.utf8.wchars[n] = (w_char *) malloc(sizeof(w_char) * (wpos - wmbr));
-            if (!ptr->conds.utf8.wchars[n]) return 1;
-            memcpy(ptr->conds.utf8.wchars[n], wmbr, sizeof(w_char) * (wpos - wmbr));
-            flag_qsort((unsigned short *) ptr->conds.utf8.wchars[n], 0, ptr->conds.utf8.wlen[n]);
-            wpos = wmbr;
-        }
-      } else { // grp == 0
-         // is UTF-8 character?
-         if (c >> 7) {
-            ptr->conds.utf8.wchars[n] = (w_char *) malloc(sizeof(w_char));
-            if (!ptr->conds.utf8.wchars[n]) return 1;
-            ptr->conds.utf8.wlen[n] = 1;
-            u8_u16(ptr->conds.utf8.wchars[n], 1, cs + i);
-            if ((c & 0xe0) == 0xe0) i+=2; else i++; // 3-byte UFT-8 character
-         } else {
-            ptr->conds.utf8.wchars[n] = NULL;
-            // not a group so just set the proper bit for this char
-            // but first handle special case of . inside condition
-            if (c == '.') {
-                ptr->conds.utf8.all[n] = 1;
-                // wild card character so set them all
-                for (j=0;j<(SETSIZE/2);j++) ptr->conds.utf8.ascii[j] = ptr->conds.utf8.ascii[j] | ((unsigned char)1 << n);
-            } else {
-                ptr->conds.utf8.all[n] = 0;
-                ptr->conds.utf8.ascii[(unsigned int) c] = ptr->conds.utf8.ascii[(unsigned int)c] | ((unsigned char)1 << n);
-            }
-         }
-         neg = 0;
-      }
-      n++;
-      ec = 0;
-      neg = 0;
-    }  
+  } else {
+    ptr->numconds = 0;
+    ptr->c.conds[0] = '\0';
   }
-
-    i++;
-  }
-  ptr->numconds = n;
   return 0;
 }
 
- // return 1 if s1 is a leading subset of s2
-/* inline int AffixMgr::isSubset(const char * s1, const char * s2)
- {
-    while ((*s1 == *s2) && *s1) {
-        s1++;
-        s2++;
-    }
-    return (*s1 == '\0');
- }
-*/
-
- // return 1 if s1 is a leading subset of s2 (dots are for infixes)
+// return 1 if s1 is a leading subset of s2 (dots are for infixes)
 inline int AffixMgr::isSubset(const char * s1, const char * s2)
  {
     while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) {
         s1++;
         s2++;
     }
     return (*s1 == '\0');
  }
@@ -1283,17 +1197,16 @@ struct hentry * AffixMgr::prefix_check_t
         } else {
              pptr = pptr->getNextNE();
         }
     }
     
     return NULL;
 }
 
-#ifdef HUNSPELL_EXPERIMENTAL
 // check word for prefixes
 char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound,
     const FLAG needflag)
 {
     char * st;
 
     char result[MAXLNLEN];
     result[0] = '\0';
@@ -1301,17 +1214,17 @@ char * AffixMgr::prefix_check_morph(cons
     pfx = NULL;
     sfxappnd = NULL;
     
     // first handle the special case of 0 length prefixes
     PfxEntry * pe = (PfxEntry *) pStart[0];
     while (pe) {
        st = pe->check_morph(word,len,in_compound, needflag);
        if (st) {
-            strcat(result, st);
+            mystrcat(result, st, MAXLNLEN);
             free(st);
        }
        // if (rv) return rv;
        pe = pe->getNext();
     }
   
     // now handle the general case
     unsigned char sp = *((const unsigned char *)word);
@@ -1319,17 +1232,17 @@ char * AffixMgr::prefix_check_morph(cons
 
     while (pptr) {
         if (isSubset(pptr->getKey(),word)) {
             st = pptr->check_morph(word,len,in_compound, needflag);
             if (st) {
               // fogemorpheme
               if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() && 
                         (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) {
-                    strcat(result, st);
+                    mystrcat(result, st, MAXLNLEN);
                     pfx = (AffEntry *)pptr;
                 }
                 free(st);
             }
             pptr = pptr->getNextEQ();
         } else {
             pptr = pptr->getNextNE();
         }
@@ -1352,45 +1265,43 @@ char * AffixMgr::prefix_check_twosfx_mor
     pfx = NULL;
     sfxappnd = NULL;
     
     // first handle the special case of 0 length prefixes
     PfxEntry * pe = (PfxEntry *) pStart[0];
     while (pe) {
         st = pe->check_twosfx_morph(word,len,in_compound, needflag);
         if (st) {
-            strcat(result, st);
+            mystrcat(result, st, MAXLNLEN);
             free(st);
         }
         pe = pe->getNext();
     }
   
     // now handle the general case
     unsigned char sp = *((const unsigned char *)word);
     PfxEntry * pptr = (PfxEntry *)pStart[sp];
 
     while (pptr) {
         if (isSubset(pptr->getKey(),word)) {
             st = pptr->check_twosfx_morph(word, len, in_compound, needflag);
             if (st) {
-                strcat(result, st);
+                mystrcat(result, st, MAXLNLEN);
                 free(st);
                 pfx = (AffEntry *)pptr;
             }
             pptr = pptr->getNextEQ();
         } else {
             pptr = pptr->getNextNE();
         }
     }
     
     if (*result) return mystrdup(result);
     return NULL;
 }
-#endif // END OF HUNSPELL_EXPERIMENTAL CODE
-
 
 // Is word a non compound with a REP substitution (see checkcompoundrep)?
 int AffixMgr::cpdrep_check(const char * word, int wl)
 {
   char candidate[MAXLNLEN];
   const char * r;
   int lenr, lenp;
 
@@ -1409,21 +1320,25 @@ int AffixMgr::cpdrep_check(const char * 
           if (candidate_check(candidate,strlen(candidate))) return 1;
           r++; // search for the next letter
       }
    }
    return 0;
 }
 
 // forbid compoundings when there are special patterns at word bound
-int AffixMgr::cpdpat_check(const char * word, int pos)
+int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2)
 {
   int len;
   for (int i = 0; i < numcheckcpd; i++) {
       if (isSubset(checkcpdtable[i].pattern2, word + pos) &&
+        (!r1 || !checkcpdtable[i].cond ||
+          (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) &&
+        (!r2 || !checkcpdtable[i].cond2 ||
+          (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) &&
         (len = strlen(checkcpdtable[i].pattern)) && (pos > len) &&
         (strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)) return 1;
   }
   return 0;
 }
 
 // forbid compounding with neighbouring upper and lower case characters at word bounds
 int AffixMgr::cpdcase_check(const char * word, int pos)
@@ -1431,41 +1346,62 @@ int AffixMgr::cpdcase_check(const char *
   if (utf8) {
       w_char u, w;
       const char * p;
       u8_u16(&u, 1, word + pos);
       for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--);
       u8_u16(&w, 1, p);
       unsigned short a = (u.h << 8) + u.l;
       unsigned short b = (w.h << 8) + w.l;
-      if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b))) return 1;
+      if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) &&
+          (a != '-') && (b != '-')) return 1;
   } else {
       unsigned char a = *(word + pos - 1);
       unsigned char b = *(word + pos);
       if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1;
   }
   return 0;
 }
 
 // check compound patterns
 int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all)
 {
   signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking
   signed short btwp[MAXWORDLEN]; // word positions for metacharacters
   int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions
   short bt = 0;  
-  int i;
+  int i, j;
   int ok;
   int w = 0;
+
   if (!*words) {
     w = 1;
     *words = def;
   }
   (*words)[wnum] = rv;
 
+  // has the last word COMPOUNDRULE flag?
+  if (rv->alen == 0) {
+    (*words)[wnum] = NULL;
+    if (w) *words = NULL;
+    return 0;
+  }
+  ok = 0;
+  for (i = 0; i < numdefcpd; i++) {
+    for (j = 0; j < defcpdtable[i].len; j++) {
+       if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' &&
+          TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) ok = 1;
+    }
+  }
+  if (ok == 0) {
+    (*words)[wnum] = NULL;
+    if (w) *words = NULL;
+    return 0;
+  }
+
   for (i = 0; i < numdefcpd; i++) {
     signed short pp = 0; // pattern position
     signed short wp = 0; // "words" position
     int ok2;
     ok = 1;
     ok2 = 1;
     do {
       while ((pp < defcpdtable[i].len) && (wp <= wnum)) {
@@ -1500,27 +1436,28 @@ int AffixMgr::defcpd_check(hentry *** wo
             if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0;
         }
       }
     if (ok && ok2) { 
         int r = pp;
         while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) &&
             ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2;
         if (defcpdtable[i].len <= r) return 1;
-    }    
+    }
     // backtrack
     if (bt) do {
         ok = 1;
         btnum[bt - 1]--;
         pp = btpp[bt - 1];
-        wp = btwp[bt - 1] + btnum[bt - 1];
+        wp = btwp[bt - 1] + (signed short) btnum[bt - 1];
     } while ((btnum[bt - 1] < 0) && --bt);
   } while (bt);
 
-  if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1; 
+  if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1;
+
   // check zero ending
   while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) &&
     ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2;
   if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1;
   }
   (*words)[wnum] = NULL;
   if (w) *words = NULL;
   return 0;
@@ -1558,122 +1495,147 @@ short AffixMgr::get_syllable(const char 
         for (; i > 0; i--) {
             if (flag_bsearch((unsigned short *) cpdvowels_utf16,
                 ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++;
         }
     }
     return num;
 }
 
+void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) {
+    if (utf8) {
+        int i;
+        for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) {
+          for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++);
+        }
+        for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) {
+          for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--);
+        }
+    } else {
+        *cmin = cpdmin;
+        *cmax = len - cpdmin + 1;
+    }
+}
+
 // check if compound word is correctly spelled
 // hu_mov_rule = spec. Hungarian rule (XXX)
 struct hentry * AffixMgr::compound_check(const char * word, int len, 
     short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL,
-    char hu_mov_rule = 0, int * cmpdstemnum = NULL, int * cmpdstem = NULL, char is_sug = 0)
+    char hu_mov_rule = 0, char is_sug = 0)
 {
     int i; 
     short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
-    int oldcmpdstemnum = 0;
     struct hentry * rv = NULL;
     struct hentry * rv_first;
     struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
     char st [MAXWORDUTF8LEN + 4];
     char ch;
     int cmin;
     int cmax;
-    
+    int striple = 0;
+    int scpd = 0;
+    int soldi = 0;
+    int oldcmin = 0;
+    int oldcmax = 0;
+    int oldlen = 0;
+    int checkedstriple = 0;
+
     int checked_prefix;
 
-#ifdef HUNSTEM
-    if (cmpdstemnum) {
-        if (wordnum == 0) {
-            *cmpdstemnum = 1;
-        } else {
-            (*cmpdstemnum)++;
-        }
-    }
-#endif
-    if (utf8) {
-        for (cmin = 0, i = 0; (i < cpdmin) && word[cmin]; i++) {
-          cmin++;
-          for (; (word[cmin] & 0xc0) == 0x80; cmin++);
-        }
-        for (cmax = len, i = 0; (i < (cpdmin - 1)) && cmax; i++) {
-          cmax--;
-          for (; (word[cmax] & 0xc0) == 0x80; cmax--);
-        }
-    } else {
-        cmin = cpdmin;
-        cmax = len - cpdmin + 1;
-    }
+    setcminmax(&cmin, &cmax, word, len);
 
     strcpy(st, word);
 
     for (i = cmin; i < cmax; i++) {
 
         oldnumsyllable = numsyllable;
         oldwordnum = wordnum;
         checked_prefix = 0;
 
         // go to end of the UTF-8 character
         if (utf8) {
             for (; (st[i] & 0xc0) == 0x80; i++);
             if (i >= cmax) return NULL;
         }
 
-        
+        do { // simplified checkcompoundpattern loop
+
+        if (scpd > 0) {
+          for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 ||
+            strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++);
+
+          if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop
+          strcpy(st + i, checkcpdtable[scpd-1].pattern);
+          soldi = i;
+          i += strlen(checkcpdtable[scpd-1].pattern);
+          strcpy(st + i, checkcpdtable[scpd-1].pattern2);
+          strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3));
+
+          oldlen = len;
+          len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3);
+          oldcmin = cmin;
+          oldcmax = cmax;
+          setcminmax(&cmin, &cmax, st, len);
+
+          cmax = len - cpdmin + 1;
+        }
+
+
         ch = st[i];
         st[i] = '\0';
 
         sfx = NULL;
         pfx = NULL;
-        
+
         // FIRST WORD
-        
+
         rv = lookup(st); // perhaps without prefix
 
         // search homonym with compound flag
         while ((rv) && !hu_mov_rule &&
-            ((pseudoroot && TESTAFF(rv->astr, pseudoroot, rv->alen)) ||
+            ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
                 !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
                   (compoundbegin && !wordnum &&
                         TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
                   (compoundmiddle && wordnum && !words &&
                     TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
                   (numdefcpd &&
                     ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
-                    (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))
-                  ))) {
+                    (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))) ||
+                  (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL &&
+                    !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)))
+                  ) {
             rv = rv->next_homonym;
         }
 
         if (!rv) {
             if (compoundflag && 
              !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
                 if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
                         FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
                     ((SfxEntry*)sfx)->getCont() &&
                         ((compoundforbidflag && TESTAFF(((SfxEntry*)sfx)->getCont(), compoundforbidflag, 
                             ((SfxEntry*)sfx)->getContLen())) || (compoundend &&
                         TESTAFF(((SfxEntry*)sfx)->getCont(), compoundend, 
                             ((SfxEntry*)sfx)->getContLen())))) {
                         rv = NULL;
                 }
             }
+
             if (rv ||
               (((wordnum == 0) && compoundbegin &&
                 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
               ((wordnum > 0) && compoundmiddle &&
                 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
               ) checked_prefix = 1;
-        // else check forbiddenwords and pseudoroot
+        // else check forbiddenwords and needaffix
         } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
-            TESTAFF(rv->astr, pseudoroot, rv->alen) || 
+            TESTAFF(rv->astr, needaffix, rv->alen) || 
             (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen))
              )) {
                 st[i] = ch;
                 continue;
         }
 
             // check non_compound flag in suffix and prefix
             if ((rv) && !hu_mov_rule &&
@@ -1691,27 +1653,27 @@ struct hentry * AffixMgr::compound_check
                 ((pfx && ((PfxEntry*)pfx)->getCont() &&
                     TESTAFF(((PfxEntry*)pfx)->getCont(), compoundend, 
                         ((PfxEntry*)pfx)->getContLen())) ||
                 (sfx && ((SfxEntry*)sfx)->getCont() &&
                     TESTAFF(((SfxEntry*)sfx)->getCont(), compoundend, 
                         ((SfxEntry*)sfx)->getContLen())))) {
                     rv = NULL;
             }
-            
+
             // check compoundmiddle flag in suffix and prefix
             if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule &&
                 ((pfx && ((PfxEntry*)pfx)->getCont() &&
                     TESTAFF(((PfxEntry*)pfx)->getCont(), compoundmiddle, 
                         ((PfxEntry*)pfx)->getContLen())) ||
                 (sfx && ((SfxEntry*)sfx)->getCont() &&
                     TESTAFF(((SfxEntry*)sfx)->getCont(), compoundmiddle, 
                         ((SfxEntry*)sfx)->getContLen())))) {
                     rv = NULL;
-            }       
+            }
 
         // check forbiddenwords
         if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
             (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
                 return NULL;
             }
 
         // increment word number, if the second root has a compoundroot flag
@@ -1731,69 +1693,81 @@ struct hentry * AffixMgr::compound_check
 // LANG_hu section: spec. Hungarian rule
             || ((langnum == LANG_hu) && hu_mov_rule && (
                     TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes
                     TESTAFF(rv->astr, 'G', rv->alen) ||
                     TESTAFF(rv->astr, 'H', rv->alen)
                 )
               )
 // END of LANG_hu section
+          ) &&
+          (
+             // test CHECKCOMPOUNDPATTERN conditions
+             scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL || 
+                TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen)
           )
-          && ! (( checkcompoundtriple && // test triple letters
+          && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters
                    (word[i-1]==word[i]) && (
-                      ((i>1) && (word[i-1]==word[i-2])) || 
+                      ((i>1) && (word[i-1]==word[i-2])) ||
                       ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
                    )
                ) ||
-               ( 
-                 // test CHECKCOMPOUNDPATTERN
-                 numcheckcpd && cpdpat_check(word, i)
-               ) ||
-               ( 
-                 checkcompoundcase && cpdcase_check(word, i)
+               (
+                 checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i)
                ))
          )
 // LANG_hu section: spec. Hungarian rule
          || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
               (sfx && ((SfxEntry*)sfx)->getCont() && ( // XXX hardwired Hungarian dic. codes
                         TESTAFF(((SfxEntry*)sfx)->getCont(), (unsigned short) 'x', ((SfxEntry*)sfx)->getContLen()) ||
                         TESTAFF(((SfxEntry*)sfx)->getCont(), (unsigned short) '%', ((SfxEntry*)sfx)->getContLen())
-                    )                
+                    )
                )
              )
-// END of LANG_hu section
-         ) {
+         ) { // first word is ok condition
 
 // LANG_hu section: spec. Hungarian rule
             if (langnum == LANG_hu) {
-                // calculate syllable number of the word            
+                // calculate syllable number of the word
                 numsyllable += get_syllable(st, i);
 
                 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
                 if (pfx && (get_syllable(((PfxEntry *)pfx)->getKey(),strlen(((PfxEntry *)pfx)->getKey())) > 1)) wordnum++;
             }
 // END of LANG_hu section
 
-#ifdef HUNSTEM
-            if (cmpdstem) cmpdstem[*cmpdstemnum - 1] = i;
-#endif
 
             // NEXT WORD(S)
             rv_first = rv;
-            rv = lookup((word+i)); // perhaps without prefix
+            st[i] = ch;
+
+        do { // striple loop
+
+            // check simplifiedtriple
+            if (simplifiedtriple) { 
+              if (striple) { 
+                checkedstriple = 1;
+                i--; // check "fahrt" instead of "ahrt" in "Schiffahrt"
+              } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1;
+            }
+
+            rv = lookup((st+i)); // perhaps without prefix
 
         // search homonym with compound flag
-        while ((rv) && ((pseudoroot && TESTAFF(rv->astr, pseudoroot, rv->alen)) ||
+        while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
                         !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
                           (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
-                           (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) {
+                           (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) ||
+                             (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL &&
+                                !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
+                           )) {
             rv = rv->next_homonym;
         }
 
-            if (rv && words && words[wnum + 1]) return rv;
+            if (rv && words && words[wnum + 1]) return rv_first;
 
             oldnumsyllable2 = numsyllable;
             oldwordnum2 = wordnum;
 
 // LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code
             if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) {
                 numsyllable--;
             }
@@ -1815,48 +1789,62 @@ struct hentry * AffixMgr::compound_check
             // then the syllable number of root words must be 6, or lesser.
 
             if ((rv) && (
                       (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
                       (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
                     )
                 && (
                       ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) || 
-                      ((cpdmaxsyllable==0) || 
-                          (numsyllable + get_syllable(&(rv->word), rv->clen)<=cpdmaxsyllable))
-                    )
-                && (
+                      ((cpdmaxsyllable!=0) && 
+                          (numsyllable + get_syllable(HENTRY_WORD(rv), rv->clen)<=cpdmaxsyllable))
+                    ) &&
+               (
+                 // test CHECKCOMPOUNDPATTERN
+                 !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv)
+               ) &&
+                (
                      (!checkcompounddup || (rv != rv_first))
                    )
+            // test CHECKCOMPOUNDPATTERN conditions
+                && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL ||
+                      TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))
                 )
                  {
                       // forbid compound word, if it is a non compound word with typical fault
                       if (checkcompoundrep && cpdrep_check(word,len)) return NULL;
-                      return rv;
+                      return rv_first;
             }
 
-            numsyllable = oldnumsyllable2 ;
+            numsyllable = oldnumsyllable2;
             wordnum = oldwordnum2;
 
             // perhaps second word has prefix or/and suffix
             sfx = NULL;
             sfxflag = FLAG_NULL;
             rv = (compoundflag) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL;
             if (!rv && compoundend) {
                 sfx = NULL;
                 pfx = NULL;
                 rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END);
             }
-            
+
             if (!rv && numdefcpd && words) {
                 rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
-                if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv;
+                if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first;
                 rv = NULL;
             }
 
+            // test CHECKCOMPOUNDPATTERN conditions (allowed forms)
+            if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL || 
+                TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL;
+
+            // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds)
+            if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv)) rv = NULL;
+
             // check non_compound flag in suffix and prefix
             if ((rv) && 
                 ((pfx && ((PfxEntry*)pfx)->getCont() &&
                     TESTAFF(((PfxEntry*)pfx)->getCont(), compoundforbidflag, 
                         ((PfxEntry*)pfx)->getContLen())) ||
                 (sfx && ((SfxEntry*)sfx)->getCont() &&
                     TESTAFF(((SfxEntry*)sfx)->getCont(), compoundforbidflag, 
                         ((SfxEntry*)sfx)->getContLen())))) {
@@ -1870,229 +1858,233 @@ struct hentry * AffixMgr::compound_check
             // pfxappnd = prefix of word+i, or NULL
             // calculate syllable number of prefix.
             // hungarian convention: when syllable number of prefix is more,
             // than 1, the prefix+word counts as two words.
 
             if (langnum == LANG_hu) {
                 // calculate syllable number of the word
                 numsyllable += get_syllable(word + i, strlen(word + i));
-                
+
                 // - affix syllable num.
                 // XXX only second suffix (inflections, not derivations)
                 if (sfxappnd) {
                     char * tmp = myrevstrdup(sfxappnd);
                     numsyllable -= get_syllable(tmp, strlen(tmp));
                     free(tmp);
                 }
-                
+
                 // + 1 word, if syllable number of the prefix > 1 (hungarian convention)
                 if (pfx && (get_syllable(((PfxEntry *)pfx)->getKey(),strlen(((PfxEntry *)pfx)->getKey())) > 1)) wordnum++;
 
                 // increment syllable num, if last word has a SYLLABLENUM flag
                 // and the suffix is beginning `s'
-            
+
                 if (cpdsyllablenum) {
                     switch (sfxflag) {
                         case 'c': { numsyllable+=2; break; }
                         case 'J': { numsyllable += 1; break; }
                         case 'I': { if (TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; }
                     }
                 }
             }
-            
+
             // increment word number, if the second word has a compoundroot flag
             if ((rv) && (compoundroot) && 
                 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
                     wordnum++;
             }
 
             // second word is acceptable, as a word with prefix or/and suffix?
             // hungarian conventions: compounding is acceptable,
             // when compound forms consist 2 word, otherwise
             // the syllable number of root words is 6, or lesser.
             if ((rv) && 
                     (
                       ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) || 
-                      ((cpdmaxsyllable == 0) || 
+                      ((cpdmaxsyllable != 0) && 
                           (numsyllable <= cpdmaxsyllable))
                     )
                 && (
                    (!checkcompounddup || (rv != rv_first))
                    )) {
                     // forbid compound word, if it is a non compound word with typical fault
                     if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
-                    return rv;
+                    return rv_first;
             }
 
             numsyllable = oldnumsyllable2;
             wordnum = oldwordnum2;
-#ifdef HUNSTEM
-            if (cmpdstemnum) oldcmpdstemnum = *cmpdstemnum;
-#endif
+
             // perhaps second word is a compound word (recursive call)
             if (wordnum < maxwordnum) {
-                rv = compound_check((word+i),strlen(word+i), wordnum+1,
-                     numsyllable, maxwordnum, wnum + 1, words,
-                     0, cmpdstemnum, cmpdstem, is_sug);
+                rv = compound_check((st+i),strlen(st+i), wordnum+1,
+                     numsyllable, maxwordnum, wnum + 1, words, 0, is_sug);
+                if (rv && numcheckcpd && (scpd == 0 && cpdpat_check(word, i, rv_first, rv) ||
+                   scpd != 0 && !cpdpat_check(word, i, rv_first, rv))) rv = NULL;
             } else {
                 rv=NULL;
             }
             if (rv) {
                 // forbid compound word, if it is a non compound word with typical fault
                 if (checkcompoundrep && cpdrep_check(word, len)) return NULL;
-                return rv;
-            } else {
-#ifdef HUNSTEM
-            if (cmpdstemnum) *cmpdstemnum = oldcmpdstemnum;
-#endif
+                return rv_first;
             }
+          } while (striple && !checkedstriple); // end of striple loop
+
+          if (checkedstriple) {
+            i++;
+            checkedstriple = 0;
+            striple = 0;
+          }
+
+        } // first word is ok condition
+
+        if (soldi != 0) {
+          i = soldi;
+          soldi = 0;
+          len = oldlen;
+          cmin = oldcmin;
+          cmax = oldcmax;
         }
-        st[i] = ch;
+        scpd++;
+
+        } while (simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop
+
+        if (soldi != 0) {
+          i = soldi;
+          strcpy(st, word); // XXX add more optim.
+          soldi = 0;
+        } else st[i] = ch;
+
+        scpd = 0;
         wordnum = oldwordnum;
         numsyllable = oldnumsyllable;
     }
-    
+
     return NULL;
-}    
-
-#ifdef HUNSPELL_EXPERIMENTAL
+}
+
 // check if compound word is correctly spelled
 // hu_mov_rule = spec. Hungarian rule (XXX)
 int AffixMgr::compound_check_morph(const char * word, int len, 
     short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words,
     char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL)
 {
     int i;
     short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
     int ok = 0;
 
     struct hentry * rv = NULL;
     struct hentry * rv_first;
     struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking
     char st [MAXWORDUTF8LEN + 4];
     char ch;
-    
+
     int checked_prefix;
     char presult[MAXLNLEN];
 
     int cmin;
     int cmax;
-    
-    if (utf8) {
-        for (cmin = 0, i = 0; (i < cpdmin) && word[cmin]; i++) {
-          cmin++;
-          for (; (word[cmin] & 0xc0) == 0x80; cmin++);
-        }
-        for (cmax = len, i = 0; (i < (cpdmin - 1)) && cmax; i++) {
-          cmax--;
-          for (; (word[cmax] & 0xc0) == 0x80; cmax--);
-        }
-    } else {
-        cmin = cpdmin;
-        cmax = len - cpdmin + 1;
-    }
+
+    setcminmax(&cmin, &cmax, word, len);
 
     strcpy(st, word);
 
     for (i = cmin; i < cmax; i++) {
         oldnumsyllable = numsyllable;
         oldwordnum = wordnum;
         checked_prefix = 0;
 
         // go to end of the UTF-8 character
         if (utf8) {
             for (; (st[i] & 0xc0) == 0x80; i++);
             if (i >= cmax) return 0;
         }
-        
+
         ch = st[i];
         st[i] = '\0';
         sfx = NULL;
 
         // FIRST WORD
         *presult = '\0';
         if (partresult) strcat(presult, partresult);
-        
+
         rv = lookup(st); // perhaps without prefix
 
         // search homonym with compound flag
         while ((rv) && !hu_mov_rule && 
-            ((pseudoroot && TESTAFF(rv->astr, pseudoroot, rv->alen)) ||
+            ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
                 !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
                 (compoundbegin && !wordnum &&
                         TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
                 (compoundmiddle && wordnum && !words &&
                     TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
                   (numdefcpd &&
                     ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) ||
                     (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))
                   ))) {
             rv = rv->next_homonym;
         }
 
         if (rv)  {
-            if (rv->description) {
-                if ((!rv->astr) || !TESTAFF(rv->astr, lemma_present, rv->alen))
-                                        strcat(presult, st);
-                strcat(presult, rv->description);
+            sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st);
+            if (!HENTRY_FIND(rv, MORPH_STEM)) {
+                sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st);
             }
-        }
-        
+            // store the pointer of the hash entry
+//            sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv);
+            if (HENTRY_DATA(rv)) {
+                sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv));
+            }
+        }        
         if (!rv) {
             if (compoundflag && 
              !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) {
                 if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL,
                         FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule &&
                     ((SfxEntry*)sfx)->getCont() &&
                         ((compoundforbidflag && TESTAFF(((SfxEntry*)sfx)->getCont(), compoundforbidflag, 
                             ((SfxEntry*)sfx)->getContLen())) || (compoundend &&
                         TESTAFF(((SfxEntry*)sfx)->getCont(), compoundend, 
                             ((SfxEntry*)sfx)->getContLen())))) {
                         rv = NULL;
                 }
             }
-            
+
             if (rv ||
               (((wordnum == 0) && compoundbegin &&
                 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) ||
               ((wordnum > 0) && compoundmiddle &&
                 ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
                 (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle)))))
               ) {
-                //char * p = prefix_check_morph(st, i, 0, compound);
+                // char * p = prefix_check_morph(st, i, 0, compound);
                 char * p = NULL;
                 if (compoundflag) p = affix_check_morph(st, i, compoundflag);
                 if (!p || (*p == '\0')) {
+                   if (p) free(p);
+                   p = NULL;
                    if ((wordnum == 0) && compoundbegin) {
                      p = affix_check_morph(st, i, compoundbegin);
                    } else if ((wordnum > 0) && compoundmiddle) {
                      p = affix_check_morph(st, i, compoundmiddle);                   
                    }
                 }
-                if (*p != '\0') {
-                    line_uniq(p);
-                    if (strchr(p, '\n')) {
-                        strcat(presult, "(");
-                        strcat(presult, line_join(p, '|'));
-                        strcat(presult, ")");
-                      } else {
-                        strcat(presult, p);
-                      }
+                if (p && (*p != '\0')) {
+                    sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD,
+                        MORPH_PART, st, line_uniq_app(&p, MSEP_REC));
                 }
-                if (presult[strlen(presult) - 1] == '\n') {
-                    presult[strlen(presult) - 1] = '\0';
-                }
+                if (p) free(p);
                 checked_prefix = 1;
-                //strcat(presult, "+");
             }
         // else check forbiddenwords
         } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
-            TESTAFF(rv->astr, pseudoroot, rv->alen))) {
+            TESTAFF(rv->astr, needaffix, rv->alen))) {
                 st[i] = ch;
                 continue;
         }
 
             // check non_compound flag in suffix and prefix
             if ((rv) && !hu_mov_rule &&
                 ((pfx && ((PfxEntry*)pfx)->getCont() &&
                     TESTAFF(((PfxEntry*)pfx)->getCont(), compoundforbidflag, 
@@ -2145,28 +2137,28 @@ int AffixMgr::compound_check_morph(const
                 hu_mov_rule && (
                     TESTAFF(rv->astr, 'F', rv->alen) ||
                     TESTAFF(rv->astr, 'G', rv->alen) ||
                     TESTAFF(rv->astr, 'H', rv->alen)
                 )
               )
 // END of LANG_hu section
           )
-          && ! (( checkcompoundtriple && // test triple letters
+          && ! (( checkcompoundtriple && !words && // test triple letters
                    (word[i-1]==word[i]) && (
                       ((i>1) && (word[i-1]==word[i-2])) || 
                       ((word[i-1]==word[i+1])) // may be word[i+1] == '\0'
                    )
                ) ||
                (
                    // test CHECKCOMPOUNDPATTERN
-                   numcheckcpd && cpdpat_check(word, i)
+                   numcheckcpd && !words && cpdpat_check(word, i, rv, NULL)
                ) ||
                ( 
-                 checkcompoundcase && cpdcase_check(word, i)
+                 checkcompoundcase && !words && cpdcase_check(word, i)
                ))
          )
 // LANG_hu section: spec. Hungarian rule
          || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) &&
               (sfx && ((SfxEntry*)sfx)->getCont() && (
                         TESTAFF(((SfxEntry*)sfx)->getCont(), (unsigned short) 'x', ((SfxEntry*)sfx)->getContLen()) ||
                         TESTAFF(((SfxEntry*)sfx)->getCont(), (unsigned short) '%', ((SfxEntry*)sfx)->getContLen())
                     )                
@@ -2185,30 +2177,40 @@ int AffixMgr::compound_check_morph(const
             }
 // END of LANG_hu section
 
             // NEXT WORD(S)
             rv_first = rv;
             rv = lookup((word+i)); // perhaps without prefix
 
         // search homonym with compound flag
-        while ((rv) && ((pseudoroot && TESTAFF(rv->astr, pseudoroot, rv->alen)) ||
+        while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
                         !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
                           (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) ||
                            (numdefcpd && defcpd_check(&words, wnum + 1, rv, NULL,1))))) {
             rv = rv->next_homonym;
         }
 
             if (rv && words && words[wnum + 1]) {
                   strcat(*result, presult);
-                  if (complexprefixes && rv->description) strcat(*result, rv->description);
-                  if (rv->description && ((!rv->astr) || 
-                     !TESTAFF(rv->astr, lemma_present, rv->alen)))
-                        strcat(*result, &(rv->word));
-                  if (!complexprefixes && rv->description) strcat(*result, rv->description);
+                  strcat(*result, " ");
+                  strcat(*result, MORPH_PART);
+                  strcat(*result, word+i);
+                  if (complexprefixes && HENTRY_DATA(rv)) strcat(*result, HENTRY_DATA2(rv));
+                  if (!HENTRY_FIND(rv, MORPH_STEM)) {
+                    strcat(*result, " ");
+                    strcat(*result, MORPH_STEM);
+                    strcat(*result, HENTRY_WORD(rv));
+                  }
+                  // store the pointer of the hash entry
+//                  sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
+                  if (!complexprefixes && HENTRY_DATA(rv)) {
+                    strcat(*result, " ");
+                    strcat(*result, HENTRY_DATA2(rv));
+                  }
                   strcat(*result, "\n");
                   ok = 1;
                   return 0;
             }
 
             oldnumsyllable2 = numsyllable;
             oldwordnum2 = wordnum;
 
@@ -2223,43 +2225,54 @@ int AffixMgr::compound_check_morph(const
                     wordnum++;
             }
 
             // check forbiddenwords
             if ((rv) && (rv->astr) && TESTAFF(rv->astr, forbiddenword, rv->alen)) {
                 st[i] = ch;
                 continue;
             }
-                    
+
             // second word is acceptable, as a root?
             // hungarian conventions: compounding is acceptable,
             // when compound forms consist of 2 words, or if more,
             // then the syllable number of root words must be 6, or lesser.
             if ((rv) && (
                       (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
                       (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))
                     )
                 && (
                       ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) || 
-                      ((cpdmaxsyllable==0) || 
-                          (numsyllable+get_syllable(&(rv->word),rv->wlen)<=cpdmaxsyllable))
+                      ((cpdmaxsyllable!=0) &&
+                          (numsyllable+get_syllable(HENTRY_WORD(rv),rv->blen)<=cpdmaxsyllable))
                     )
                 && (
                      (!checkcompounddup || (rv != rv_first))
                    )
                 )
                  {
                       // bad compound word
                       strcat(*result, presult);
-                                          
-                      if (rv->description) {
-                        if (complexprefixes) strcat(*result, rv->description);
-                        if ((!rv->astr) || !TESTAFF(rv->astr, lemma_present, rv->alen))
-                                               strcat(*result, &(rv->word));
-                        if (!complexprefixes) strcat(*result, rv->description);
+                      strcat(*result, " ");
+                      strcat(*result, MORPH_PART);
+                      strcat(*result, word+i);
+
+                      if (HENTRY_DATA(rv)) {
+                        if (complexprefixes) strcat(*result, HENTRY_DATA2(rv));
+                        if (! HENTRY_FIND(rv, MORPH_STEM)) {
+                           strcat(*result, " ");
+                           strcat(*result, MORPH_STEM);
+                           strcat(*result, HENTRY_WORD(rv));
+                        }
+                        // store the pointer of the hash entry
+//                        sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv);
+                        if (!complexprefixes) {
+                            strcat(*result, " ");
+                            strcat(*result, HENTRY_DATA2(rv));
+                        }
                       }
                       strcat(*result, "\n");
                               ok = 1;
             }
 
             numsyllable = oldnumsyllable2 ;
             wordnum = oldwordnum2;
 
@@ -2275,30 +2288,26 @@ int AffixMgr::compound_check_morph(const
                 rv = affix_check((word+i),strlen(word+i), compoundend);
             }
 
             if (!rv && numdefcpd && words) {
                 rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END);
                 if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
                       char * m = NULL;
                       if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
-                      if ((!m || *m == '\0') && compoundend)
+                      if ((!m || *m == '\0') && compoundend) {
+                            if (m) free(m);
                             m = affix_check_morph((word+i),strlen(word+i), compoundend);
+                      }
                       strcat(*result, presult);
-                      if (m) {
-                        line_uniq(m);
-                        if (strchr(m, '\n')) {
-                            strcat(*result, "(");
-                            strcat(*result, line_join(m, '|'));
-                            strcat(*result, ")");
-                        } else {
-                            strcat(*result, m);
-                        }
-                        free(m);
+                      if (m || (*m != '\0')) {
+                        sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
+                            MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
                       }
+                      if (m) free(m);
                       strcat(*result, "\n");
                       ok = 1;
                 }
             }
 
             // check non_compound flag in suffix and prefix
             if ((rv) && 
                 ((pfx && ((PfxEntry*)pfx)->getCont() &&
@@ -2307,17 +2316,17 @@ int AffixMgr::compound_check_morph(const
                 (sfx && ((SfxEntry*)sfx)->getCont() &&
                     TESTAFF(((SfxEntry*)sfx)->getCont(), compoundforbidflag, 
                         ((SfxEntry*)sfx)->getContLen())))) {
                     rv = NULL;
             }
 
             // check forbiddenwords
             if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen))
-                    && (! TESTAFF(rv->astr, pseudoroot, rv->alen))) {
+                    && (! TESTAFF(rv->astr, needaffix, rv->alen))) {
                         st[i] = ch;
                         continue;
                     }
 
             if (langnum == LANG_hu) {
                 // calculate syllable number of the word
                 numsyllable += get_syllable(word + i, strlen(word + i));
 
@@ -2351,39 +2360,35 @@ int AffixMgr::compound_check_morph(const
             }
             // second word is acceptable, as a word with prefix or/and suffix?
             // hungarian conventions: compounding is acceptable,
             // when compound forms consist 2 word, otherwise
             // the syllable number of root words is 6, or lesser.
             if ((rv) && 
                     (
                       ((cpdwordmax==-1) || (wordnum+1<cpdwordmax)) || 
-                      ((cpdmaxsyllable==0) || 
+                      ((cpdmaxsyllable!=0) &&
                           (numsyllable <= cpdmaxsyllable))
                     )
                 && (
                    (!checkcompounddup || (rv != rv_first))
                    )) {
                       char * m = NULL;
                       if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag);
-                      if ((!m || *m == '\0') && compoundend)
+                      if ((!m || *m == '\0') && compoundend) {
+                            if (m) free(m);
                             m = affix_check_morph((word+i),strlen(word+i), compoundend);
+                      }
                       strcat(*result, presult);
-                      if (m) {
-                        line_uniq(m);
-                        if (strchr(m, '\n')) {
-                            strcat(*result, "(");
-                            strcat(*result, line_join(m, '|'));
-                            strcat(*result, ")");
-                        } else {
-                            strcat(*result, m);
-                        }
-                        free(m);
+                      if (m && (*m != '\0')) {
+                        sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD,
+                            MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC));
                       }
-                      strcat(*result, "\n");
+                      if (m) free(m);
+                      sprintf(*result + strlen(*result), "%c", MSEP_REC);
                       ok = 1;
             }
 
             numsyllable = oldnumsyllable2;
             wordnum = oldwordnum2;
 
             // perhaps second word is a compound word (recursive call)
             if ((wordnum < maxwordnum) && (ok == 0)) {
@@ -2394,17 +2399,16 @@ int AffixMgr::compound_check_morph(const
             }
         }
         st[i] = ch;
         wordnum = oldwordnum;
         numsyllable = oldnumsyllable;
     }
     return 0;
 }    
-#endif // END OF HUNSPELL_EXPERIMENTAL CODE
 
  // return 1 if s1 (reversed) is a leading subset of end of s2
 /* inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len)
  {
     while ((len > 0) && *s1 && (*s1 == *end_of_s2)) {
         s1++;
         end_of_s2--;
         len--;
@@ -2425,18 +2429,16 @@ inline int AffixMgr::isRevSubset(const c
 
 // check word for suffixes
 
 struct hentry * AffixMgr::suffix_check (const char * word, int len, 
        int sfxopts, AffEntry * ppfx, char ** wlst, int maxSug, int * ns, 
        const FLAG cclass, const FLAG needflag, char in_compound)
 {
     struct hentry * rv = NULL;
-    char result[MAXLNLEN];
-
     PfxEntry* ep = (PfxEntry *) ppfx;
 
     // first handle the special case of 0 length suffixes
     SfxEntry * se = (SfxEntry *) sStart[0];
 
     while (se) {
         if (!cclass || se->getCont()) {
             // suffixes are not allowed in beginning of compounds
@@ -2450,21 +2452,21 @@ struct hentry * AffixMgr::suffix_check (
                (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
               // circumfix flag in prefix AND suffix
               ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
                    circumfix, ep->getContLen())) &&
                (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen())))))  &&
             // fogemorpheme
               (in_compound || 
                  !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) &&
-            // pseudoroot on prefix or first suffix
+            // needaffix on prefix or first suffix
               (cclass || 
-                   !(se->getCont() && TESTAFF(se->getCont(), pseudoroot, se->getContLen())) ||
+                   !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
                    (ppfx && !((ep->getCont()) &&
-                     TESTAFF(ep->getCont(), pseudoroot,
+                     TESTAFF(ep->getCont(), needaffix,
                        ep->getContLen())))
               )
             ) {
                 rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass, 
                     needflag, (in_compound ? 0 : onlyincompound));
                 if (rv) {
                     sfx=(AffEntry *)se; // BUG: sfx not stateless
                     return rv;
@@ -2492,41 +2494,30 @@ struct hentry * AffixMgr::suffix_check (
                (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
               // circumfix flag in prefix AND suffix
               ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
                    circumfix, ep->getContLen())) &&
                (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))))  &&
             // fogemorpheme
               (in_compound || 
                  !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
-            // pseudoroot on prefix or first suffix
+            // needaffix on prefix or first suffix
               (cclass || 
-                  !(sptr->getCont() && TESTAFF(sptr->getCont(), pseudoroot, sptr->getContLen())) ||
+                  !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
                   (ppfx && !((ep->getCont()) &&
-                     TESTAFF(ep->getCont(), pseudoroot,
+                     TESTAFF(ep->getCont(), needaffix,
                        ep->getContLen())))
               )
             ) {
                 rv = sptr->checkword(word,len, sfxopts, ppfx, wlst,
                     maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound));
                 if (rv) {
                     sfx=(AffEntry *)sptr; // BUG: sfx not stateless
                     sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
                     if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
-                    if (cclass || sptr->getCont()) {
-                                if (!derived) {
-                                        derived = mystrdup(word);
-                                } else {
-                                        strcpy(result, derived); // XXX check size
-                                        strcat(result, "\n");
-                                        strcat(result, word);
-                                        free(derived);
-                                        derived = mystrdup(result);
-                                }
-                    }
                     return rv;
                 }
              }
              sptr = sptr->getNextEQ();
         } else {
              sptr = sptr->getNextNE();
         }
     }
@@ -2571,17 +2562,16 @@ struct hentry * AffixMgr::suffix_check_t
         } else {
              sptr = sptr->getNextNE();
         }
     }
 
     return NULL;
 }
 
-#ifdef HUNSPELL_EXPERIMENTAL
 char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len, 
        int sfxopts, AffEntry * ppfx, const FLAG needflag)
 {
     char result[MAXLNLEN];
     char result2[MAXLNLEN];
     char result3[MAXLNLEN];
     
     char * st;
@@ -2593,22 +2583,28 @@ char * AffixMgr::suffix_check_twosfx_mor
     // first handle the special case of 0 length suffixes
     SfxEntry * se = (SfxEntry *) sStart[0];
     while (se) {
         if (contclasses[se->getFlag()])
         {
             st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
             if (st) {
                 if (ppfx) {
-                    if (((PfxEntry *) ppfx)->getMorph()) strcat(result, ((PfxEntry *) ppfx)->getMorph());
+                    if (((PfxEntry *) ppfx)->getMorph()) {
+                        mystrcat(result, ((PfxEntry *) ppfx)->getMorph(), MAXLNLEN);
+                        mystrcat(result, " ", MAXLNLEN);
+                    } else debugflag(result, ((PfxEntry *) ppfx)->getFlag());
                 }
-                strcat(result, st);
+                mystrcat(result, st, MAXLNLEN);
                 free(st);
-                if (se->getMorph()) strcat(result, se->getMorph());
-                strcat(result, "\n");
+                if (se->getMorph()) {
+                    mystrcat(result, " ", MAXLNLEN);
+                    mystrcat(result, se->getMorph(), MAXLNLEN);
+                } else debugflag(result, se->getFlag());
+                mystrcat(result, "\n", MAXLNLEN);
             }
         }
         se = se->getNext();
     }
   
     // now handle the general case
     unsigned char sp = *((const unsigned char *)(word + len - 1));
     SfxEntry * sptr = (SfxEntry *) sStart[sp];
@@ -2620,37 +2616,32 @@ char * AffixMgr::suffix_check_twosfx_mor
                 st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag);
                 if (st) {
                     sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless
                     if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless
                     strcpy(result2, st);
                     free(st);
 
                 result3[0] = '\0';
-#ifdef DEBUG
-                unsigned short flag = sptr->getFlag();
-                if (flag_mode == FLAG_NUM) {
-                    sprintf(result3, "<%d>", sptr->getKey());
-                } else if (flag_mode == FLAG_LONG) {
-                    sprintf(result3, "<%c%c>", flag >> 8, (flag << 8) >>8);
-                } else sprintf(result3, "<%c>", flag);
-                strcat(result3, ":");
-#endif
-                if (sptr->getMorph()) strcat(result3, sptr->getMorph());
+
+                if (sptr->getMorph()) {
+                    mystrcat(result3, " ", MAXLNLEN);
+                    mystrcat(result3, sptr->getMorph(), MAXLNLEN);
+                } else debugflag(result3, sptr->getFlag());
                 strlinecat(result2, result3);
-                strcat(result2, "\n");
-                strcat(result,  result2);
+                mystrcat(result2, "\n", MAXLNLEN);
+                mystrcat(result,  result2, MAXLNLEN);
                 }
             }
             sptr = sptr->getNextEQ();
         } else {
              sptr = sptr->getNextNE();
         }
     }
-    if (result) return mystrdup(result);
+    if (*result) return mystrdup(result);
     return NULL;
 }
 
 char * AffixMgr::suffix_check_morph(const char * word, int len, 
        int sfxopts, AffEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound)
 {
     char result[MAXLNLEN];
     
@@ -2675,36 +2666,50 @@ char * AffixMgr::suffix_check_morph(cons
                (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) ||
               // circumfix flag in prefix AND suffix
               ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
                    circumfix, ep->getContLen())) &&
                (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen())))))  &&
             // fogemorpheme
               (in_compound || 
                  !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) &&
-            // pseudoroot on prefix or first suffix
+            // needaffix on prefix or first suffix
               (cclass || 
-                   !(se->getCont() && TESTAFF(se->getCont(), pseudoroot, se->getContLen())) ||
+                   !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
                    (ppfx && !((ep->getCont()) &&
-                     TESTAFF(ep->getCont(), pseudoroot,
+                     TESTAFF(ep->getCont(), needaffix,
                        ep->getContLen())))
               )
             ))
             rv = se->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
          while (rv) {
            if (ppfx) {
-                if (((PfxEntry *) ppfx)->getMorph()) strcat(result, ((PfxEntry *) ppfx)->getMorph());
+                if (((PfxEntry *) ppfx)->getMorph()) {
+                    mystrcat(result, ((PfxEntry *) ppfx)->getMorph(), MAXLNLEN);
+                    mystrcat(result, " ", MAXLNLEN);
+                } else debugflag(result, ((PfxEntry *) ppfx)->getFlag());
+            }
+            if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+            if (! HENTRY_FIND(rv, MORPH_STEM)) {
+                mystrcat(result, " ", MAXLNLEN);                                
+                mystrcat(result, MORPH_STEM, MAXLNLEN);
+                mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
             }
-            if (complexprefixes && rv->description) strcat(result, rv->description);
-            if (rv->description && ((!rv->astr) || 
-                                        !TESTAFF(rv->astr, lemma_present, rv->alen)))
-                                               strcat(result, &(rv->word));
-            if (!complexprefixes && rv->description) strcat(result, rv->description);
-            if (se->getMorph()) strcat(result, se->getMorph());
-            strcat(result, "\n");
+            // store the pointer of the hash entry
+//            sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
+            
+            if (!complexprefixes && HENTRY_DATA(rv)) {
+                    mystrcat(result, " ", MAXLNLEN);                                
+                    mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+            }
+            if (se->getMorph()) {
+                mystrcat(result, " ", MAXLNLEN);                                
+                mystrcat(result, se->getMorph(), MAXLNLEN);
+            } else debugflag(result, se->getFlag());
+            mystrcat(result, "\n", MAXLNLEN);
             rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
          }
        }
        se = se->getNext();
     }
   
     // now handle the general case
     unsigned char sp = *((const unsigned char *)(word + len - 1));
@@ -2724,60 +2729,62 @@ char * AffixMgr::suffix_check_morph(cons
                (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) ||
               // circumfix flag in prefix AND suffix
               ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(),
                    circumfix, ep->getContLen())) &&
                (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))))  &&
             // fogemorpheme
               (in_compound || 
                  !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) &&
-            // pseudoroot on first suffix
+            // needaffix on first suffix
               (cclass || !(sptr->getCont() && 
-                   TESTAFF(sptr->getCont(), pseudoroot, sptr->getContLen())))
+                   TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())))
             )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag);
             while (rv) {
                     if (ppfx) {
-                        if (((PfxEntry *) ppfx)->getMorph()) strcat(result, ((PfxEntry *) ppfx)->getMorph());
+                        if (((PfxEntry *) ppfx)->getMorph()) {
+                            mystrcat(result, ((PfxEntry *) ppfx)->getMorph(), MAXLNLEN);
+                            mystrcat(result, " ", MAXLNLEN);
+                        } else debugflag(result, ((PfxEntry *) ppfx)->getFlag());
                     }    
-                    if (complexprefixes && rv->description) strcat(result, rv->description);
-                    if (rv->description && ((!rv->astr) || 
-                        !TESTAFF(rv->astr, lemma_present, rv->alen))) strcat(result, &(rv->word));
-                    if (!complexprefixes && rv->description) strcat(result, rv->description);
-#ifdef DEBUG
-                unsigned short flag = sptr->getFlag();
-                if (flag_mode == FLAG_NUM) {
-                    sprintf(result, "<%d>", sptr->getKey());
-                } else if (flag_mode == FLAG_LONG) {
-                    sprintf(result, "<%c%c>", flag >> 8, (flag << 8) >>8);
-                } else sprintf(result, "<%c>", flag);
-                strcat(result, ":");
-#endif
-
-                if (sptr->getMorph()) strcat(result, sptr->getMorph());
-                strcat(result, "\n");
+                    if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+                    if (! HENTRY_FIND(rv, MORPH_STEM)) {
+                            mystrcat(result, " ", MAXLNLEN);                                
+                            mystrcat(result, MORPH_STEM, MAXLNLEN);
+                            mystrcat(result, HENTRY_WORD(rv), MAXLNLEN);
+                    }
+                    // store the pointer of the hash entry
+//                    sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv);
+
+                    if (!complexprefixes && HENTRY_DATA(rv)) {
+                        mystrcat(result, " ", MAXLNLEN);                                
+                        mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN);
+                    }
+
+                if (sptr->getMorph()) {
+                    mystrcat(result, " ", MAXLNLEN);
+                    mystrcat(result, sptr->getMorph(), MAXLNLEN);
+                } else debugflag(result, sptr->getFlag());
+                mystrcat(result, "\n", MAXLNLEN);
                 rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
             }
              sptr = sptr->getNextEQ();
         } else {
              sptr = sptr->getNextNE();
         }
     }
 
     if (*result) return mystrdup(result);
     return NULL;
 }
-#endif // END OF HUNSPELL_EXPERIMENTAL CODE
-
 
 // check if word with affixes is correctly spelled
 struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound)
 {
     struct hentry * rv= NULL;
-    if (derived) free(derived);
-    derived =  NULL;
 
     // check all prefixes (also crossed with suffixes if allowed) 
     rv = prefix_check(word, len, in_compound, needflag);
     if (rv) return rv;
 
     // if still not found check all suffixes
     rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound);
 
@@ -2789,115 +2796,194 @@ struct hentry * AffixMgr::affix_check (c
         rv = suffix_check_twosfx(word, len, 0, NULL, needflag);
         if (rv) return rv;
         // if still not found check all two-level suffixes
         rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag);
     }
     return rv;
 }
 
-#ifdef HUNSPELL_EXPERIMENTAL
 // check if word with affixes is correctly spelled
 char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound)
 {
     char result[MAXLNLEN];
     char * st = NULL;
 
     *result = '\0';
     
     // check all prefixes (also crossed with suffixes if allowed) 
     st = prefix_check_morph(word, len, in_compound);
     if (st) {
-        strcat(result, st);
+        mystrcat(result, st, MAXLNLEN);
         free(st);
     }
 
     // if still not found check all suffixes    
     st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound);
     if (st) {
-        strcat(result, st);
+        mystrcat(result, st, MAXLNLEN);
         free(st);
     }
 
     if (havecontclass) {
         sfx = NULL;
         pfx = NULL;
         // if still not found check all two-level suffixes
         st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag);
         if (st) {
-            strcat(result, st);
+            mystrcat(result, st, MAXLNLEN);
             free(st);
         }
 
         // if still not found check all two-level suffixes
         st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag);
         if (st) {
-            strcat(result, st);
+            mystrcat(result, st, MAXLNLEN);
             free(st);
         }
     }
-    
+
     return mystrdup(result);
 }
-#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap,
+    unsigned short al, char * morph, char * targetmorph, int level)
+{
+    // handle suffixes
+    char * stemmorph;
+    char * stemmorphcatpos;
+    char mymorph[MAXLNLEN];
+
+    if (!morph && !targetmorph) return NULL;
+
+    // check substandard flag
+    if (TESTAFF(ap, substandard, al)) return NULL;
+
+    if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts);
+
+//    int targetcount = get_sfxcount(targetmorph);
+
+    // use input suffix fields, if exist
+    if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) {
+        stemmorph = mymorph;
+        strcpy(stemmorph, morph);
+        strcat(stemmorph, " ");
+        stemmorphcatpos = stemmorph + strlen(stemmorph);
+    } else {
+        stemmorph = morph;
+        stemmorphcatpos = NULL;
+    }
+
+    for (int i = 0; i < al; i++) {
+        const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
+        SfxEntry * sptr = (SfxEntry *)sFlag[c];
+        while (sptr) {
+            if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) || 
+                // don't generate forms with substandard affixes
+                !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) {
+
+                if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph());
+                else stemmorph = (char *) sptr->getMorph();
+
+                int cmp = morphcmp(stemmorph, targetmorph);
+
+                if (cmp == 0) {
+                    char * newword = sptr->add(ts, wl);
+                    if (newword) {
+                        hentry * check = pHMgr->lookup(newword); // XXX extra dic
+                        if (!check || !check->astr || 
+                            !TESTAFF(check->astr, forbiddenword, check->alen)) {
+                                return newword;
+                        }
+                        free(newword);
+                    }
+                }
+                
+                // recursive call for secondary suffixes
+                if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) &&
+//                    (get_sfxcount(stemmorph) < targetcount) &&
+                    !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) {
+                    char * newword = sptr->add(ts, wl);
+                    if (newword) {
+                        char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(),
+                            sptr->getContLen(), stemmorph, targetmorph, 1);
+
+                        if (newword2) {
+                            free(newword);
+                            return newword2;
+                        }
+                        free(newword);
+                        newword = NULL;
+                    }
+                }
+            }
+            sptr = (SfxEntry *)sptr ->getFlgNxt();
+        }
+    }
+   return NULL;
+}
 
 
 int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts,
     int wl, const unsigned short * ap, unsigned short al, char * bad, int badl,
-    char * phone)
+    char * phon)
 {
-
     int nh=0;
     // first add root word to list
-    if ((nh < maxn) && !(al && ((pseudoroot && TESTAFF(ap, pseudoroot, al)) ||
+    if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) ||
          (onlyincompound && TESTAFF(ap, onlyincompound, al))))) {
        wlst[nh].word = mystrdup(ts);
+       if (!wlst[nh].word) return 0;
        wlst[nh].allow = (1 == 0);
        wlst[nh].orig = NULL;
        nh++;
        // add special phonetic version
-       if (phone && (nh < maxn)) {
-    	    wlst[nh].word = mystrdup(phone);
+       if (phon && (nh < maxn)) {
+    	    wlst[nh].word = mystrdup(phon);
+            if (!wlst[nh].word) return nh - 1;
     	    wlst[nh].allow = (1 == 0);
     	    wlst[nh].orig = mystrdup(ts);
+            if (!wlst[nh].orig) return nh - 1;
     	    nh++;
        }
     }
 
     // handle suffixes
     for (int i = 0; i < al; i++) {
        const unsigned char c = (unsigned char) (ap[i] & 0x00FF);
        SfxEntry * sptr = (SfxEntry *)sFlag[c];
        while (sptr) {
          if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) &&
                 (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) &&
-                // check pseudoroot flag
-                !(sptr->getCont() && ((pseudoroot && 
-                      TESTAFF(sptr->getCont(), pseudoroot, sptr->getContLen())) ||
+                // check needaffix flag
+                !(sptr->getCont() && ((needaffix && 
+                      TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
                   (circumfix && 
                       TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) ||
                   (onlyincompound && 
                       TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))
                 ) {
             char * newword = sptr->add(ts, wl);
             if (newword) {
                 if (nh < maxn) {
                     wlst[nh].word = newword;
                     wlst[nh].allow = sptr->allowCross();
                     wlst[nh].orig = NULL;
                     nh++;
                     // add special phonetic version
-    		    if (phone && (nh < maxn)) {
+    		    if (phon && (nh < maxn)) {
     			char st[MAXWORDUTF8LEN];
-    			strcpy(st, phone);
+    			strcpy(st, phon);
     			strcat(st, sptr->getKey());
-    			reverseword(st + strlen(phone));
+    			reverseword(st + strlen(phon));
     			wlst[nh].word = mystrdup(st);
+    			if (!wlst[nh].word) return nh - 1;
     			wlst[nh].allow = (1 == 0);
     			wlst[nh].orig = mystrdup(newword);
+                        if (!wlst[nh].orig) return nh - 1;
     			nh++;
     		    }
                 } else {
                     free(newword);
                 }
             }
          }
          sptr = (SfxEntry *)sptr ->getFlgNxt();
@@ -2905,28 +2991,28 @@ int AffixMgr::expand_rootword(struct gue
     }
 
     int n = nh;
 
     // handle cross products of prefixes and suffixes
     for (int j=1;j<n ;j++)
        if (wlst[j].allow) {
           for (int k = 0; k < al; k++) {
-    	     const unsigned char c = (unsigned char) (ap[k] & 0x00FF);
+             const unsigned char c = (unsigned char) (ap[k] & 0x00FF);
              PfxEntry * cptr = (PfxEntry *) pFlag[c];
              while (cptr) {
                 if ((cptr->getFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) &&
                         (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) {
                     int l1 = strlen(wlst[j].word);
                     char * newword = cptr->add(wlst[j].word, l1);
                     if (newword) {
                        if (nh < maxn) {
                           wlst[nh].word = newword;
                           wlst[nh].allow = cptr->allowCross();
-                	  wlst[nh].orig = NULL;
+                          wlst[nh].orig = NULL;
                           nh++;
                        } else {
                           free(newword);
                        }
                     }
                 }
                 cptr = (PfxEntry *)cptr ->getFlgNxt();
              }
@@ -2936,19 +3022,19 @@ int AffixMgr::expand_rootword(struct gue
 
     // now handle pure prefixes
     for (int m = 0; m < al; m ++) {
        const unsigned char c = (unsigned char) (ap[m] & 0x00FF);
        PfxEntry * ptr = (PfxEntry *) pFlag[c];
        while (ptr) {
          if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) &&
                 (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) &&
-                // check pseudoroot flag
-                !(ptr->getCont() && ((pseudoroot && 
-                      TESTAFF(ptr->getCont(), pseudoroot, ptr->getContLen())) ||
+                // check needaffix flag
+                !(ptr->getCont() && ((needaffix && 
+                      TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) ||
                      (circumfix && 
                       TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) ||                      
                   (onlyincompound && 
                       TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen()))))
                 ) {
             char * newword = ptr->add(ts, wl);
             if (newword) {
                 if (nh < maxn) {
@@ -2963,31 +3049,43 @@ int AffixMgr::expand_rootword(struct gue
          }
          ptr = (PfxEntry *)ptr ->getFlgNxt();
        }
     }
 
     return nh;
 }
 
-
-
 // return length of replacing table
 int AffixMgr::get_numrep()
 {
   return numrep;
 }
 
 // return replacing table
 struct replentry * AffixMgr::get_reptable()
 {
   if (! reptable ) return NULL;
   return reptable;
 }
 
+// return iconv table
+RepList * AffixMgr::get_iconvtable()
+{
+  if (! iconvtable ) return NULL;
+  return iconvtable;
+}
+
+// return oconv table
+RepList * AffixMgr::get_oconvtable()
+{
+  if (! oconvtable ) return NULL;
+  return oconvtable;
+}
+
 // return replacing table
 struct phonetable * AffixMgr::get_phonetable()
 {
   if (! phone ) return NULL;
   return phone;
 }
 
 // return length of character map table
@@ -3014,44 +3112,54 @@ char ** AffixMgr::get_breaktable()
 {
   if (! breaktable ) return NULL;
   return breaktable;
 }
 
 // return text encoding of dictionary
 char * AffixMgr::get_encoding()
 {
-  if (! encoding ) {
-      encoding = mystrdup("ISO8859-1");
-  }
+  if (! encoding ) encoding = mystrdup(SPELL_ENCODING);
   return mystrdup(encoding);
 }
 
 // return text encoding of dictionary
 int AffixMgr::get_langnum()
 {
   return langnum;
 }
 
 // return double prefix option
 int AffixMgr::get_complexprefixes()
 {
   return complexprefixes;
 }
 
+// return FULLSTRIP option
+int AffixMgr::get_fullstrip()
+{
+  return fullstrip;
+}
+
 FLAG AffixMgr::get_keepcase()
 {
   return keepcase;
 }
 
 int AffixMgr::get_checksharps()
 {
   return checksharps;
 }
 
+char * AffixMgr::encode_flag(unsigned short aflag)
+{
+  return pHMgr->encode_flag(aflag);
+}
+
+
 // return the preferred ignore string for suggestions
 char * AffixMgr::get_ignore()
 {
   if (!ignorechars) return NULL;
   return ignorechars;
 }
 
 // return the preferred ignore string for suggestions
@@ -3059,17 +3167,17 @@ unsigned short * AffixMgr::get_ignore_ut
 {
   *len = ignorechars_utf16_len;
   return ignorechars_utf16;
 }
 
 // return the keyboard string for suggestions
 char * AffixMgr::get_key_string()
 {
-  if (! keystring ) return NULL;
+  if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING);
   return mystrdup(keystring);
 }
 
 // return the preferred try string for suggestions
 char * AffixMgr::get_try_string()
 {
   if (! trystring ) return NULL;
   return mystrdup(trystring);
@@ -3107,19 +3215,19 @@ FLAG AffixMgr::get_forbiddenword()
 
 // return the forbidden words control flag
 FLAG AffixMgr::get_nosuggest()
 {
   return nosuggest;
 }
 
 // return the forbidden words flag modify flag
-FLAG AffixMgr::get_pseudoroot()
+FLAG AffixMgr::get_needaffix()
 {
-  return pseudoroot;
+  return needaffix;
 }
 
 // return the onlyincompound flag
 FLAG AffixMgr::get_onlyincompound()
 {
   return onlyincompound;
 }
 
@@ -3149,39 +3257,37 @@ const char * AffixMgr::get_prefix()
 }
 
 // return the value of suffix
 const char * AffixMgr::get_suffix()
 {
   return sfxappnd;
 }
 
-// return the value of derived form (base word with first suffix).
-const char * AffixMgr::get_derived()
-{
-  return derived;
-}
-
 // return the value of suffix
 const char * AffixMgr::get_version()
 {
   return version;
 }
 
 // return lemma_present flag
 FLAG AffixMgr::get_lemma_present()
 {
   return lemma_present;
 }
 
 // utility method to look up root words in hash table
 struct hentry * AffixMgr::lookup(const char * word)
 {
-  if (! pHMgr) return NULL;
-  return pHMgr->lookup(word);
+  int i;
+  struct hentry * he = NULL;
+  for (i = 0; i < *maxdic && !he; i++) {
+    he = (alldic[i])->lookup(word);
+  }
+  return he;
 }
 
 // return the value of suffix
 const int AffixMgr::have_contclass()
 {
   return havecontclass;
 }
 
@@ -3205,43 +3311,43 @@ int AffixMgr::get_nosplitsugs(void)
 
 // return sugswithdots
 int AffixMgr::get_sugswithdots(void)
 {
   return sugswithdots;
 }
 
 /* parse flag */
-int AffixMgr::parse_flag(char * line, unsigned short * out, const char * name) {
+int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) {
    char * s = NULL;
-   if (*out != FLAG_NULL) {
-      HUNSPELL_WARNING(stderr, "error: duplicate %s line\n", name);
+   if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) {
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
       return 1;
    }
-   if (parse_string(line, &s, name)) return 1;
+   if (parse_string(line, &s, af->getlinenum())) return 1;
    *out = pHMgr->decode_flag(s);
    free(s);
    return 0;
 }
 
 /* parse num */
-int AffixMgr::parse_num(char * line, int * out, const char * name) {
+int AffixMgr::parse_num(char * line, int * out, FileMgr * af) {
    char * s = NULL;
    if (*out != -1) {
-      HUNSPELL_WARNING(stderr, "error: duplicate %s line\n", name);
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum());
       return 1;
    }
-   if (parse_string(line, &s, name)) return 1;
+   if (parse_string(line, &s, af->getlinenum())) return 1;
    *out = atoi(s);
    free(s);
    return 0;
 }
 
 /* parse in the max syllablecount of compound words and  */
-int  AffixMgr::parse_cpdsyllable(char * line)
+int  AffixMgr::parse_cpdsyllable(char * line, FileMgr * af)
 {
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    w_char w[MAXWORDLEN];
    piece = mystrsep(&tp, 0);
    while (piece) {
@@ -3264,415 +3370,516 @@ int  AffixMgr::parse_cpdsyllable(char * 
                 }
                 np++;
                 break;
              }
              default: break;
           }
           i++;
       }
-      free(piece);
       piece = mystrsep(&tp, 0);
    }
    if (np < 2) {
-      HUNSPELL_WARNING(stderr, "error: missing compoundsyllable information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum());
       return 1;
    }
    if (np == 2) cpdvowels = mystrdup("aeiouAEIOU");
    return 0;
 }
 
 /* parse in the typical fault correcting table */
-int  AffixMgr::parse_reptable(char * line, FILE * af)
+int  AffixMgr::parse_reptable(char * line, FileMgr * af)
 {
    if (numrep != 0) {
-      HUNSPELL_WARNING(stderr, "error: duplicate REP tables used\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
       return 1;
    }
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    piece = mystrsep(&tp, 0);
    while (piece) {
        if (*piece != '\0') {
           switch(i) {
              case 0: { np++; break; }
              case 1: { 
                        numrep = atoi(piece);
                        if (numrep < 1) {
-                          HUNSPELL_WARNING(stderr, "incorrect number of entries in replacement table\n");
-                          free(piece);
+                          HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
                           return 1;
                        }
                        reptable = (replentry *) malloc(numrep * sizeof(struct replentry));
                        if (!reptable) return 1;
                        np++;
                        break;
                      }
              default: break;
           }
           i++;
        }
-       free(piece);
        piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
-      HUNSPELL_WARNING(stderr, "error: missing replacement table information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
       return 1;
    } 
  
    /* now parse the numrep lines to read in the remainder of the table */
-   char * nl = line;
+   char * nl;
    for (int j=0; j < numrep; j++) {
-        if (!fgets(nl,MAXLNLEN,af)) return 1;
+        if (!(nl = af->getline())) return 1;
         mychomp(nl);
         tp = nl;
         i = 0;
         reptable[j].pattern = NULL;
         reptable[j].pattern2 = NULL;
         piece = mystrsep(&tp, 0);
         while (piece) {
            if (*piece != '\0') {
                switch(i) {
                   case 0: {
                              if (strncmp(piece,"REP",3) != 0) {
-                                 HUNSPELL_WARNING(stderr, "error: replacement table is corrupt\n");
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
                                  numrep = 0;
-                                 free(piece);
                                  return 1;
                              }
                              break;
                           }
                   case 1: { reptable[j].pattern = mystrrep(mystrdup(piece),"_"," "); break; }
                   case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; }
                   default: break;
                }
                i++;
            }
-           free(piece);
            piece = mystrsep(&tp, 0);
         }
         if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) {
-             HUNSPELL_WARNING(stderr, "error: replacement table is corrupt\n");
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
              numrep = 0;
              return 1;
         }
    }
    return 0;
 }
 
 /* parse in the typical fault correcting table */
-int  AffixMgr::parse_phonetable(char * line, FILE * af)
+int  AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword)
+{
+   if (*rl) {
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
+      return 1;
+   }
+   char * tp = line;
+   char * piece;
+   int i = 0;
+   int np = 0;
+   int numrl = 0;
+   piece = mystrsep(&tp, 0);
+   while (piece) {
+       if (*piece != '\0') {
+          switch(i) {
+             case 0: { np++; break; }
+             case 1: { 
+                       numrl = atoi(piece);
+                       if (numrl < 1) {
+                          HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum());
+                          return 1;
+                       }
+                       *rl = new RepList(numrl);
+                       if (!rl) return 1;
+                       np++;
+                       break;
+                     }
+             default: break;
+          }
+          i++;
+       }
+       piece = mystrsep(&tp, 0);
+   }
+   if (np != 2) {
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+      return 1;
+   } 
+ 
+   /* now parse the num lines to read in the remainder of the table */
+   char * nl;
+   for (int j=0; j < numrl; j++) {
+        if (!(nl = af->getline())) return 1;
+        mychomp(nl);
+        tp = nl;
+        i = 0;
+        char * pattern = NULL;
+        char * pattern2 = NULL;
+        piece = mystrsep(&tp, 0);
+        while (piece) {
+           if (*piece != '\0') {
+               switch(i) {
+                  case 0: {
+                             if (strncmp(piece, keyword, sizeof(keyword)) != 0) {
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+                                 delete *rl;
+                                 *rl = NULL;
+                                 return 1;
+                             }
+                             break;
+                          }
+                  case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; }
+                  case 2: { 
+                    pattern2 = mystrrep(mystrdup(piece),"_"," ");
+                    break; 
+                  }
+                  default: break;
+               }
+               i++;
+           }
+           piece = mystrsep(&tp, 0);
+        }
+        if (!pattern || !pattern2) {
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+             return 1;
+        }
+        (*rl)->add(pattern, pattern2);
+   }
+   return 0;
+}
+
+
+/* parse in the typical fault correcting table */
+int  AffixMgr::parse_phonetable(char * line, FileMgr * af)
 {
    if (phone) {
-      HUNSPELL_WARNING(stderr, "error: duplicate PHONE tables used\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
       return 1;
    }
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    piece = mystrsep(&tp, 0);
    while (piece) {
        if (*piece != '\0') {
           switch(i) {
              case 0: { np++; break; }
              case 1: { 
-            	       phone = (phonetable *) malloc(sizeof(struct phonetable));
-            	       phone->num = atoi(piece);
-            	       phone->rules = NULL;
-            	       phone->utf8 = utf8;
+                       phone = (phonetable *) malloc(sizeof(struct phonetable));
+                       phone->num = atoi(piece);
+                       phone->rules = NULL;
+                       phone->utf8 = (char) utf8;
                        if (!phone) return 1;
                        if (phone->num < 1) {
-                          HUNSPELL_WARNING(stderr, "incorrect number of entries in phonelacement table\n");
-                          free(piece);
+                          HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
                           return 1;
                        }
                        phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *));
                        if (!phone->rules) return 1;
                        np++;
                        break;
                      }
              default: break;
           }
           i++;
        }
-       free(piece);
        piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
-      HUNSPELL_WARNING(stderr, "error: missing PHONE table information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
       return 1;
    } 
  
    /* now parse the phone->num lines to read in the remainder of the table */
-   char * nl = line;
+   char * nl;
    for (int j=0; j < phone->num; j++) {
-        if (!fgets(nl,MAXLNLEN,af)) return 1;
+        if (!(nl = af->getline())) return 1;
         mychomp(nl);
         tp = nl;
         i = 0;
         phone->rules[j * 2] = NULL;
         phone->rules[j * 2 + 1] = NULL;
         piece = mystrsep(&tp, 0);
         while (piece) {
            if (*piece != '\0') {
                switch(i) {
                   case 0: {
                              if (strncmp(piece,"PHONE",5) != 0) {
-                                 HUNSPELL_WARNING(stderr, "error: PHONE table is corrupt\n");
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
                                  phone->num = 0;
-                                 free(piece);
                                  return 1;
                              }
                              break;
                           }
                   case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; }
                   case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; }
                   default: break;
                }
                i++;
            }
-           free(piece);
            piece = mystrsep(&tp, 0);
         }
         if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) {
-             HUNSPELL_WARNING(stderr, "error: PHONE table is corrupt\n");
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
              phone->num = 0;
              return 1;
         }
    }
    phone->rules[phone->num * 2] = mystrdup("");
    phone->rules[phone->num * 2 + 1] = mystrdup("");
    init_phonet_hash(*phone);
    return 0;
 }
 
 /* parse in the checkcompoundpattern table */
-int  AffixMgr::parse_checkcpdtable(char * line, FILE * af)
+int  AffixMgr::parse_checkcpdtable(char * line, FileMgr * af)
 {
    if (numcheckcpd != 0) {
-      HUNSPELL_WARNING(stderr, "error: duplicate compound pattern tables used\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
       return 1;
    }
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    piece = mystrsep(&tp, 0);
    while (piece) {
        if (*piece != '\0') {
           switch(i) {
              case 0: { np++; break; }
              case 1: { 
                        numcheckcpd = atoi(piece);
                        if (numcheckcpd < 1) {
-                          HUNSPELL_WARNING(stderr, "incorrect number of entries in compound pattern table\n");
-                          free(piece);
+                          HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
                           return 1;
                        }
-                       checkcpdtable = (replentry *) malloc(numcheckcpd * sizeof(struct replentry));
+                       checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry));
                        if (!checkcpdtable) return 1;
                        np++;
                        break;
                      }
              default: break;
           }
           i++;
        }
-       free(piece);
        piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
-      HUNSPELL_WARNING(stderr, "error: missing compound pattern table information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",  af->getlinenum());
       return 1;
-   } 
- 
+   }
+
    /* now parse the numcheckcpd lines to read in the remainder of the table */
-   char * nl = line;
+   char * nl;
    for (int j=0; j < numcheckcpd; j++) {
-        if (!fgets(nl,MAXLNLEN,af)) return 1;
+        if (!(nl = af->getline())) return 1;
         mychomp(nl);
         tp = nl;
         i = 0;
         checkcpdtable[j].pattern = NULL;
         checkcpdtable[j].pattern2 = NULL;
+        checkcpdtable[j].pattern3 = NULL;
+        checkcpdtable[j].cond = FLAG_NULL;
+        checkcpdtable[j].cond2 = FLAG_NULL;
         piece = mystrsep(&tp, 0);
         while (piece) {
            if (*piece != '\0') {
                switch(i) {
                   case 0: {
                              if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) {
-                                 HUNSPELL_WARNING(stderr, "error: compound pattern table is corrupt\n");
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
                                  numcheckcpd = 0;
-                                 free(piece);
                                  return 1;
                              }
                              break;
                           }
-                  case 1: { checkcpdtable[j].pattern = mystrdup(piece); break; }
-                  case 2: { checkcpdtable[j].pattern2 = mystrdup(piece); break; }
+                  case 1: { 
+                    checkcpdtable[j].pattern = mystrdup(piece); 
+                    char * p = strchr(checkcpdtable[j].pattern, '/');
+                    if (p) {
+                      *p = '\0';
+                    checkcpdtable[j].cond = pHMgr->decode_flag(p + 1);
+                    }
+                    break; }
+                  case 2: { 
+                    checkcpdtable[j].pattern2 = mystrdup(piece);
+                    char * p = strchr(checkcpdtable[j].pattern2, '/');
+                    if (p) {
+                      *p = '\0';
+                      checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1);
+                    }
+                    break;
+                    }
+                  case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; }
                   default: break;
                }
                i++;
            }
-           free(piece);
            piece = mystrsep(&tp, 0);
         }
         if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) {
-             HUNSPELL_WARNING(stderr, "error: compound pattern table is corrupt\n");
-	     numcheckcpd = 0;
-	     return 1;
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
+             numcheckcpd = 0;
+             return 1;
         }
    }
    return 0;
 }
 
 /* parse in the compound rule table */
-int  AffixMgr::parse_defcpdtable(char * line, FILE * af)
+int  AffixMgr::parse_defcpdtable(char * line, FileMgr * af)
 {
    if (numdefcpd != 0) {
-      HUNSPELL_WARNING(stderr, "error: duplicate compound rule tables used\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
       return 1;
    }
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    piece = mystrsep(&tp, 0);
    while (piece) {
        if (*piece != '\0') {
           switch(i) {
              case 0: { np++; break; }
              case 1: { 
                        numdefcpd = atoi(piece);
                        if (numdefcpd < 1) {
-                          HUNSPELL_WARNING(stderr, "incorrect number of entries in compound rule table\n");
-                          free(piece);
+                          HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
                           return 1;
                        }
                        defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry));
                        if (!defcpdtable) return 1;
                        np++;
                        break;
                      }
              default: break;
           }
           i++;
        }
-       free(piece);
        piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
-      HUNSPELL_WARNING(stderr, "error: missing compound rule table information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
       return 1;
    } 
  
    /* now parse the numdefcpd lines to read in the remainder of the table */
-   char * nl = line;
+   char * nl;
    for (int j=0; j < numdefcpd; j++) {
-        if (!fgets(nl,MAXLNLEN,af)) return 1;
+        if (!(nl = af->getline())) return 1;
         mychomp(nl);
         tp = nl;
         i = 0;
         defcpdtable[j].def = NULL;
         piece = mystrsep(&tp, 0);
         while (piece) {
            if (*piece != '\0') {
                switch(i) {
                   case 0: {
                              if (strncmp(piece, "COMPOUNDRULE", 12) != 0) {
-                                 HUNSPELL_WARNING(stderr, "error: compound rule table is corrupt\n");
-                                 free(piece);
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
                                  numdefcpd = 0;
                                  return 1;
                              }
                              break;
                           }
-                  case 1: { 
-                            defcpdtable[j].len = 
-                                pHMgr->decode_flags(&(defcpdtable[j].def), piece);
+                  case 1: { // handle parenthesized flags
+                            if (strchr(piece, '(')) {
+                                defcpdtable[j].def = (FLAG *) malloc(sizeof(piece) * sizeof(FLAG));
+                                defcpdtable[j].len = 0;
+                                int end = 0;
+                                FLAG * conv;
+                                while (!end) {
+                                    char * par = piece + 1;
+                                    while (*par != '(' && *par != ')' && *par != '\0') par++;
+                                    if (*par == '\0') end = 1; else *par = '\0';
+                                    if (*piece == '(') piece++;
+                                    if (*piece == '*' || *piece == '?') {
+                                        defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece;
+                                    } else if (*piece != '\0') {
+                                        int l = pHMgr->decode_flags(&conv, piece, af);
+                                        for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k];
+                                        free(conv);
+                                    }
+                                    piece = par + 1;
+                                }
+                            } else {
+                                defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af);
+                            }
                             break; 
                            }
                   default: break;
                }
                i++;
            }
-           free(piece);
            piece = mystrsep(&tp, 0);
         }
         if (!defcpdtable[j].len) {
-             HUNSPELL_WARNING(stderr, "error: compound rule table is corrupt\n");
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
              numdefcpd = 0;
              return 1;
         }
    }
    return 0;
 }
 
 
 /* parse in the character map table */
-int  AffixMgr::parse_maptable(char * line, FILE * af)
+int  AffixMgr::parse_maptable(char * line, FileMgr * af)
 {
    if (nummap != 0) {
-      HUNSPELL_WARNING(stderr, "error: duplicate MAP tables used\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
       return 1;
    }
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    piece = mystrsep(&tp, 0);
    while (piece) {
        if (*piece != '\0') {
           switch(i) {
              case 0: { np++; break; }
              case 1: { 
                        nummap = atoi(piece);
                        if (nummap < 1) {
-                          HUNSPELL_WARNING(stderr, "incorrect number of entries in map table\n");
-                          free(piece);
+                          HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
                           return 1;
                        }
                        maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry));
                        if (!maptable) return 1;
                        np++;
                        break;
                      }
              default: break;
           }
           i++;
        }
-       free(piece);
        piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
-      HUNSPELL_WARNING(stderr, "error: missing map table information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
       return 1;
    } 
  
    /* now parse the nummap lines to read in the remainder of the table */
-   char * nl = line;
+   char * nl;
    for (int j=0; j < nummap; j++) {
-        if (!fgets(nl,MAXLNLEN,af)) return 1;
+        if (!(nl = af->getline())) return 1;
         mychomp(nl);
         tp = nl;
         i = 0;
         maptable[j].set = NULL;
         maptable[j].len = 0;
         piece = mystrsep(&tp, 0);
         while (piece) {
            if (*piece != '\0') {
                switch(i) {
                   case 0: {
                              if (strncmp(piece,"MAP",3) != 0) {
-                                 HUNSPELL_WARNING(stderr, "error: map table is corrupt\n");
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
                                  nummap = 0;
-                                 free(piece);
                                  return 1;
                              }
                              break;
                           }
                   case 1: {
                             maptable[j].len = 0;
                             maptable[j].set = NULL;
                             maptable[j].set_utf16 = NULL;
@@ -3690,109 +3897,129 @@ int  AffixMgr::parse_maptable(char * lin
                                 }
                                 maptable[j].len = n;
                             }
                             break; }
                   default: break;
                }
                i++;
            }
-           free(piece);
            piece = mystrsep(&tp, 0);
         }
         if ((!(maptable[j].set || maptable[j].set_utf16)) || (!(maptable[j].len))) {
-             HUNSPELL_WARNING(stderr, "error: map table is corrupt\n");
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
              nummap = 0;
              return 1;
         }
    }
    return 0;
 }
 
 /* parse in the word breakpoint table */
-int  AffixMgr::parse_breaktable(char * line, FILE * af)
+int  AffixMgr::parse_breaktable(char * line, FileMgr * af)
 {
    if (numbreak != 0) {
-      HUNSPELL_WARNING(stderr, "error: duplicate word breakpoint tables used\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
       return 1;
    }
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    piece = mystrsep(&tp, 0);
    while (piece) {
        if (*piece != '\0') {
           switch(i) {
              case 0: { np++; break; }
              case 1: { 
                        numbreak = atoi(piece);
                        if (numbreak < 1) {
-                          HUNSPELL_WARNING(stderr, "incorrect number of entries in BREAK table\n");
-                          free(piece);
+                          HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
                           return 1;
                        }
                        breaktable = (char **) malloc(numbreak * sizeof(char *));
                        if (!breaktable) return 1;
                        np++;
                        break;
                      }
              default: break;
           }
           i++;
        }
-       free(piece);
        piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
-      HUNSPELL_WARNING(stderr, "error: missing word breakpoint table information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
       return 1;
    } 
  
    /* now parse the numbreak lines to read in the remainder of the table */
-   char * nl = line;
+   char * nl;
    for (int j=0; j < numbreak; j++) {
-        if (!fgets(nl,MAXLNLEN,af)) return 1;
+        if (!(nl = af->getline())) return 1;
         mychomp(nl);
         tp = nl;
         i = 0;
         piece = mystrsep(&tp, 0);
         while (piece) {
            if (*piece != '\0') {
                switch(i) {
                   case 0: {
                              if (strncmp(piece,"BREAK",5) != 0) {
-                                 HUNSPELL_WARNING(stderr, "error: BREAK table is corrupt\n");
-                                 free(piece);
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
                                  numbreak = 0;
                                  return 1;
                              }
                              break;
                           }
                   case 1: {
                             breaktable[j] = mystrdup(piece);
                             break;
                           }
                   default: break;
                }
                i++;
            }
-           free(piece);
            piece = mystrsep(&tp, 0);
         }
         if (!breaktable) {
-             HUNSPELL_WARNING(stderr, "error: BREAK table is corrupt\n");
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
              numbreak = 0;
              return 1;
         }
    }
    return 0;
 }
 
-int  AffixMgr::parse_affix(char * line, const char at, FILE * af, char * dupflags)
+void AffixMgr::reverse_condition(char * piece) {
+    int neg = 0;
+    for (char * k = piece + strlen(piece) - 1; k >= piece; k--) {
+        switch(*k) {
+          case '[': {
+                if (neg) *(k+1) = '['; else *k = ']';
+                    break;
+            }
+          case ']': {
+                *k = '[';
+                if (neg) *(k+1) = '^';
+                neg = 0;
+                break;
+            }
+          case '^': {
+               if (*(k+1) == ']') neg = 1; else *(k+1) = *k;
+               break;
+                }
+          default: {
+            if (neg) *(k+1) = *k;
+          }
+       }
+    }
+}
+
+int  AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags)
 {
    int numents = 0;      // number of affentry structures to parse
 
    unsigned short aflag = 0;      // affix char identifier
 
    char ff=0;
    struct affentry * ptr= NULL;
    struct affentry * nptr= NULL;
@@ -3805,108 +4032,113 @@ int  AffixMgr::parse_affix(char * line, 
    // checking lines with bad syntax
 #ifdef DEBUG
    int basefieldnum = 0;
 #endif
 
    // split affix header line into pieces
 
    int np = 0;
+
    piece = mystrsep(&tp, 0);
    while (piece) {
       if (*piece != '\0') {
           switch(i) {
              // piece 1 - is type of affix
              case 0: { np++; break; }
           
              // piece 2 - is affix char
              case 1: { 
                     np++;
                     aflag = pHMgr->decode_flag(piece);
                     if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
                         ((at == 'P') && (dupflags[aflag] & dupPFX))) {
-                        HUNSPELL_WARNING(stderr, "error: duplicate affix flag %s in line %s\n", piece, nl);
+                        HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n",
+                            af->getlinenum());
                         // return 1; XXX permissive mode for bad dictionaries
                     }
-                    dupflags[aflag] += ((at == 'S') ? dupSFX : dupPFX);
+                    dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX);
                     break; 
                     }
              // piece 3 - is cross product indicator 
              case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; }
 
              // piece 4 - is number of affentries
              case 3: { 
                        np++;
                        numents = atoi(piece); 
                        if (numents == 0) {
                            char * err = pHMgr->encode_flag(aflag);
-                           HUNSPELL_WARNING(stderr, "error: affix %s header has incorrect entry count in line %s\n",
-                                   err, nl);
-                           free(err);
+                           if (err) {
+                                HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
+                                   af->getlinenum());
+                                free(err);
+                           }
                            return 1;
                        }
                        ptr = (struct affentry *) malloc(numents * sizeof(struct affentry));
                        if (!ptr) return 1;
                        ptr->opts = ff;
                        if (utf8) ptr->opts += aeUTF8;
                        if (pHMgr->is_aliasf()) ptr->opts += aeALIASF;
-#ifdef HUNSPELL_EXPERIMENTAL
                        if (pHMgr->is_aliasm()) ptr->opts += aeALIASM;
-#endif
                        ptr->aflag = aflag;
                      }
 
              default: break;
           }
           i++;
       }
-      free(piece);
       piece = mystrsep(&tp, 0);
    }
    // check to make sure we parsed enough pieces
    if (np != 4) {
-       char * err = pHMgr->encode_flag(aflag); 
-       HUNSPELL_WARNING(stderr, "error: affix %s header has insufficient data in line %s\n", err, nl);
-       free(err);
+       char * err = pHMgr->encode_flag(aflag);
+       if (err) {
+            HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
+            free(err);
+       }
        free(ptr);
        return 1;
    }
  
    // store away ptr to first affentry
    nptr = ptr;
 
    // now parse numents affentries for this affix
    for (int j=0; j < numents; j++) {
-      if (!fgets(nl,MAXLNLEN,af)) return 1;
+      if (!(nl = af->getline())) return 1;
       mychomp(nl);
       tp = nl;
       i = 0;
       np = 0;
 
       // split line into pieces
       piece = mystrsep(&tp, 0);
       while (piece) {
          if (*piece != '\0') {
              switch(i) {
                 // piece 1 - is type
                 case 0: { 
                           np++;
-                          if (nptr != ptr) nptr->opts = ptr->opts;
+                          if (nptr != ptr) nptr->opts = ptr->opts &
+                             (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM);
                           break;
                         }
 
                 // piece 2 - is affix char
                 case 1: { 
                           np++;
                           if (pHMgr->decode_flag(piece) != aflag) {
                               char * err = pHMgr->encode_flag(aflag);
-                              HUNSPELL_WARNING(stderr, "error: affix %s is corrupt near line %s\n", err, nl);
-                              HUNSPELL_WARNING(stderr, "error: possible incorrect count\n");
-                              free(err);
-                              free(piece);
+                              if (err) {
+                                HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
+                                    af->getlinenum(), err);
+                                free(err);
+                              }
                               return 1;
                           }
 
                           if (nptr != ptr) nptr->aflag = ptr->aflag;
                           break;
                         }
 
                 // piece 3 - is string to strip or 0 for null 
@@ -3923,44 +4155,43 @@ int  AffixMgr::parse_affix(char * line, 
                               nptr->stripl = 0;
                           }   
                           break; 
                         }
 
                 // piece 4 - is affix string or 0 for null
                 case 3: { 
                           char * dash;  
-#ifdef HUNSPELL_EXPERIMENTAL
                           nptr->morphcode = NULL;
-#endif
                           nptr->contclass = NULL;
                           nptr->contclasslen = 0;
                           np++;
                           dash = strchr(piece, '/');
                           if (dash) {
                             *dash = '\0';
 
                             if (ignorechars) {
                               if (utf8) {
                                 remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len);
                               } else {
                                 remove_ignored_chars(piece,ignorechars);
                               }
                             }
-                            
+
                             if (complexprefixes) {
                                 if (utf8) reverseword_utf(piece); else reverseword(piece);
                             }
                             nptr->appnd = mystrdup(piece);
-                            
+
                             if (pHMgr->is_aliasf()) {
                                 int index = atoi(dash + 1);
-                                nptr->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(nptr->contclass));
+                                nptr->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(nptr->contclass), af);
+                                if (!nptr->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1);
                             } else {
-                                nptr->contclasslen = (unsigned short) pHMgr->decode_flags(&(nptr->contclass), dash + 1);
+                                nptr->contclasslen = (unsigned short) pHMgr->decode_flags(&(nptr->contclass), dash + 1, af);
                                 flag_qsort(nptr->contclass, 0, nptr->contclasslen);
                             }
                             *dash = '/';
 
                             havecontclass = 1;
                             for (unsigned short _i = 0; _i < nptr->contclasslen; _i++) {
                               contclasses[(nptr->contclass)[_i]] = 1;
                             }
@@ -3971,185 +4202,172 @@ int  AffixMgr::parse_affix(char * line, 
                               } else {
                                 remove_ignored_chars(piece,ignorechars);
                               }
                             }
 
                             if (complexprefixes) {
                                 if (utf8) reverseword_utf(piece); else reverseword(piece);
                             }
-                            nptr->appnd = mystrdup(piece);       
+                            nptr->appnd = mystrdup(piece);
                           }
-                          
+
                           nptr->appndl = (unsigned char) strlen(nptr->appnd);
                           if (strcmp(nptr->appnd,"0") == 0) {
                               free(nptr->appnd);
                               nptr->appnd=mystrdup("");
                               nptr->appndl = 0;
                           }   
                           break; 
                         }
 
                 // piece 5 - is the conditions descriptions
                 case 4: { 
                           np++;
                           if (complexprefixes) {
-                            int neg = 0;
                             if (utf8) reverseword_utf(piece); else reverseword(piece);
-                            // reverse condition
-                            for (char * k = piece + strlen(piece) - 1; k >= piece; k--) {
-                                switch(*k) {
-                                  case '[': {
-                                        if (neg) *(k+1) = '['; else *k = ']';
-                                        break;
-                                    }
-                                  case ']': {
-                                        *k = '[';
-                                        if (neg) *(k+1) = '^';
-                                        neg = 0;
-                                        break;
-                                    }
-                                  case '^': {
-                                       if (*(k+1) == ']') neg = 1; else *(k+1) = *k;
-                                       break;
-                                    }
-                                  default: {
-                                    if (neg) *(k+1) = *k;
-                                  }
-                               }
-                            }
+                            reverse_condition(piece);
                           }
                           if (nptr->stripl && (strcmp(piece, ".") != 0) &&
-                            redundant_condition(at, nptr->strip, nptr->stripl, piece, nl))
+                            redundant_condition(at, nptr->strip, nptr->stripl, piece, af->getlinenum()))
                                 strcpy(piece, ".");
-                          if (encodeit(nptr,piece)) return 1;
+                          if (at == 'S') {
+                            reverseword(piece);
+                            reverse_condition(piece);
+                          }
+                          if (encodeit(nptr, piece)) return 1;
                          break;
                 }
-                
-#ifdef HUNSPELL_EXPERIMENTAL
+
                 case 5: {
                           np++;
                           if (pHMgr->is_aliasm()) {
                             int index = atoi(piece);
                             nptr->morphcode = pHMgr->get_aliasm(index);
                           } else {
-                            if (complexprefixes) {
+                            if (complexprefixes) { // XXX - fix me for morph. gen.
                                 if (utf8) reverseword_utf(piece); else reverseword(piece);
                             }
+                            // add the remaining of the line
+                            if (*tp) {
+                                *(tp - 1) = ' ';
+                                tp = tp + strlen(tp);
+                            }
                             nptr->morphcode = mystrdup(piece);
+                            if (!nptr->morphcode) return 1;
                           }
                           break; 
                 }
-#endif
-
                 default: break;
              }
              i++;
          }
-         free(piece);
          piece = mystrsep(&tp, 0);
       }
       // check to make sure we parsed enough pieces
       if (np < 4) {
           char * err = pHMgr->encode_flag(aflag);
-          HUNSPELL_WARNING(stderr, "error: affix %s is corrupt near line %s\n", err, nl);
-          free(err);
+          if (err) {
+            HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
+                af->getlinenum(), err);
+            free(err);
+          }
           free(ptr);
           return 1;
       }
 
 #ifdef DEBUG
-#ifdef HUNSPELL_EXPERIMENTAL
       // detect unnecessary fields, excepting comments
       if (basefieldnum) {
         int fieldnum = !(nptr->morphcode) ? 5 : ((*(nptr->morphcode)=='#') ? 5 : 6);
           if (fieldnum != basefieldnum) 
-            HUNSPELL_WARNING(stderr, "warning: bad field number:\n%s\n", nl);
+            HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum());
       } else {
         basefieldnum = !(nptr->morphcode) ? 5 : ((*(nptr->morphcode)=='#') ? 5 : 6);
       }
 #endif
-#endif
       nptr++;
    }
  
    // now create SfxEntry or PfxEntry objects and use links to
    // build an ordered (sorted by affix string) list
    nptr = ptr;
    for (int k = 0; k < numents; k++) {
       if (at == 'P') {
           PfxEntry * pfxptr = new PfxEntry(this,nptr);
           build_pfxtree((AffEntry *)pfxptr);
       } else {
           SfxEntry * sfxptr = new SfxEntry(this,nptr);
           build_sfxtree((AffEntry *)sfxptr); 
       }
       nptr++;
-   }      
+   }
    free(ptr);
    return 0;
 }
 
-int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, char * warnvar) {
+int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) {
   int condl = strlen(cond);
   int i;
   int j;
   int neg;
   int in;
   if (ft == 'P') { // prefix
     if (strncmp(strip, cond, condl) == 0) return 1;
     if (utf8) {
     } else {
       for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) {
         if (cond[j] != '[') {
           if (cond[j] != strip[i]) {
-            HUNSPELL_WARNING(stderr, "warning: incompatible stripping characters and condition:\n%s\n", warnvar);
+            HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
+            return 0;
           }
         } else {
           neg = (cond[j+1] == '^') ? 1 : 0;
           in = 0;
           do {
             j++;
             if (strip[i] == cond[j]) in = 1;
           } while ((j < (condl - 1)) && (cond[j] != ']'));
           if (j == (condl - 1) && (cond[j] != ']')) {
-            HUNSPELL_WARNING(stderr, "error: missing ] in condition:\n%s\n", warnvar);
+            HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum);
             return 0;
           }
           if ((!neg && !in) || (neg && in)) {
-            HUNSPELL_WARNING(stderr, "warning: incompatible stripping characters and condition:\n%s\n", warnvar);
-            return 0;          
+            HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
+            return 0;
           }
         }
       }
       if (j >= condl) return 1;
     }
   } else { // suffix
     if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1;
     if (utf8) {
     } else {
       for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) {
         if (cond[j] != ']') {
           if (cond[j] != strip[i]) {
-            HUNSPELL_WARNING(stderr, "warning: incompatible stripping characters and condition:\n%s\n", warnvar);
+            HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
+            return 0;
           }
         } else {
           in = 0;
           do {
             j--;
             if (strip[i] == cond[j]) in = 1;
           } while ((j > 0) && (cond[j] != '['));
           if ((j == 0) && (cond[j] != '[')) {
-            HUNSPELL_WARNING(stderr, "error: missing ] in condition:\n%s\n", warnvar);
+            HUNSPELL_WARNING(stderr, "error: error: %d: missing ] in condition:\n%s\n", linenum);
             return 0;
           }
           neg = (cond[j+1] == '^') ? 1 : 0;
           if ((!neg && !in) || (neg && in)) {
-            HUNSPELL_WARNING(stderr, "warning: incompatible stripping characters and condition:\n%s\n", warnvar);
-            return 0;          
+            HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum);
+            return 0;
           }
         }
       }
       if (j < 0) return 1;
-    }    
+    }
   }
   return 0;
 }
--- a/extensions/spellcheck/hunspell/src/affixmgr.hxx
+++ b/extensions/spellcheck/hunspell/src/affixmgr.hxx
@@ -65,29 +65,32 @@ using namespace std;
 #else
 #include <cstdio>
 #endif
 
 #include "atypes.hxx"
 #include "baseaffix.hxx"
 #include "hashmgr.hxx"
 #include "phonet.hxx"
+#include "replist.hxx"
 
 // check flag duplication
 #define dupSFX        (1 << 0)
 #define dupPFX        (1 << 1)
 
 class AffixMgr
 {
 
   AffEntry *          pStart[SETSIZE];
   AffEntry *          sStart[SETSIZE];
-  AffEntry *          pFlag[CONTSIZE];
-  AffEntry *          sFlag[CONTSIZE];
+  AffEntry *          pFlag[SETSIZE];
+  AffEntry *          sFlag[SETSIZE];
   HashMgr *           pHMgr;
+  HashMgr **          alldic;
+  int *               maxdic;
   char *              keystring;
   char *              trystring;
   char *              encoding;
   struct cs_info *    csconv;
   int                 utf8;
   int                 complexprefixes;
   FLAG                compoundflag;
   FLAG                compoundbegin;
@@ -95,28 +98,32 @@ class AffixMgr
   FLAG                compoundend;
   FLAG                compoundroot;
   FLAG                compoundforbidflag;
   FLAG                compoundpermitflag;
   int                 checkcompounddup;
   int                 checkcompoundrep;
   int                 checkcompoundcase;
   int                 checkcompoundtriple;
+  int                 simplifiedtriple;
   FLAG                forbiddenword;
   FLAG                nosuggest;
-  FLAG                pseudoroot;
+  FLAG                needaffix;
   int                 cpdmin;
   int                 numrep;
   replentry *         reptable;
+  RepList *           iconvtable;
+  RepList *           oconvtable;
   int                 nummap;
   mapentry *          maptable;
   int                 numbreak;
   char **             breaktable;
   int                 numcheckcpd;
-  replentry *         checkcpdtable;
+  patentry *          checkcpdtable;
+  int                 simplifiedcpd;
   int                 numdefcpd;
   flagentry *         defcpdtable;
   phonetable *        phone;
   int                 maxngramsugs;
   int                 nosplitsugs;
   int                 sugswithdots;
   int                 cpdwordmax;
   int                 cpdmaxsyllable;
@@ -139,73 +146,85 @@ class AffixMgr
   int                 ignorechars_utf16_len;
   char *              version;
   char *              lang;
   int                 langnum;
   FLAG                lemma_present;
   FLAG                circumfix;
   FLAG                onlyincompound;
   FLAG                keepcase;
+  FLAG                substandard;
   int                 checksharps;
+  int                 fullstrip;
 
   int                 havecontclass; // boolean variable
   char                contclasses[CONTSIZE]; // flags of possible continuing classes (twofold affix)
-  flag                flag_mode;
-  
+
 public:
- 
-  AffixMgr(const char * affpath, HashMgr * ptr);
+
+  AffixMgr(const char * affpath, HashMgr** ptr, int * md,
+    const char * key = NULL);
   ~AffixMgr();
   struct hentry *     affix_check(const char * word, int len,
-            const unsigned short needflag = (unsigned short) 0, char in_compound = IN_CPD_NOT);
+            const unsigned short needflag = (unsigned short) 0,
+            char in_compound = IN_CPD_NOT);
   struct hentry *     prefix_check(const char * word, int len,
             char in_compound, const FLAG needflag = FLAG_NULL);
   inline int isSubset(const char * s1, const char * s2);
   struct hentry *     prefix_check_twosfx(const char * word, int len,
             char in_compound, const FLAG needflag = FLAG_NULL);
   inline int isRevSubset(const char * s1, const char * end_of_s2, int len);
-  struct hentry *     suffix_check(const char * word, int len, int sfxopts, AffEntry* ppfx,
-                        char ** wlst, int maxSug, int * ns, const FLAG cclass = FLAG_NULL,
-                        const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT);
+  struct hentry *     suffix_check(const char * word, int len, int sfxopts,
+            AffEntry* ppfx, char ** wlst, int maxSug, int * ns,
+            const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL,
+            char in_compound = IN_CPD_NOT);
   struct hentry *     suffix_check_twosfx(const char * word, int len,
             int sfxopts, AffEntry* ppfx, const FLAG needflag = FLAG_NULL);
 
   char * affix_check_morph(const char * word, int len,
-                    const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT);
+            const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT);
   char * prefix_check_morph(const char * word, int len,
-                    char in_compound, const FLAG needflag = FLAG_NULL);
-  char * suffix_check_morph (const char * word, int len, int sfxopts, AffEntry * ppfx,
-            const FLAG cclass = FLAG_NULL, const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT);
+            char in_compound, const FLAG needflag = FLAG_NULL);
+  char * suffix_check_morph (const char * word, int len, int sfxopts,
+            AffEntry * ppfx, const FLAG cclass = FLAG_NULL,
+            const FLAG needflag = FLAG_NULL, char in_compound = IN_CPD_NOT);
 
   char * prefix_check_twosfx_morph(const char * word, int len,
             char in_compound, const FLAG needflag = FLAG_NULL);
   char * suffix_check_twosfx_morph(const char * word, int len,
             int sfxopts, AffEntry * ppfx, const FLAG needflag = FLAG_NULL);
 
-  int                 expand_rootword(struct guessword * wlst, int maxn, const char * ts,
-                        int wl, const unsigned short * ap, unsigned short al, char * bad, int,
-                        char *);
+  char * morphgen(char * ts, int wl, const unsigned short * ap,
+            unsigned short al, char * morph, char * targetmorph, int level);
+
+  int    expand_rootword(struct guessword * wlst, int maxn, const char * ts,
+            int wl, const unsigned short * ap, unsigned short al, char * bad,
+            int, char *);
 
-  short               get_syllable (const char * word, int wlen);
-  int                 cpdrep_check(const char * word, int len);
-  int                 cpdpat_check(const char * word, int len);
-  int                 defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** rwords, char all);
-  int                 cpdcase_check(const char * word, int len);
-  inline int                 candidate_check(const char * word, int len);
-  struct hentry *     compound_check(const char * word, int len,
-                              short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words,
-                              char hu_mov_rule, int * cmpdstemnum, int * cmpdstem, char is_sug);
+  short       get_syllable (const char * word, int wlen);
+  int         cpdrep_check(const char * word, int len);
+  int         cpdpat_check(const char * word, int len, hentry * r1, hentry * r2);
+  int         defcpd_check(hentry *** words, short wnum, hentry * rv,
+                    hentry ** rwords, char all);
+  int         cpdcase_check(const char * word, int len);
+  inline int  candidate_check(const char * word, int len);
+  void        setcminmax(int * cmin, int * cmax, const char * word, int len);
+  struct hentry * compound_check(const char * word, int len, short wordnum,
+            short numsyllable, short maxwordnum, short wnum, hentry ** words,
+            char hu_mov_rule, char is_sug);
 
-  int compound_check_morph(const char * word, int len,
-                              short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words,
-                              char hu_mov_rule, char ** result, char * partresult);
+  int compound_check_morph(const char * word, int len, short wordnum,
+            short numsyllable, short maxwordnum, short wnum, hentry ** words,
+            char hu_mov_rule, char ** result, char * partresult);
 
-  struct hentry *     lookup(const char * word);
+  struct hentry * lookup(const char * word);
   int                 get_numrep();
   struct replentry *  get_reptable();
+  RepList *           get_iconvtable();
+  RepList *           get_oconvtable();
   struct phonetable * get_phonetable();
   int                 get_nummap();
   struct mapentry *   get_maptable();
   int                 get_numbreak();
   char **             get_breaktable();
   char *              get_encoding();
   int                 get_langnum();
   char *              get_key_string();
@@ -214,18 +233,17 @@ public:
   unsigned short *    get_wordchars_utf16(int * len);
   char *              get_ignore();
   unsigned short *    get_ignore_utf16(int * len);
   int                 get_compound();
   FLAG                get_compoundflag();
   FLAG                get_compoundbegin();
   FLAG                get_forbiddenword();
   FLAG                get_nosuggest();
-//  FLAG                get_circumfix();
-  FLAG                get_pseudoroot();
+  FLAG                get_needaffix();
   FLAG                get_onlyincompound();
   FLAG                get_compoundroot();
   FLAG                get_lemma_present();
   int                 get_checknum();
   char *              get_possible_root();
   const char *        get_prefix();
   const char *        get_suffix();
   const char *        get_derived();
@@ -234,39 +252,42 @@ public:
   int                 get_utf8();
   int                 get_complexprefixes();
   char *              get_suffixed(char );
   int                 get_maxngramsugs();
   int                 get_nosplitsugs();
   int                 get_sugswithdots(void);
   FLAG                get_keepcase(void);
   int                 get_checksharps(void);
+  char *              encode_flag(unsigned short aflag);
+  int                 get_fullstrip();
 
 private:
-  int  parse_file(const char * affpath);
-//  int  parse_string(char * line, char ** out, const char * name);
-  int  parse_flag(char * line, unsigned short * out, const char * name);
-  int  parse_num(char * line, int * out, const char * name);
-//  int  parse_array(char * line, char ** out, unsigned short ** out_utf16,
-//            int * out_utf16_len, const char * name);
-  int  parse_cpdsyllable(char * line);
-  int  parse_reptable(char * line, FILE * af);
-  int  parse_phonetable(char * line, FILE * af);
-  int  parse_maptable(char * line, FILE * af);
-  int  parse_breaktable(char * line, FILE * af);
-  int  parse_checkcpdtable(char * line, FILE * af);
-  int  parse_defcpdtable(char * line, FILE * af);
-  int  parse_affix(char * line, const char at, FILE * af, char * dupflags);
+  int  parse_file(const char * affpath, const char * key);
+  int  parse_flag(char * line, unsigned short * out, FileMgr * af);
+  int  parse_num(char * line, int * out, FileMgr * af);
+  int  parse_cpdsyllable(char * line, FileMgr * af);
+  int  parse_reptable(char * line, FileMgr * af);
+  int  parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword);
+  int  parse_phonetable(char * line, FileMgr * af);
+  int  parse_maptable(char * line, FileMgr * af);
+  int  parse_breaktable(char * line, FileMgr * af);
+  int  parse_checkcpdtable(char * line, FileMgr * af);
+  int  parse_defcpdtable(char * line, FileMgr * af);
+  int  parse_affix(char * line, const char at, FileMgr * af, char * dupflags);
 
+  void reverse_condition(char *);
+  void debugflag(char * result, unsigned short flag);
+  int condlen(char *);
   int encodeit(struct affentry * ptr, char * cs);
   int build_pfxtree(AffEntry* pfxptr);
   int build_sfxtree(AffEntry* sfxptr);
   int process_pfx_order();
   int process_sfx_order();
   AffEntry * process_pfx_in_order(AffEntry * ptr, AffEntry * nptr);
   AffEntry * process_sfx_in_order(AffEntry * ptr, AffEntry * nptr);
   int process_pfx_tree_to_list();
   int process_sfx_tree_to_list();
-  int redundant_condition(char, char * strip, int stripl, const char * cond, char *);
+  int redundant_condition(char, char * strip, int stripl,
+      const char * cond, int);
 };
 
 #endif
-
--- a/extensions/spellcheck/hunspell/src/atypes.hxx
+++ b/extensions/spellcheck/hunspell/src/atypes.hxx
@@ -64,41 +64,43 @@
 // empty inline function to switch off warnings (instead of the C99 standard variadic macros)
 static inline void HUNSPELL_WARNING(FILE *, const char *, ...) {}
 #endif
 #endif
 
 // HUNSTEM def.
 #define HUNSTEM
 
-#include "csutil.hxx"
 #include "hashmgr.hxx"
+#include "w_char.hxx"
 
 #define SETSIZE         256
 #define CONTSIZE        65536
 #define MAXWORDLEN      100
 #define MAXWORDUTF8LEN  256
 
 // affentry options
 #define aeXPRODUCT      (1 << 0)
 #define aeUTF8          (1 << 1)
 #define aeALIASF        (1 << 2)
 #define aeALIASM        (1 << 3)
-#define aeINFIX         (1 << 4)
+#define aeLONGCOND      (1 << 4)
 
 // compound options
 #define IN_CPD_NOT   0
 #define IN_CPD_BEGIN 1
 #define IN_CPD_END   2
 #define IN_CPD_OTHER 3
 
 #define MAXLNLEN        8192
 
 #define MINCPDLEN       3
 #define MAXCOMPOUND     10
+#define MAXCONDLEN      20
+#define MAXCONDLEN_1    (MAXCONDLEN - sizeof(char *))
 
 #define MAXACC          1000
 
 #define FLAG unsigned short
 #define FLAG_NULL 0x00
 #define FREE_FLAG(a) a = 0
 
 #define TESTAFF( a, b , c ) flag_bsearch((unsigned short *) a, (unsigned short) b, c)
@@ -107,48 +109,46 @@ struct affentry
 {
    char * strip;
    char * appnd;
    unsigned char stripl;
    unsigned char appndl;
    char  numconds;
    char  opts;
    unsigned short aflag;
-   union {
-        char   base[SETSIZE];
-        struct {
-                char ascii[SETSIZE/2];
-                char neg[8];
-                char all[8];
-                w_char * wchars[8];
-                int wlen[8];
-        } utf8;
-   } conds;
-#ifdef HUNSPELL_EXPERIMENTAL
-   char *       morphcode;
-#endif
    unsigned short * contclass;
    short        contclasslen;
+   union {
+     char conds[MAXCONDLEN];
+     struct {
+       char conds1[MAXCONDLEN_1];
+       char * conds2;
+     } l;
+   } c;
+   char *       morphcode;
+};
+
+struct guessword {
+  char * word;
+  bool allow;
+  char * orig;
 };
 
 struct mapentry {
   char * set;
   w_char * set_utf16;
   int len;
 };
 
 struct flagentry {
   FLAG * def;
   int len;
 };
 
-struct guessword {
-  char * word;
-  bool allow;
-  char * orig;
+struct patentry {
+  char * pattern;
+  char * pattern2;
+  char * pattern3;
+  FLAG cond;
+  FLAG cond2;
 };
 
 #endif
-
-
-
-
-
--- a/extensions/spellcheck/hunspell/src/baseaffix.hxx
+++ b/extensions/spellcheck/hunspell/src/baseaffix.hxx
@@ -57,31 +57,28 @@
 #ifndef _BASEAFF_HXX_
 #define _BASEAFF_HXX_
 
 class AffEntry
 {
 public:
 
 protected:
-       char *       appnd;
-       char *       strip;
-       unsigned char  appndl;
-       unsigned char  stripl;
-       char         numconds;
-       char  opts;
-       unsigned short aflag;
-       union {
-         char   base[SETSIZE];
-         struct {
-                char  ascii[SETSIZE/2];
-                char neg[8];
-                char all[8];
-                w_char * wchars[8];
-                int wlen[8];
-         } utf8;
-       } conds;
-       char *       morphcode;
-       unsigned short * contclass;
-       short        contclasslen;
+    char *         appnd;
+    char *         strip;
+    unsigned char  appndl;
+    unsigned char  stripl;
+    char           numconds;
+    char           opts;
+    unsigned short aflag;
+    union {
+        char       conds[MAXCONDLEN];
+        struct {
+            char   conds1[MAXCONDLEN_1];
+            char * conds2;
+        } l;
+    } c;
+    char *           morphcode;
+    unsigned short * contclass;
+    short            contclasslen;
 };
 
 #endif
--- a/extensions/spellcheck/hunspell/src/csutil.cpp
+++ b/extensions/spellcheck/hunspell/src/csutil.cpp
@@ -61,18 +61,18 @@
 #include <cctype>
 #else
 #include <stdlib.h> 
 #include <string.h>
 #include <stdio.h> 
 #include <ctype.h>
 #endif
 
+#include "atypes.hxx"
 #include "csutil.hxx"
-#include "atypes.hxx"
 #include "langnum.hxx"
 
 #ifdef OPENOFFICEORG
 #  include <unicode/uchar.h>
 #else
 #  ifndef MOZILLA_CLIENT
 #    include "utf_info.cxx"
 #    define UTF_LST_LEN (sizeof(utf_lst) / (sizeof(unicode_info)))
@@ -103,18 +103,18 @@ using namespace std;
 #endif
 #endif
 
 static struct unicode_info2 * utf_tbl = NULL;
 static int utf_tbl_count = 0; // utf_tbl can be used by multiple Hunspell instances
 
 /* only UTF-16 (BMP) implementation */
 char * u16_u8(char * dest, int size, const w_char * src, int srclen) {
-    char * u8 = dest;
-    char * u8_max = u8 + size;
+    signed char * u8 = (signed char *)dest;
+    signed char * u8_max = (signed char *)(u8 + size);
     const w_char * u2 = src;
     const w_char * u2_max = src + srclen;
     while ((u2 < u2_max) && (u8 < u8_max)) {
         if (u2->h) { // > 0xFF
             // XXX 4-byte haven't implemented yet.
             if (u2->h >= 0x08) {   // >= 0x800 (3-byte UTF-8 character)
                 *u8 = 0xe0 + (u2->h >> 4);
                 u8++;
@@ -151,17 +151,17 @@ char * u16_u8(char * dest, int size, con
     }
     *u8 = '\0';
     return dest;
 }
 
 
 /* only UTF-16 (BMP) implementation */
 int u8_u16(w_char * dest, int size, const char * src) {
-    const char * u8 = src;
+    const signed char * u8 = (const signed char *)src;
     w_char * u2 = dest;
     w_char * u2_max = u2 + size;
     
     while ((u2 < u2_max) && *u8) {
     switch ((*u8) & 0xf0) {
         case 0x00:
         case 0x10:
         case 0x20:
@@ -173,48 +173,48 @@ int u8_u16(w_char * dest, int size, cons
             u2->h = 0;
             u2->l = *u8;
             break;
         }
         case 0x80:
         case 0x90:
         case 0xa0:
         case 0xb0: {
-            HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Unexpected continuation bytes in %d. character position\n%s\n", u8 - src, src);    
+            HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Unexpected continuation bytes in %ld. character position\n%s\n", static_cast<long>(u8 - (signed char *)src), src);    
             u2->h = 0xff;
             u2->l = 0xfd;
             break;
         }
         case 0xc0:
         case 0xd0: {    // 2-byte UTF-8 codes
             if ((*(u8+1) & 0xc0) == 0x80) {
                 u2->h = (*u8 & 0x1f) >> 2;
                 u2->l = (*u8 << 6) + (*(u8+1) & 0x3f);
                 u8++;
             } else {
-                HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %d. character position:\n%s\n", u8 - src, src);
+                HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast<long>(u8 - (signed char *)src), src);
                 u2->h = 0xff;
                 u2->l = 0xfd;
             }
             break;
         }
         case 0xe0: {    // 3-byte UTF-8 codes
             if ((*(u8+1) & 0xc0) == 0x80) {
                 u2->h = ((*u8 & 0x0f) << 4) + ((*(u8+1) & 0x3f) >> 2);
                 u8++;
                 if ((*(u8+1) & 0xc0) == 0x80) {
                     u2->l = (*u8 << 6) + (*(u8+1) & 0x3f);
                     u8++;
                 } else {
-                    HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %d. character position:\n%s\n", u8 - src, src);
+                    HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast<long>(u8 - (signed char *)src), src);
                     u2->h = 0xff;
                     u2->l = 0xfd;
                 }
             } else {
-                HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %d. character position:\n%s\n", u8 - src, src);
+                HUNSPELL_WARNING(stderr, "UTF-8 encoding error. Missing continuation byte in %ld. character position:\n%s\n", static_cast<long>(u8 - (signed char *)src), src);
                 u2->h = 0xff;
                 u2->l = 0xfd;
             }
             break;
         }
         case 0xf0: {    // 4 or more byte UTF-8 codes
             HUNSPELL_WARNING(stderr, "This UTF-8 encoding can't convert to UTF-16:\n%s\n", src);
             u2->h = 0xff;
@@ -269,65 +269,68 @@ int flag_bsearch(unsigned short flags[],
 
  // strip strings into token based on single char delimiter
  // acts like strsep() but only uses a delim char and not
  // a delim string
  // default delimiter: white space characters
  
  char * mystrsep(char ** stringp, const char delim)
  {
-   char * rv = NULL;
    char * mp = *stringp;
-   int n = strlen(mp);
-   if (n > 0) {
+   if (*mp != '\0') {
       char * dp;
       if (delim) {
-        dp = (char *)memchr(mp,(int)((unsigned char)delim),n);
+        dp = strchr(mp, delim);
       } else {
         // don't use isspace() here, the string can be in some random charset
         // that's way different than the locale's
         for (dp = mp; (*dp && *dp != ' ' && *dp != '\t'); dp++);
         if (!*dp) dp = NULL;
       }
       if (dp) {
          *stringp = dp+1;
          int nc = (int)((unsigned long)dp - (unsigned long)mp);
-         rv = (char *) malloc(nc+1);
-	 if (rv) {
-            memcpy(rv,mp,nc);
-            *(rv+nc) = '\0';
-            return rv;
-	 }
+         *(mp+nc) = '\0';
+         return mp;
       } else {
-         rv = (char *) malloc(n+1);
-         if (rv) {
-    	    memcpy(rv, mp, n);
-            *(rv+n) = '\0';
-            *stringp = mp + n;
-            return rv;
-         }
+         *stringp = mp + strlen(mp);
+         return mp;
       }
    }
    return NULL;
  }
 
- 
  // replaces strdup with ansi version
  char * mystrdup(const char * s)
  {
    char * d = NULL;
    if (s) {
       int sl = strlen(s);
       d = (char *) malloc(((sl+1) * sizeof(char)));
-      if (d) memcpy(d,s,((sl+1)*sizeof(char)));
+      if (d) {
+         memcpy(d,s,((sl+1)*sizeof(char)));
+         return d;
+      }
+      HUNSPELL_WARNING(stderr, "Can't allocate memory.\n");
    }
    return d;
  }
- 
- 
+
+ // strcat for limited length destination string
+ char * mystrcat(char * dest, const char * st, int max) {
+   int len;
+   int len2;
+   if (dest == NULL || st == NULL) return dest;
+   len = strlen(dest);
+   len2 = strlen(st);
+   if (len + len2 + 1 > max) return dest;
+   strcpy(dest + len, st);
+   return dest;
+ }
+
  // remove cross-platform text line end characters
  void mychomp(char * s)
  {
    int k = strlen(s);
    if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0';
    if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0';
  }
  
@@ -344,122 +347,268 @@ int flag_bsearch(unsigned short flags[],
           char * q = d;
           while (p >= s) *q++ = *p--;
           *q = '\0';
         }
      }
      return d;
  }
 
-#ifdef HUNSPELL_EXPERIMENTAL
+// break text to lines
+// return number of lines
+int line_tok(const char * text, char *** lines, char breakchar) {
+    int linenum = 0;
+    char * dup = mystrdup(text);
+    char * p = strchr(dup, breakchar);
+    while (p) {
+        linenum++;
+        *p = '\0';
+        p++;
+        p = strchr(p, breakchar);
+    }
+    linenum++;
+//    fprintf(stderr, "LINEN:%d %p %p\n", linenum, lines, *lines);
+    *lines = (char **) malloc(linenum * sizeof(char *));
+//    fprintf(stderr, "hello\n");
+    if (!(*lines)) {
+        free(dup);
+        return 0;
+    }
+
+    p = dup;
+    int l = 0;
+    for (int i = 0; i < linenum; i++) {
+        if (*p != '\0') {
+            (*lines)[l] = mystrdup(p);
+            if (!(*lines)[l]) {
+                for (i = 0; i < l; i++) free((*lines)[i]);
+                free(dup);
+                return 0;
+            }
+            l++;
+        }
+        p += strlen(p) + 1;
+    }
+    free(dup);
+    if (!l) free(*lines);
+    return l;
+}
+
+// uniq line in place
+char * line_uniq(char * text, char breakchar) {
+    char ** lines;
+    int linenum = line_tok(text, &lines, breakchar);
+    int i;
+    strcpy(text, lines[0]);
+    for ( i = 1; i < linenum; i++ ) {
+        int dup = 0;
+        for (int j = 0; j < i; j++) {
+            if (strcmp(lines[i], lines[j]) == 0) dup = 1;
+        }
+        if (!dup) {
+            if ((i > 1) || (*(lines[0]) != '\0')) {
+                sprintf(text + strlen(text), "%c", breakchar);
+            }
+            strcat(text, lines[i]);
+        }
+    }
+    for ( i = 0; i < linenum; i++ ) {
+        if (lines[i]) free(lines[i]);
+    }
+    if (lines) free(lines);
+    return text;
+}
+
+// uniq and boundary for compound analysis: "1\n\2\n\1" -> " ( \1 | \2 ) "
+char * line_uniq_app(char ** text, char breakchar) {
+    if (!strchr(*text, breakchar)) {
+        return *text;
+    }
+    
+    char ** lines;
+    int i;
+    int linenum = line_tok(*text, &lines, breakchar);
+    int dup = 0;
+    for (i = 0; i < linenum; i++) {
+        for (int j = 0; j < (i - 1); j++) {
+            if (strcmp(lines[i], lines[j]) == 0) {
+                *(lines[i]) = '\0';
+                dup++;
+                break;
+            }
+        }
+    }
+    if ((linenum - dup) == 1) {
+        strcpy(*text, lines[0]);
+        freelist(&lines, linenum);
+        return *text;
+    }
+    char * newtext = (char *) malloc(strlen(*text) + 2 * linenum + 3 + 1);
+    if (newtext) {
+        free(*text);
+        *text = newtext;
+    } else {
+        freelist(&lines, linenum);
+        return *text;
+    }    
+    strcpy(*text," ( ");
+    for (i = 0; i < linenum; i++) if (*(lines[i])) {
+        sprintf(*text + strlen(*text), "%s%s", lines[i], " | ");
+    }
+    (*text)[strlen(*text) - 2] = ')'; // " ) "
+    freelist(&lines, linenum);
+    return *text;
+}
+
  // append s to ends of every lines in text
  void strlinecat(char * dest, const char * s)
  {
     char * dup = mystrdup(dest);
     char * source = dup;
     int len = strlen(s);
-    while (*source) {
-        if (*source == '\n') {
-            strncpy(dest, s, len);
-            dest += len;
+    if (dup) {
+        while (*source) {
+            if (*source == '\n') {
+                strncpy(dest, s, len);
+                dest += len;
+            }
+            *dest = *source;
+            source++; dest++;
         }
-        *dest = *source;
-        source++; dest++;
+        strcpy(dest, s);
+        free(dup);
     }
-    strcpy(dest, s);
-    free(dup);
  }
 
-// break text to lines
-// return number of lines
-int line_tok(const char * text, char *** lines) {
-    int linenum = 0;
-    char * dup = mystrdup(text);
-    char * p = strchr(dup, '\n');
-    while (p) {
-        linenum++;
-        *p = '\0';
-        p++;
-        p = strchr(p, '\n');
-    }
-    *lines = (char **) calloc(linenum + 1, sizeof(char *));
-    if (!(*lines)) return -1;
-
-    p = dup; 
-    for (int i = 0; i < linenum + 1; i++) {
-        (*lines)[i] = mystrdup(p);
-        p += strlen(p) + 1;
-    }
-    free(dup);
-    return linenum;
-}
-
-// uniq line in place
-char * line_uniq(char * text) {
-    char ** lines;
-    int linenum = line_tok(text, &lines);
-    int i;
-    strcpy(text, lines[0]);
-    for ( i = 1; i<=linenum; i++ ) {
-        int dup = 0;
-        for (int j = 0; j < i; j++) {
-            if (strcmp(lines[i], lines[j]) == 0) dup = 1;
-        }
-        if (!dup) {
-            if ((i > 1) || (*(lines[0]) != '\0')) strcat(text, "\n");
-            strcat(text, lines[i]);
-        }
-    }
-    for ( i = 0; i<=linenum; i++ ) {
-        if (lines[i]) free(lines[i]);
-    }
-    if (lines) free(lines);
+// change \n to char c
+char * tr(char * text, char oldc, char newc) {
+    char * p;
+    for (p = text; *p; p++) if (*p == oldc) *p = newc;
     return text;
 }
 
-// change \n to char c
-char * line_join(char * text, char c) {
-    char * p;
-    for (p = text; *p; p++) if (*p == '\n') *p = c;
-    return text;
+// morphcmp(): compare MORPH_DERI_SFX, MORPH_INFL_SFX and MORPH_TERM_SFX fields
+// in the first line of the inputs
+// return 0, if inputs equal
+// return 1, if inputs may equal with a secondary suffix
+// otherwise return -1
+int morphcmp(const char * s, const char * t)
+{
+    int se = 0;
+    int te = 0;
+    const char * sl;
+    const char * tl;    
+    const char * olds;
+    const char * oldt;
+    if (!s || !t) return 1;
+    olds = s;
+    sl = strchr(s, '\n');
+    s = strstr(s, MORPH_DERI_SFX);
+    if (!s || (sl && sl < s)) s = strstr(olds, MORPH_INFL_SFX);
+    if (!s || (sl && sl < s)) {
+        s= strstr(olds, MORPH_TERM_SFX);
+        olds = NULL;
+    }
+    oldt = t;
+    tl = strchr(t, '\n');
+    t = strstr(t, MORPH_DERI_SFX);
+    if (!t || (tl && tl < t)) t = strstr(oldt, MORPH_INFL_SFX);
+    if (!t || (tl && tl < t)) {
+        t = strstr(oldt, MORPH_TERM_SFX);
+        oldt = NULL;
+    }
+    while (s && t && (!sl || sl > s) && (!tl || tl > t)) {
+        s += MORPH_TAG_LEN;
+        t += MORPH_TAG_LEN;
+        se = 0;
+        te = 0;
+        while ((*s == *t) && !se && !te) {
+            s++;
+            t++;
+            switch(*s) {
+                case ' ':
+                case '\n':
+                case '\t':
+                case '\0': se = 1;
+            }
+            switch(*t) {
+                case ' ':
+                case '\n':
+                case '\t':
+                case '\0': te = 1;
+            }
+        }
+        if (!se || !te) {
+            // not terminal suffix difference
+            if (olds) return -1;
+            return 1;
+        }
+        olds = s;
+        s = strstr(s, MORPH_DERI_SFX);
+        if (!s || (sl && sl < s)) s = strstr(olds, MORPH_INFL_SFX);
+        if (!s || (sl && sl < s)) {
+            s = strstr(olds, MORPH_TERM_SFX);
+            olds = NULL;
+        }
+        oldt = t;
+        t = strstr(t, MORPH_DERI_SFX);
+        if (!t || (tl && tl < t)) t = strstr(oldt, MORPH_INFL_SFX);
+        if (!t || (tl && tl < t)) {
+            t = strstr(oldt, MORPH_TERM_SFX);
+            oldt = NULL;
+        }
+    }
+    if (!s && !t && se && te) return 0;
+    return 1;
 }
 
-// leave only last {[^}]*} substring for handling zero morphemes
-char * delete_zeros(char * morphout) {
-    char * p = morphout;
-    char * q = p;
-    char * q2 = NULL;
-    int suffix = 0;
-    
-    for (;*p && *(p+1);) {
-        switch (*p) {
-            case '{': 
-                q2 = q;
-                q--;
-                break;
-            case '}':
-                if (q2) {
-                    suffix = 1;
-                    q--;
-                }
-                break; 
-            default:
-                if (suffix) {
-                    q = q2;
-                }
-                suffix = 0;
-                *q = *p;
-        }
-        p++;
-        q++;
+int get_sfxcount(const char * morph)
+{
+    if (!morph || !*morph) return 0;
+    int n = 0;
+    const char * old = morph;
+    morph = strstr(morph, MORPH_DERI_SFX);
+    if (!morph) morph = strstr(old, MORPH_INFL_SFX);
+    if (!morph) morph = strstr(old, MORPH_TERM_SFX);
+    while (morph) {
+        n++;
+        old = morph;
+        morph = strstr(morph + 1, MORPH_DERI_SFX);
+        if (!morph) morph = strstr(old + 1, MORPH_INFL_SFX);
+        if (!morph) morph = strstr(old + 1, MORPH_TERM_SFX);
     }
-    *q = '\0';
-    return morphout;
+    return n;
+}
+
+
+int fieldlen(const char * r)
+{
+    int n = 0;
+    while (r && *r != '\t' && *r != '\0' && *r != '\n' && *r != ' ') {
+        r++;
+        n++;
+    }
+    return n;
 }
-#endif // END OF HUNSPELL_EXPERIMENTAL CODE
+
+char * copy_field(char * dest, const char * morph, const char * var)
+{
+    if (!morph) return NULL;
+    const char * beg = strstr(morph, var);
+    if (beg) {
+       char * d = dest;
+       for (beg += MORPH_TAG_LEN; *beg != ' ' && *beg != '\t' &&
+            *beg != '\n' && *beg != '\0'; d++, beg++) {
+         *d = *beg;
+       }
+       *d = '\0';
+       return dest;
+  }
+  return NULL;
+}
 
 char * mystrrep(char * word, const char * pat, const char * rep) {
     char * pos = strstr(word, pat);
     if (pos) {
         int replen = strlen(rep);
         int patlen = strlen(pat);
         if (replen < patlen) {
             char * end = word + strlen(word);
@@ -500,16 +649,44 @@ char * mystrrep(char * word, const char 
    for (w_char * dest = w + l - 1; p < dest; p++, dest--) {
      r=*p;
      *p = *dest;
      *dest = r;
    }
    u16_u8(word, MAXWORDUTF8LEN, w, l);
    return 0;
  }
+
+ int uniqlist(char ** list, int n) {
+   int i;
+   if (n < 2) return n;
+   for (i = 0; i < n; i++) {
+     for (int j = 0; j < i; j++) {
+        if (list[j] && list[i] && (strcmp(list[j], list[i]) == 0)) {
+            free(list[i]);
+            list[i] = NULL;
+            break;
+        }
+     }
+   } 
+   int m = 1;  
+   for (i = 1; i < n; i++) if (list[i]) {
+        list[m] = list[i];
+        m++;
+    }
+   return m;
+ }
+ 
+ void freelist(char *** list, int n) {
+   if (list && *list && n > 0) {
+     for (int i = 0; i < n; i++) if ((*list)[i]) free((*list)[i]);
+     free(*list);
+     *list = NULL;
+   }
+ }
  
  // convert null terminated string to all caps
  void mkallcap(char * p, const struct cs_info * csconv)
  {
    while (*p != '\0') {
      *p = csconv[((unsigned char) *p)].cupper;
      p++;
    }
@@ -545,16 +722,30 @@ void mkallcap_utf(w_char * u, int nc, in
 }
  
  // convert null terminated string to have intial capital
  void mkinitcap(char * p, const struct cs_info * csconv)
  {
    if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
  }
 
+ // conversion function for protected memory
+ void store_pointer(char * dest, char * source)
+ {
+    memcpy(dest, &source, sizeof(char *));
+ }
+
+ // conversion function for protected memory
+ char * get_stored_pointer(char * s)
+ {
+    char * p;
+    memcpy(&p, s, sizeof(char *));
+    return p;
+ }
+
 #ifndef MOZILLA_CLIENT
  // convert null terminated string to all caps using encoding
  void enmkallcap(char * d, const char * p, const char * encoding)
  
  {
    struct cs_info * csconv = get_current_cs(encoding);
    while (*p != '\0') {
      *d++ = csconv[((unsigned char) *p)].cupper;
@@ -837,17 +1028,17 @@ struct cs_info iso1_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xdc },
 { 0x00, 0xfd, 0xdd },
 { 0x00, 0xfe, 0xde },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 
 struct cs_info iso2_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
@@ -1097,17 +1288,17 @@ struct cs_info iso2_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xdc },
 { 0x00, 0xfd, 0xdd },
 { 0x00, 0xfe, 0xde },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 
 struct cs_info iso3_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
@@ -1357,17 +1548,17 @@ struct cs_info iso3_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xdc },
 { 0x00, 0xfd, 0xdd },
 { 0x00, 0xfe, 0xde },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 struct cs_info iso4_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -1616,17 +1807,17 @@ struct cs_info iso4_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xdc },
 { 0x00, 0xfd, 0xdd },
 { 0x00, 0xfe, 0xde },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 struct cs_info iso5_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -1875,17 +2066,17 @@ struct cs_info iso5_tbl[] = {
 { 0x00, 0xf7, 0xa7 },
 { 0x00, 0xf8, 0xa8 },
 { 0x00, 0xf9, 0xa9 },
 { 0x00, 0xfa, 0xaa },
 { 0x00, 0xfb, 0xab },
 { 0x00, 0xfc, 0xac },
 { 0x00, 0xfd, 0xfd },
 { 0x00, 0xfe, 0xae },
-{ 0x00, 0xff, 0xaf },
+{ 0x00, 0xff, 0xaf }
 };
 
 struct cs_info iso6_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -2134,17 +2325,17 @@ struct cs_info iso6_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xf8 },
 { 0x00, 0xf9, 0xf9 },
 { 0x00, 0xfa, 0xfa },
 { 0x00, 0xfb, 0xfb },
 { 0x00, 0xfc, 0xfc },
 { 0x00, 0xfd, 0xfd },
 { 0x00, 0xfe, 0xfe },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 struct cs_info iso7_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -2393,17 +2584,17 @@ struct cs_info iso7_tbl[] = {
 { 0x00, 0xf7, 0xd7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xbc },
 { 0x00, 0xfd, 0xbe },
 { 0x00, 0xfe, 0xbf },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 struct cs_info iso8_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -2652,17 +2843,17 @@ struct cs_info iso8_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xf8 },
 { 0x00, 0xf9, 0xf9 },
 { 0x00, 0xfa, 0xfa },
 { 0x00, 0xfb, 0xfb },
 { 0x00, 0xfc, 0xfc },
 { 0x00, 0xfd, 0xfd },
 { 0x00, 0xfe, 0xfe },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 struct cs_info iso9_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -2911,17 +3102,17 @@ struct cs_info iso9_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xdc },
 { 0x00, 0xfd, 0x49 },
 { 0x00, 0xfe, 0xde },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 struct cs_info iso10_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -3170,17 +3361,17 @@ struct cs_info iso10_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xf8 },
 { 0x00, 0xf9, 0xf9 },
 { 0x00, 0xfa, 0xfa },
 { 0x00, 0xfb, 0xfb },
 { 0x00, 0xfc, 0xfc },
 { 0x00, 0xfd, 0xfd },
 { 0x00, 0xfe, 0xfe },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 struct cs_info koi8r_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -3429,17 +3620,17 @@ struct cs_info koi8r_tbl[] = {
 { 0x01, 0xd7, 0xf7 },
 { 0x01, 0xd8, 0xf8 },
 { 0x01, 0xd9, 0xf9 },
 { 0x01, 0xda, 0xfa },
 { 0x01, 0xdb, 0xfb },
 { 0x01, 0xdc, 0xfc },
 { 0x01, 0xdd, 0xfd },
 { 0x01, 0xde, 0xfe },
-{ 0x01, 0xdf, 0xff },
+{ 0x01, 0xdf, 0xff }
 };
 
 struct cs_info koi8u_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -3688,17 +3879,17 @@ struct cs_info koi8u_tbl[] = {
 { 0x01, 0xd7, 0xf7 },
 { 0x01, 0xd8, 0xf8 },
 { 0x01, 0xd9, 0xf9 },
 { 0x01, 0xda, 0xfa },
 { 0x01, 0xdb, 0xfb },
 { 0x01, 0xdc, 0xfc },
 { 0x01, 0xdd, 0xfd },
 { 0x01, 0xde, 0xfe },
-{ 0x01, 0xdf, 0xff },
+{ 0x01, 0xdf, 0xff }
 };
 
 struct cs_info cp1251_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -3947,17 +4138,17 @@ struct cs_info cp1251_tbl[] = {
 { 0x00, 0xf7, 0xd7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xdc },
 { 0x00, 0xfd, 0xdd },
 { 0x00, 0xfe, 0xde },
-{ 0x00, 0xff, 0xdf },
+{ 0x00, 0xff, 0xdf }
 };
 
 struct cs_info iso13_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -4206,17 +4397,17 @@ struct cs_info iso13_tbl[] = {
 { 0x00, 0xF7, 0xF7 },
 { 0x00, 0xF8, 0xD8 },
 { 0x00, 0xF9, 0xD9 },
 { 0x00, 0xFA, 0xDA },
 { 0x00, 0xFB, 0xDB },
 { 0x00, 0xFC, 0xDC },
 { 0x00, 0xFD, 0xDD },
 { 0x00, 0xFE, 0xDE },
-{ 0x00, 0xFF, 0xFF },
+{ 0x00, 0xFF, 0xFF }
 };
 
 
 struct cs_info iso14_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
@@ -4466,17 +4657,17 @@ struct cs_info iso14_tbl[] = {
 { 0x00, 0xf7, 0xd7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xdc },
 { 0x00, 0xfd, 0xdd },
 { 0x00, 0xfe, 0xde },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
 struct cs_info iso15_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -4725,17 +4916,17 @@ struct cs_info iso15_tbl[] = {
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xd8 },
 { 0x00, 0xf9, 0xd9 },
 { 0x00, 0xfa, 0xda },
 { 0x00, 0xfb, 0xdb },
 { 0x00, 0xfc, 0xdc },
 { 0x00, 0xfd, 0xdd },
 { 0x00, 0xfe, 0xde },
-{ 0x00, 0xff, 0xbe },
+{ 0x00, 0xff, 0xbe }
 };
 
 struct cs_info iscii_devanagari_tbl[] = {
 { 0x00, 0x00, 0x00 },
 { 0x00, 0x01, 0x01 },
 { 0x00, 0x02, 0x02 },
 { 0x00, 0x03, 0x03 },
 { 0x00, 0x04, 0x04 },
@@ -4984,45 +5175,46 @@ struct cs_info iscii_devanagari_tbl[] = 
 { 0x00, 0xf7, 0xf7 },
 { 0x00, 0xf8, 0xf8 },
 { 0x00, 0xf9, 0xf9 },
 { 0x00, 0xfa, 0xfa },
 { 0x00, 0xfb, 0xfb },
 { 0x00, 0xfc, 0xfc },
 { 0x00, 0xfd, 0xfd },
 { 0x00, 0xfe, 0xfe },
-{ 0x00, 0xff, 0xff },
+{ 0x00, 0xff, 0xff }
 };
 
-struct enc_entry encds[] = {
+static struct enc_entry encds[] = {
 {"ISO8859-1",iso1_tbl},
 {"ISO8859-2",iso2_tbl},
 {"ISO8859-3",iso3_tbl},
 {"ISO8859-4",iso4_tbl},
 {"ISO8859-5",iso5_tbl},
 {"ISO8859-6",iso6_tbl},
 {"ISO8859-7",iso7_tbl},
 {"ISO8859-8",iso8_tbl},
 {"ISO8859-9",iso9_tbl},
 {"ISO8859-10",iso10_tbl},
 {"KOI8-R",koi8r_tbl},
 {"KOI8-U",koi8u_tbl},
 {"microsoft-cp1251",cp1251_tbl},
 {"ISO8859-13", iso13_tbl},
 {"ISO8859-14", iso14_tbl},
 {"ISO8859-15", iso15_tbl},
-{"ISCII-DEVANAGARI", iscii_devanagari_tbl},
+{"ISCII-DEVANAGARI", iscii_devanagari_tbl}
 };
 
 struct cs_info * get_current_cs(const char * es) {
   struct cs_info * ccs = encds[0].cs_table;
   int n = sizeof(encds) / sizeof(encds[0]);
   for (int i = 0; i < n; i++) {
     if (strcmp(es,encds[i].enc_name) == 0) {
       ccs = encds[i].cs_table;
+      break;
     }
   }
   return ccs;
 }
 #else
 // XXX This function was rewritten for mozilla. Instead of storing the
 // conversion tables static in this file, create them when needed
 // with help the mozilla backend.
@@ -5258,24 +5450,24 @@ int unicodeisalpha(unsigned short c)
 }
 
 /* get type of capitalization */
 int get_captype(char * word, int nl, cs_info * csconv) {
    // now determine the capitalization type of the first nl letters
    int ncap = 0;
    int nneutral = 0;
    int firstcap = 0;
-
-      for (char * q = word; *q != '\0'; q++) {
-         if (csconv[*((unsigned char *)q)].ccase) ncap++;
-         if (csconv[*((unsigned char *)q)].cupper == csconv[*((unsigned char *)q)].clower) nneutral++;
-      }
-      if (ncap) {
-        firstcap = csconv[*((unsigned char *) word)].ccase;
-      }
+   if (csconv == NULL) return NOCAP;
+   for (char * q = word; *q != '\0'; q++) {
+      if (csconv[*((unsigned char *)q)].ccase) ncap++;
+      if (csconv[*((unsigned char *)q)].cupper == csconv[*((unsigned char *)q)].clower) nneutral++;
+   }
+   if (ncap) {
+     firstcap = csconv[*((unsigned char *) word)].ccase;
+   }
 
    // now finally set the captype
    if (ncap == 0) {
         return NOCAP;
    } else if ((ncap == 1) && firstcap) {
         return INITCAP;
    } else if ((ncap == nl) || ((ncap + nneutral) == nl)) {
         return ALLCAP;
@@ -5343,53 +5535,54 @@ void remove_ignored_chars(char * word, c
       if (!strchr(ignored_chars, *p)) {
         *word = *p;
         word++;
       }
    }
    *word = '\0';
 }
 
-int parse_string(char * line, char ** out, const char * warnvar)
+int parse_string(char * line, char ** out, int ln)
 {
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    if (*out) {
-      HUNSPELL_WARNING(stderr, "error: duplicate %s line\n", warnvar);
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions\n", ln);
       return 1;
    }
    piece = mystrsep(&tp, 0);
    while (piece) {
       if (*piece != '\0') {
           switch(i) {
               case 0: { np++; break; }
               case 1: { 
                 *out = mystrdup(piece);
+                if (!*out) return 1;
                 np++;
                 break;
               }
               default: break;
           }
           i++;
       }
-      free(piece);
+      // free(piece);
       piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
-      HUNSPELL_WARNING(stderr, "error: missing %s information\n", warnvar);
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", ln);
       return 1;
    } 
    return 0;
 }
 
-int parse_array(char * line, char ** out,
-        unsigned short ** out_utf16, int * out_utf16_len, const char * name, int utf8) {
-   if (parse_string(line, out, name)) return 1;
+int parse_array(char * line, char ** out, unsigned short ** out_utf16,
+       int * out_utf16_len, int utf8, int ln) {
+   if (parse_string(line, out, ln)) return 1;
    if (utf8) {
         w_char w[MAXWORDLEN];
         int n = u8_u16(w, MAXWORDLEN, *out);
         if (n > 0) {
             flag_qsort((unsigned short *) w, 0, n);
             *out_utf16 = (unsigned short *) malloc(n * sizeof(unsigned short));
             if (!*out_utf16) return 1;
             memcpy(*out_utf16, w, n * sizeof(unsigned short));
--- a/extensions/spellcheck/hunspell/src/csutil.hxx
+++ b/extensions/spellcheck/hunspell/src/csutil.hxx
@@ -54,38 +54,64 @@
  *
  ******* END LICENSE BLOCK *******/
 
 #ifndef __CSUTILHXX__
 #define __CSUTILHXX__
 
 // First some base level utility routines
 
+#include "w_char.hxx"
+
+// casing
 #define NOCAP   0
 #define INITCAP 1
 #define ALLCAP  2
 #define HUHCAP  3
 #define HUHINITCAP  4
 
-#define FIELD_STEM  "st:"
-#define FIELD_POS   "po:"
-#define FIELD_SUFF  "su:"
-#define FIELD_PREF  "pr:"
-#define FIELD_FREQ  "fr:"
-#define FIELD_PHON  "ph:"
-#define FIELD_HYPH  "hy:"
-#define FIELD_COMP  "co:"
+// default encoding and keystring
+#define SPELL_ENCODING  "ISO8859-1"
+#define SPELL_KEYSTRING "qwertyuiop|asdfghjkl|zxcvbnm" 
+
+// default morphological fields
+#define MORPH_STEM        "st:"
+#define MORPH_ALLOMORPH   "al:"
+#define MORPH_POS         "po:"
+#define MORPH_DERI_PFX    "dp:"
+#define MORPH_INFL_PFX    "ip:"
+#define MORPH_TERM_PFX    "tp:"
+#define MORPH_DERI_SFX    "ds:"
+#define MORPH_INFL_SFX    "is:"
+#define MORPH_TERM_SFX    "ts:"
+#define MORPH_SURF_PFX    "sp:"
+#define MORPH_FREQ        "fr:"
+#define MORPH_PHON        "ph:"
+#define MORPH_HYPH        "hy:"
+#define MORPH_PART        "pa:"
+#define MORPH_FLAG        "fl:"
+#define MORPH_HENTRY      "_H:"
+#define MORPH_TAG_LEN     strlen(MORPH_STEM)
+
+#define MSEP_FLD ' '
+#define MSEP_REC '\n'
+#define MSEP_ALT '\v'
 
 // default flags
-#define ONLYUPCASEFLAG 65535
+#define DEFAULTFLAGS   65510
+#define FORBIDDENWORD  65510
+#define ONLYUPCASEFLAG 65511
 
-typedef struct {
-    unsigned char l;
-    unsigned char h;
-} w_char;
+// hash entry macros
+#define HENTRY_DATA(h) (h->var ? ((h->var & H_OPT_ALIASM) ? \
+    get_stored_pointer(&(h->word) + h->blen + 1) : &(h->word) + h->blen + 1) : NULL)
+// NULL-free version for warning-free OOo build
+#define HENTRY_DATA2(h) (h->var ? ((h->var & H_OPT_ALIASM) ? \
+    get_stored_pointer(&(h->word) + h->blen + 1) : &(h->word) + h->blen + 1) : "")
+#define HENTRY_FIND(h,p) (HENTRY_DATA(h) ? strstr(HENTRY_DATA(h), p) : NULL)
 
 #define w_char_eq(a,b) (((a).l == (b).l) && ((a).h == (b).h))
 
 // convert UTF-16 characters to UTF-8
 char * u16_u8(char * dest, int size, const w_char * src, int srclen);
 
 // convert UTF-8 characters to UTF-16
 int u8_u16(w_char * dest, int size, const char * src);
@@ -97,61 +123,62 @@ void flag_qsort(unsigned short flags[], 
 int flag_bsearch(unsigned short flags[], unsigned short flag, int right);
 
 // remove end of line char(s)
 void   mychomp(char * s);
 
 // duplicate string
 char * mystrdup(const char * s);
 
+// strcat for limited length destination string
+char * mystrcat(char * dest, const char * st, int max);
+
 // duplicate reverse of string
 char * myrevstrdup(const char * s);
 
 // parse into tokens with char delimiter
 char * mystrsep(char ** sptr, const char delim);
 // parse into tokens with char delimiter
 char * mystrsep2(char ** sptr, const char delim);
 
 // parse into tokens with char delimiter
 char * mystrrep(char *, const char *, const char *);
 
 // append s to ends of every lines in text
 void strlinecat(char * lines, const char * s);
 
 // tokenize into lines with new line
-   int line_tok(const char * text, char *** lines);
+   int line_tok(const char * text, char *** lines, char breakchar);
 
 // tokenize into lines with new line and uniq in place
-   char * line_uniq(char * text);
+   char * line_uniq(char * text, char breakchar);
+   char * line_uniq_app(char ** text, char breakchar);
 
-// change \n to c in place
-   char * line_join(char * text, char c);
-
-// leave only last {[^}]*} pattern in string
-   char * delete_zeros(char * morphout);
+// change oldchar to newchar in place
+   char * tr(char * text, char oldc, char newc);
 
 // reverse word
    int reverseword(char *);
 
 // reverse word
    int reverseword_utf(char *);
 
+// remove duplicates
+ int uniqlist(char ** list, int n);
+
+// free character array list
+   void freelist(char *** list, int n);
+
 // character encoding information
 struct cs_info {
   unsigned char ccase;
   unsigned char clower;
   unsigned char cupper;
 };
 
-// two character arrays
-struct replentry {
-  char * pattern;
-  char * pattern2;
-};
-
 // Unicode character encoding information
 struct unicode_info {
   unsigned short c;
   unsigned short cupper;
   unsigned short clower;
 };
 
 struct unicode_info2 {
@@ -220,14 +247,27 @@ int get_captype(char * q, int nl, cs_inf
 int get_captype_utf8(w_char * q, int nl, int langnum);
 
 // strip all ignored characters in the string
 void remove_ignored_chars_utf(char * word, unsigned short ignored_chars[], int ignored_len);
 
 // strip all ignored characters in the string
 void remove_ignored_chars(char * word, char * ignored_chars);
 
-int parse_string(char * line, char ** out, const char * name);
+int parse_string(char * line, char ** out, int ln);
+
+int parse_array(char * line, char ** out, unsigned short ** out_utf16,
+    int * out_utf16_len, int utf8, int ln);
+
+int fieldlen(const char * r);
+char * copy_field(char * dest, const char * morph, const char * var);
 
-int parse_array(char * line, char ** out,
-        unsigned short ** out_utf16, int * out_utf16_len, const char * name, int utf8);
+int morphcmp(const char * s, const char * t);
+
+int get_sfxcount(const char * morph);
+
+// conversion function for protected memory
+void store_pointer(char * dest, char * source);
+
+// conversion function for protected memory
+char * get_stored_pointer(char * s);
 
 #endif
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/dictmgr.cpp
@@ -0,0 +1,219 @@
+/******* BEGIN LICENSE BLOCK *******
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ * 
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ * 
+ * The Initial Developers of the Original Code are Kevin Hendricks (MySpell)
+ * and László Németh (Hunspell). Portions created by the Initial Developers
+ * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved.
+ * 
+ * Contributor(s): László Németh (nemethl@gyorsposta.hu)
+ * 
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ ******* END LICENSE BLOCK *******/
+
+#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 "dictmgr.hxx"
+
+#ifndef MOZILLA_CLIENT
+#ifndef W32
+using namespace std;
+#endif
+#endif
+
+DictMgr::DictMgr(const char * dictpath, const char * etype) 
+{
+  // load list of etype entries
+  numdict = 0;
+  pdentry = (dictentry *)malloc(MAXDICTIONARIES*sizeof(struct dictentry));
+  if (pdentry) {
+     if (parse_file(dictpath, etype)) {
+        numdict = 0;
+        // no dictionary.lst found is okay
+     }
+  } else {
+     numdict = 0;
+  }
+}
+
+
+DictMgr::~DictMgr() 
+{
+  dictentry * pdict = NULL;
+  if (pdentry) {
+     pdict = pdentry;
+     for (int i=0;i<numdict;i++) {
+        if (pdict->lang) {
+            free(pdict->lang);
+            pdict->lang = NULL;
+        }
+        if (pdict->region) {
+            free(pdict->region);
+            pdict->region=NULL;
+        }
+        if (pdict->filename) {
+            free(pdict->filename);
+            pdict->filename = NULL;
+        }
+        pdict++;
+     }
+     free(pdentry);
+     pdentry = NULL;
+     pdict = NULL;
+  }
+  numdict = 0;
+}
+
+
+// read in list of etype entries and build up structure to describe them
+int  DictMgr::parse_file(const char * dictpath, const char * etype)
+{
+
+    int i;
+    char line[MAXDICTENTRYLEN+1];
+    dictentry * pdict = pdentry;
+
+    // open the dictionary list file
+    FILE * dictlst;
+    dictlst = fopen(dictpath,"r");
+    if (!dictlst) {
+      return 1;
+    }
+
+    // step one is to parse the dictionary list building up the 
+    // descriptive structures
+
+    // read in each line ignoring any that dont start with etype
+    while (fgets(line,MAXDICTENTRYLEN,dictlst)) {
+       mychomp(line);
+
+       /* parse in a dictionary entry */
+       if (strncmp(line,etype,4) == 0) {
+          if (numdict < MAXDICTIONARIES) {
+             char * tp = line;
+             char * piece;
+             i = 0;
+             while ((piece=mystrsep(&tp,' '))) {
+                if (*piece != '\0') {
+                    switch(i) {
+                       case 0: break;
+                       case 1: pdict->lang = mystrdup(piece); break;
+                       case 2: if (strcmp (piece, "ANY") == 0)
+                                 pdict->region = mystrdup("");
+                               else
+                                 pdict->region = mystrdup(piece);
+                               break;
+                       case 3: pdict->filename = mystrdup(piece); break;
+                       default: break;
+                    }
+                    i++;
+                }
+                free(piece);
+             }
+             if (i == 4) {
+                 numdict++;
+                 pdict++;
+             } else {
+                 fprintf(stderr,"dictionary list corruption in line \"%s\"\n",line);
+                 fflush(stderr);
+             }
+          }
+       }
+    }
+    fclose(dictlst);
+    return 0;
+}
+
+// return text encoding of dictionary
+int DictMgr::get_list(dictentry ** ppentry)
+{
+  *ppentry = pdentry;
+  return numdict;
+}
+
+
+
+// strip strings into token based on single char delimiter
+// acts like strsep() but only uses a delim char and not 
+// a delim string
+
+char * DictMgr::mystrsep(char ** stringp, const char delim)
+{
+  char * rv = NULL;
+  char * mp = *stringp;
+  int n = strlen(mp);
+  if (n > 0) {
+     char * dp = (char *)memchr(mp,(int)((unsigned char)delim),n);
+     if (dp) {
+        *stringp = dp+1;
+        int nc = (int)((unsigned long)dp - (unsigned long)mp); 
+        rv = (char *) malloc(nc+1);
+        if (rv) {
+           memcpy(rv,mp,nc);
+           *(rv+nc) = '\0';
+           return rv;
+        }
+     } else {
+       rv = (char *) malloc(n+1);
+       if (rv) {
+          memcpy(rv, mp, n);
+          *(rv+n) = '\0';
+          *stringp = mp + n;
+          return rv;
+       }
+     }
+  }
+  return NULL;
+}
+
+
+// replaces strdup with ansi version
+char * DictMgr::mystrdup(const char * s)
+{
+  char * d = NULL;
+  if (s) {
+     int sl = strlen(s);
+     d = (char *) malloc(((sl+1) * sizeof(char)));
+     if (d) memcpy(d,s,((sl+1)*sizeof(char)));
+  }
+  return d;
+}
+
+
+// remove cross-platform text line end characters
+void DictMgr:: mychomp(char * s)
+{
+  int k = strlen(s);
+  if ((k > 0) && ((*(s+k-1)=='\r') || (*(s+k-1)=='\n'))) *(s+k-1) = '\0';
+  if ((k > 1) && (*(s+k-2) == '\r')) *(s+k-2) = '\0';
+}
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/dictmgr.hxx
@@ -0,0 +1,67 @@
+/******* BEGIN LICENSE BLOCK *******
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ * 
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ * 
+ * The Initial Developers of the Original Code are Kevin Hendricks (MySpell)
+ * and László Németh (Hunspell). Portions created by the Initial Developers
+ * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved.
+ * 
+ * Contributor(s): László Németh (nemethl@gyorsposta.hu)
+ * 
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ ******* END LICENSE BLOCK *******/
+
+#ifndef _DICTMGR_HXX_
+#define _DICTMGR_HXX_
+
+#define MAXDICTIONARIES 100
+#define MAXDICTENTRYLEN 1024
+
+struct dictentry {
+  char * filename;
+  char * lang;
+  char * region;
+};
+
+
+class DictMgr
+{
+
+  int                 numdict;
+  dictentry *         pdentry;
+
+public:
+ 
+  DictMgr(const char * dictpath, const char * etype);
+  ~DictMgr();
+  int get_list(dictentry** ppentry);
+            
+private:
+  int  parse_file(const char * dictpath, const char * etype);
+  char * mystrsep(char ** stringp, const char delim);
+  char * mystrdup(const char * s);
+  void mychomp(char * s);
+
+};
+
+#endif
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/filemgr.cpp
@@ -0,0 +1,84 @@
+/******* BEGIN LICENSE BLOCK *******
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ * 
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ * 
+ * The Initial Developers of the Original Code are Kevin Hendricks (MySpell)
+ * and László Németh (Hunspell). Portions created by the Initial Developers
+ * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved.
+ * 
+ * Contributor(s): László Németh (nemethl@gyorsposta.hu)
+ * 
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ ******* END LICENSE BLOCK *******/
+
+#ifndef MOZILLA_CLIENT
+#include <cstdlib>
+#include <cstring>
+#include <cstdio>
+#else
+#include <stdlib.h> 
+#include <string.h>
+#include <stdio.h> 
+#endif
+
+#include "filemgr.hxx"
+
+int FileMgr::fail(const char * err, const char * par) {
+    fprintf(stderr, err, par);
+    return -1;
+}
+
+FileMgr::FileMgr(const char * file, const char * key) {
+    linenum = 0;
+    hin = NULL;
+    fin = fopen(file, "r");
+    if (!fin) {
+        // check hzipped file
+        char * st = (char *) malloc(strlen(file) + strlen(HZIP_EXTENSION));
+        if (st) {
+            strcpy(st, file);
+            strcat(st, HZIP_EXTENSION);
+            hin = new Hunzip(st, key);
+        }
+    }    
+    if (!fin && !hin) fail(MSG_OPEN, file);
+}
+
+FileMgr::~FileMgr()
+{
+    if (fin) fclose(fin);
+    if (hin) delete hin;
+}
+
+char * FileMgr::getline() {
+    const char * l;
+    linenum++;
+    if (fin) return fgets(in, BUFSIZE - 1, fin);
+    if (hin && (l = hin->getline())) return strcpy(in, l);
+    linenum--;
+    return NULL;
+}
+
+int FileMgr::getlinenum() {
+    return linenum;
+}
new file mode 100644
--- /dev/null
+++ b/extensions/spellcheck/hunspell/src/filemgr.hxx
@@ -0,0 +1,54 @@
+/******* BEGIN LICENSE BLOCK *******
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ * 
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ * 
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ * 
+ * The Initial Developers of the Original Code are Kevin Hendricks (MySpell)
+ * and László Németh (Hunspell). Portions created by the Initial Developers
+ * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved.
+ * 
+ * Contributor(s): László Németh (nemethl@gyorsposta.hu)
+ * 
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ ******* END LICENSE BLOCK *******/
+
+#ifndef _FILEMGR_HXX_
+#define _FILEMGR_HXX_
+
+#include "hunzip.hxx"
+
+class FileMgr
+{
+protected:
+    FILE * fin;
+    Hunzip * hin;
+    char in[BUFSIZE + 50]; // input buffer
+    int fail(const char * err, const char * par);
+    int linenum;
+
+public:
+    FileMgr(const char * filename, const char * key = NULL);
+    ~FileMgr();
+    char * getline();
+    int getlinenum();
+};
+#endif
--- a/extensions/spellcheck/hunspell/src/hashmgr.cpp
+++ b/extensions/spellcheck/hunspell/src/hashmgr.cpp
@@ -61,33 +61,33 @@
 #include <cctype>
 #else
 #include <stdlib.h> 
 #include <string.h>
 #include <stdio.h> 
 #include <ctype.h>
 #endif
 
-#include "hashmgr.hxx"
+#include "atypes.hxx"
 #include "csutil.hxx"
-#include "atypes.hxx"
+#include "hashmgr.hxx"
 
 #ifdef MOZILLA_CLIENT
 #ifdef __SUNPRO_CC // for SunONE Studio compiler
 using namespace std;
 #endif
 #else
 #ifndef W32
 using namespace std;
 #endif
 #endif
 
 // build a hash table from a munched word list
 
-HashMgr::HashMgr(const char * tpath, const char * apath)
+HashMgr::HashMgr(const char * tpath, const char * apath, const char * key)
 {
   tablesize = 0;
   tableptr = NULL;
   flag_mode = FLAG_CHAR;
   complexprefixes = 0;
   utf8 = 0;
   langnum = 0;
   lang = NULL;
@@ -95,19 +95,19 @@ HashMgr::HashMgr(const char * tpath, con
   csconv = 0;
   ignorechars = NULL;
   ignorechars_utf16 = NULL;
   ignorechars_utf16_len = 0;
   numaliasf = 0;
   aliasf = NULL;
   numaliasm = 0;
   aliasm = NULL;
-  forbiddenword = FLAG_NULL; // forbidden word signing flag
-  load_config(apath);  
-  int ec = load_tables(tpath);
+  forbiddenword = FORBIDDENWORD; // forbidden word signing flag
+  load_config(apath, key);
+  int ec = load_tables(tpath, key);
   if (ec) {
     /* error condition - what should we do here */
     HUNSPELL_WARNING(stderr, "Hash Manager Error : %d\n",ec);
     if (tableptr) {
       free(tableptr);
       tableptr = NULL;
     }
     tablesize = 0;
@@ -118,30 +118,19 @@ HashMgr::HashMgr(const char * tpath, con
 HashMgr::~HashMgr()
 {
   if (tableptr) {
     // now pass through hash table freeing up everything
     // go through column by column of the table
     for (int i=0; i < tablesize; i++) {
       struct hentry * pt = tableptr[i];
       struct hentry * nt = NULL;
-/*      if (pt) {
-        if (pt->astr && (!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen))) free(pt->astr);
-#ifdef HUNSPELL_EXPERIMENTAL
-        if (pt->description && !aliasm) free(pt->description);
-#endif
-        pt = pt->next;
-      }
-*/
       while(pt) {
         nt = pt->next;
         if (pt->astr && (!aliasf || TESTAFF(pt->astr, ONLYUPCASEFLAG, pt->alen))) free(pt->astr);
-#ifdef HUNSPELL_EXPERIMENTAL
-        if (pt->description && !aliasm) free(pt->description);
-#endif
         free(pt);
         pt = nt;
       }
     }
     free(tableptr);
   }
   tablesize = 0;
 
@@ -188,66 +177,59 @@ struct hentry * HashMgr::lookup(const ch
     return NULL;
 }
 
 // add a word to the hash table (private)
 int HashMgr::add_word(const char * word, int wbl, int wcl, unsigned short * aff,
     int al, const char * desc, bool onlyupcase)
 {
     bool upcasehomonym = false;
-    int descl = (desc) ? strlen(desc) : 0;
+    int descl = desc ? (aliasm ? sizeof(short) : strlen(desc) + 1) : 0;
     // variable-length hash record with word and optional fields
-    // instead of mmap implementation temporarily
     struct hentry* hp = 
-	(struct hentry *) malloc (sizeof(struct hentry) + wbl + descl + 1);
+	(struct hentry *) malloc (sizeof(struct hentry) + wbl + descl);
     if (!hp) return 1;
     char * hpw = &(hp->word);
     strcpy(hpw, word);
-    if (desc && strncmp(desc, FIELD_PHON, strlen(FIELD_PHON)) == 0) {
-	strcpy(hpw + wbl + 1, desc + strlen(FIELD_PHON));
-	hp->var = 1;
-    } else {
-	hp->var = 0;
-    }
     if (ignorechars != NULL) {
       if (utf8) {
         remove_ignored_chars_utf(hpw, ignorechars_utf16, ignorechars_utf16_len);
       } else {
         remove_ignored_chars(hpw, ignorechars);
       }
     }
     if (complexprefixes) {
         if (utf8) reverseword_utf(hpw); else reverseword(hpw);
     }
 
     int i = hash(hpw);
 
-       hp->blen = (unsigned char) wbl;
-       hp->clen = (unsigned char) wcl;
-       hp->alen = (short) al;
-       hp->astr = aff;
-       hp->next = NULL;      
-       hp->next_homonym = NULL;
-#ifdef HUNSPELL_EXPERIMENTAL       
-       if (aliasm) {
-            hp->description = (desc) ? get_aliasm(atoi(desc)) : mystrdup(desc);
-       } else {
-            hp->description = mystrdup(desc);
-            if (desc && !hp->description)
-            {
-                free(hp->astr);
-                free(hp);
-                return 1;
+    hp->blen = (unsigned char) wbl;
+    hp->clen = (unsigned char) wcl;
+    hp->alen = (short) al;
+    hp->astr = aff;
+    hp->next = NULL;      
+    hp->next_homonym = NULL;
+
+    // store the description string or its pointer
+    if (desc) {
+        hp->var = H_OPT;
+        if (aliasm) {
+            hp->var += H_OPT_ALIASM;
+            store_pointer(hpw + wbl + 1, get_aliasm(atoi(desc)));
+        } else {
+	    strcpy(hpw + wbl + 1, desc);
+            if (complexprefixes) {
+                if (utf8) reverseword_utf(HENTRY_DATA(hp));
+                else reverseword(HENTRY_DATA(hp));
             }
-            if (hp->description && complexprefixes) {
-                if (utf8) reverseword_utf(hp->description); else reverseword(hp->description);
-            }
-       }
-#endif
-       
+        }
+	if (strstr(HENTRY_DATA(hp), MORPH_PHON)) hp->var += H_OPT_PHON;
+    } else hp->var = 0;
+
        struct hentry * dp = tableptr[i];
        if (!dp) {
          tableptr[i] = hp;
          return 0;
        }
        while (dp->next != NULL) {
          if ((!dp->next_homonym) && (strcmp(&(hp->word), &(dp->word)) == 0)) {
     	    // remove hidden onlyupcase homonym
@@ -302,69 +284,110 @@ int HashMgr::add_hidden_capitalized_word
     if (((captype == HUHCAP) || (captype == HUHINITCAP) ||
       ((captype == ALLCAP) && (flags != NULL))) &&
       !((flags != NULL) && TESTAFF(flags, forbiddenword, al))) {
           unsigned short * flags2 = (unsigned short *) malloc (sizeof(unsigned short) * (al+1));
 	  if (!flags2) return 1;
           if (al) memcpy(flags2, flags, al * sizeof(unsigned short));
           flags2[al] = ONLYUPCASEFLAG;
           if (utf8) {
-              char st[MAXDELEN];
-              w_char w[MAXDELEN];
-              int wlen = u8_u16(w, MAXDELEN, word);
+              char st[BUFSIZE];
+              w_char w[BUFSIZE];
+              int wlen = u8_u16(w, BUFSIZE, word);
               mkallsmall_utf(w, wlen, langnum);
               mkallcap_utf(w, 1, langnum);
-              u16_u8(st, MAXDELEN, w, wlen);
+              u16_u8(st, BUFSIZE, w, wlen);
               return add_word(st,wbl,wcl,flags2,al+1,dp, true);
            } else {
                mkallsmall(word, csconv);
                mkinitcap(word, csconv);
                return add_word(word,wbl,wcl,flags2,al+1,dp, true);
            }
     }
     return 0;
 }
 
 // detect captype and modify word length for UTF-8 encoding
 int HashMgr::get_clen_and_captype(const char * word, int wbl, int * captype) {
     int len;
     if (utf8) {
-      w_char dest_utf[MAXDELEN];
-      len = u8_u16(dest_utf, MAXDELEN, word);
+      w_char dest_utf[BUFSIZE];
+      len = u8_u16(dest_utf, BUFSIZE, word);
       *captype = get_captype_utf8(dest_utf, len, langnum);
     } else {
       len = wbl;
       *captype = get_captype((char *) word, len, csconv);
     }
     return len;
 }
 
-// add a custom dic. word to the hash table (public)
-int HashMgr::put_word(const char * word, char * aff)
+// remove word (personal dictionary function for standalone applications)
+int HashMgr::remove(const char * word)
 {
-    unsigned short * flags;
-    int al = 0;
-    if (aff) {
-        al = decode_flags(&flags, aff);
-        flag_qsort(flags, 0, al);
-    } else {
-        flags = NULL;
+    struct hentry * dp = lookup(word);
+    while (dp) {
+        if (dp->alen == 0 || !TESTAFF(dp->astr, forbiddenword, dp->alen)) {
+            unsigned short * flags =
+                (unsigned short *) malloc(sizeof(short *) * (dp->alen + 1));
+            if (!flags) return 1;
+            for (int i = 0; i < dp->alen; i++) flags[i] = dp->astr[i];
+            flags[dp->alen] = forbiddenword;
+            dp->astr = flags;
+            dp->alen++;
+            flag_qsort(flags, 0, dp->alen);
+        }
+        dp = dp->next_homonym;
     }
-
-    int captype;
-    int wbl = strlen(word);
-    int wcl = get_clen_and_captype(word, wbl, &captype);
-    add_word(word, wbl, wcl, flags, al, NULL, false);
-    return add_hidden_capitalized_word((char *) word, wbl, wcl, flags, al, NULL, captype);
+    return 0;
 }
 
-int HashMgr::put_word_pattern(const char * word, const char * pattern)
+/* remove forbidden flag to add a personal word to the hash */
+int HashMgr::remove_forbidden_flag(const char * word) {
+    struct hentry * dp = lookup(word);
+    if (!dp) return 1;
+    while (dp) {
+         if (dp->astr && TESTAFF(dp->astr, forbiddenword, dp->alen)) {
+            if (dp->alen == 1) dp->alen = 0; // XXX forbidden words of personal dic.
+            else {
+                unsigned short * flags2 =
+                    (unsigned short *) malloc(sizeof(short *) * (dp->alen - 1));
+                if (!flags2) return 1;
+                int i, j = 0;
+                for (i = 0; i < dp->alen; i++) {
+                    if (dp->astr[i] != forbiddenword) flags2[j++] = dp->astr[i];
+                }
+                dp->alen--;
+                dp->astr = flags2; // XXX allowed forbidden words
+            }
+         }
+         dp = dp->next_homonym;
+       }
+   return 0;
+}
+
+// add a custom dic. word to the hash table (public)
+int HashMgr::add(const char * word)
+{
+    unsigned short * flags = NULL;
+    int al = 0;
+    if (remove_forbidden_flag(word)) {
+        int captype;
+        int wbl = strlen(word);
+        int wcl = get_clen_and_captype(word, wbl, &captype);
+        add_word(word, wbl, wcl, flags, al, NULL, false);
+        return add_hidden_capitalized_word((char *) word, wbl, wcl, flags, al, NULL, captype);
+    }
+    return 0;
+}
+
+int HashMgr::add_with_affix(const char * word, const char * example)
 {
     // detect captype and modify word length for UTF-8 encoding
-    struct hentry * dp = lookup(pattern);
+    struct hentry * dp = lookup(example);
+    remove_forbidden_flag(word);
     if (dp && dp->astr) {
         int captype;
         int wbl = strlen(word);
         int wcl = get_clen_and_captype(word, wbl, &captype);
 	if (aliasf) {
 	    add_word(word, wbl, wcl, dp->astr, dp->alen, NULL, false);	
 	} else {
     	    unsigned short * flags = (unsigned short *) malloc (dp->alen * sizeof(short));
@@ -387,72 +410,86 @@ struct hentry * HashMgr::walk_hashtable(
     if (tableptr[col]) return tableptr[col];
   }
   // null at end and reset to start
   col = -1;
   return NULL;
 }
 
 // load a munched word list and build a hash table on the fly
-int HashMgr::load_tables(const char * tpath)
+int HashMgr::load_tables(const char * tpath, const char * key)
 {
   int al;
   char * ap;
   char * dp;
+  char * dp2;
   unsigned short * flags;
+  char * ts;
 
-  // raw dictionary - munched file
-  FILE * rawdict = fopen(tpath, "r");
-  if (rawdict == NULL) return 1;
+  // open dictionary file
+  FileMgr * dict = new FileMgr(tpath, key);
+  if (dict == NULL) return 1;
 
   // first read the first line of file to get hash table size */
-  char ts[MAXDELEN];
-  if (! fgets(ts, MAXDELEN-1,rawdict)) {
+  if (!(ts = dict->getline())) {
     HUNSPELL_WARNING(stderr, "error: empty dic file\n");
-    fclose(rawdict);
+    delete dict;
     return 2;
   }
   mychomp(ts);
-  
+
   /* remove byte order mark */
   if (strncmp(ts,"\xEF\xBB\xBF",3) == 0) {
     memmove(ts, ts+3, strlen(ts+3)+1);
     HUNSPELL_WARNING(stderr, "warning: dic file begins with byte order mark: possible incompatibility with old Hunspell versions\n");
   }
-  
-  if ((*ts < '1') || (*ts > '9')) HUNSPELL_WARNING(stderr, "error - missing word count in dictionary file\n");
+
   tablesize = atoi(ts);
-  if (!tablesize) {
-    fclose(rawdict);
+  if (tablesize == 0) {
+    HUNSPELL_WARNING(stderr, "error: line 1: missing or bad word count in the dic file\n");
+    delete dict;
     return 4;
   }
   tablesize = tablesize + 5 + USERWORD;
   if ((tablesize %2) == 0) tablesize++;
 
   // allocate the hash table
   tableptr = (struct hentry **) malloc(tablesize * sizeof(struct hentry *));
   if (! tableptr) {
-    fclose(rawdict);
+    delete dict;
     return 3;
   }
   for (int i=0; i<tablesize; i++) tableptr[i] = NULL;
 
   // loop through all words on much list and add to hash
   // table and create word and affix strings
 
-  while (fgets(ts,MAXDELEN-1,rawdict)) {
+  while ((ts = dict->getline())) {
     mychomp(ts);
     // split each line into word and morphological description
-    dp = strchr(ts,'\t');
+    dp = ts;
+    while ((dp = strchr(dp, ':'))) {
+	if ((dp > ts + 3) && (*(dp - 3) == ' ' || *(dp - 3) == '\t')) {
+	    for (dp -= 4; dp >= ts && (*dp == ' ' || *dp == '\t'); dp--);
+	    if (dp < ts) { // missing word
+		dp = NULL;
+	    } else {
+		*(dp + 1) = '\0';
+		dp = dp + 2;
+	    }
+	    break;
+	}
+	dp++;
+    }
 
-    if (dp) {
-      *dp = '\0';
-      dp++;
-    } else {
-      dp = NULL;
+    // tabulator is the old morphological field separator
+    dp2 = strchr(ts, '\t');
+    if (dp2 && (!dp || dp2 < dp)) {
+	*dp2 = '\0';
+	dp = dp2 + 1;
     }
 
     // split each line into word and affix char strings
     // "\/" signs slash in words (not affix separator)
     // "/" at beginning of the line is word character (not affix separator)
     ap = strchr(ts,'/');
     while (ap) {
         if (ap == ts) {
@@ -463,138 +500,147 @@ int HashMgr::load_tables(const char * tp
         for (char * sp = ap - 1; *sp; *sp = *(sp + 1), sp++);
         ap = strchr(ap,'/');
     }
 
     if (ap) {
       *ap = '\0';
       if (aliasf) {
         int index = atoi(ap + 1);
-        al = get_aliasf(index, &flags);
+        al = get_aliasf(index, &flags, dict);
         if (!al) {
-            HUNSPELL_WARNING(stderr, "error - bad flag vector alias: %s\n", ts);
+            HUNSPELL_WARNING(stderr, "error: line %d: bad flag vector alias\n", dict->getlinenum());
             *ap = '\0';
         }
       } else {
-        al = decode_flags(&flags, ap + 1);
+        al = decode_flags(&flags, ap + 1, dict);
         flag_qsort(flags, 0, al);
       }
     } else {
       al = 0;
       ap = NULL;
       flags = NULL;
     }
 
     int captype;
     int wbl = strlen(ts);
     int wcl = get_clen_and_captype(ts, wbl, &captype);
     // add the word and its index plus its capitalized form optionally
     if (add_word(ts,wbl,wcl,flags,al,dp, false) ||
 	add_hidden_capitalized_word(ts, wbl, wcl, flags, al, dp, captype)) {
-	fclose(rawdict);
+	delete dict;
 	return 5;
     }
   }
 
-  fclose(rawdict);
+  delete dict;
   return 0;
 }
 
-
 // the hash function is a simple load and rotate
 // algorithm borrowed
 
 int HashMgr::hash(const char * word) const
 {
     long  hv = 0;
     for (int i=0; i < 4  &&  *word != 0; i++)
         hv = (hv << 8) | (*word++);
     while (*word != 0) {
       ROTATE(hv,ROTATE_LEN);
       hv ^= (*word++);
     }
     return (unsigned long) hv % tablesize;
 }
 
-int HashMgr::decode_flags(unsigned short ** result, char * flags) {
+int HashMgr::decode_flags(unsigned short ** result, char * flags, FileMgr * af) {
     int len;
     switch (flag_mode) {
       case FLAG_LONG: { // two-character flags (1x2yZz -> 1x 2y Zz)
         len = strlen(flags);
-        if (len%2 == 1) HUNSPELL_WARNING(stderr, "error: length of FLAG_LONG flagvector is odd: %s\n", flags);
+        if (len%2 == 1) HUNSPELL_WARNING(stderr, "error: line %d: bad flagvector\n", af->getlinenum());
         len /= 2;
         *result = (unsigned short *) malloc(len * sizeof(short));
         if (!*result) return -1;
         for (int i = 0; i < len; i++) {
             (*result)[i] = (((unsigned short) flags[i * 2]) << 8) + (unsigned short) flags[i * 2 + 1]; 
         }
         break;
       }
       case FLAG_NUM: { // decimal numbers separated by comma (4521,23,233 -> 4521 23 233)
+        int i;
         len = 1;
         char * src = flags; 
         unsigned short * dest;
         char * p;
         for (p = flags; *p; p++) {
           if (*p == ',') len++;
         }
         *result = (unsigned short *) malloc(len * sizeof(short));
         if (!*result) return -1;
         dest = *result;
         for (p = flags; *p; p++) {
           if (*p == ',') {
-            *dest = (unsigned short) atoi(src);
-            if (*dest == 0) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n");
+            i = atoi(src);
+            if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n",
+              af->getlinenum(), i, DEFAULTFLAGS - 1);
+            *dest = (unsigned short) i;
+            if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum());
             src = p + 1;
             dest++;
           }
         }
-        *dest = (unsigned short) atoi(src);
-        if (*dest == 0) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n");
+        i = atoi(src);
+        if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: line %d: flag id %d is too large (max: %d)\n",
+          af->getlinenum(), i, DEFAULTFLAGS - 1);
+        *dest = (unsigned short) i;
+        if (*dest == 0) HUNSPELL_WARNING(stderr, "error: line %d: 0 is wrong flag id\n", af->getlinenum());
         break;
       }    
       case FLAG_UNI: { // UTF-8 characters
-        w_char w[MAXDELEN/2];
-        len = u8_u16(w, MAXDELEN/2, flags);
+        w_char w[BUFSIZE/2];
+        len = u8_u16(w, BUFSIZE/2, flags);
         *result = (unsigned short *) malloc(len * sizeof(short));
         if (!*result) return -1;
         memcpy(*result, w, len * sizeof(short));
         break;
       }
       default: { // Ispell's one-character flags (erfg -> e r f g)
         unsigned short * dest;
         len = strlen(flags);
         *result = (unsigned short *) malloc(len * sizeof(short));
         if (!*result) return -1;
         dest = *result;
         for (unsigned char * p = (unsigned char *) flags; *p; p++) {
           *dest = (unsigned short) *p;
           dest++;
         }
       }
-    }      
+    }
     return len;
 }
 
 unsigned short HashMgr::decode_flag(const char * f) {
     unsigned short s = 0;
+    int i;
     switch (flag_mode) {
       case FLAG_LONG:
         s = ((unsigned short) f[0] << 8) + (unsigned short) f[1];
         break;
       case FLAG_NUM:
-        s = (unsigned short) atoi(f);
+        i = atoi(f);
+        if (i >= DEFAULTFLAGS) HUNSPELL_WARNING(stderr, "error: flag id %d is too large (max: %d)\n", i, DEFAULTFLAGS - 1);
+        s = (unsigned short) i;
         break;
       case FLAG_UNI:
         u8_u16((w_char *) &s, 1, f);
         break;
       default:
         s = (unsigned short) *((unsigned char *)f);
     }
-    if (!s) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n");
+    if (s == 0) HUNSPELL_WARNING(stderr, "error: 0 is wrong flag id\n");
     return s;
 }
 
 char * HashMgr::encode_flag(unsigned short f) {
     unsigned char ch[10];
     if (f==0) return mystrdup("(NULL)");
     if (flag_mode == FLAG_LONG) {
         ch[0] = (unsigned char) (f >> 8);
@@ -607,122 +653,119 @@ char * HashMgr::encode_flag(unsigned sho
     } else {
         ch[0] = (unsigned char) (f);
         ch[1] = '\0';
     }
     return mystrdup((char *) ch);
 }
 
 // read in aff file and set flag mode
-int  HashMgr::load_config(const char * affpath)
+int  HashMgr::load_config(const char * affpath, const char * key)
 {
+  char * line; // io buffers
   int firstline = 1;
-  
-  // io buffers
-  char line[MAXDELEN+1];
  
   // open the affix file
-  FILE * afflst;
-  afflst = fopen(affpath,"r");
+  FileMgr * afflst = new FileMgr(affpath, key);
   if (!afflst) {
     HUNSPELL_WARNING(stderr, "Error - could not open affix description file %s\n",affpath);
     return 1;
   }
 
     // read in each line ignoring any that do not
     // start with a known line type indicator
 
-    while (fgets(line,MAXDELEN,afflst)) {
+    while ((line = afflst->getline())) {
         mychomp(line);
 
        /* remove byte order mark */
        if (firstline) {
          firstline = 0;
          if (strncmp(line,"\xEF\xBB\xBF",3) == 0) memmove(line, line+3, strlen(line+3)+1);
        }
 
         /* parse in the try string */
         if ((strncmp(line,"FLAG",4) == 0) && isspace(line[4])) {
             if (flag_mode != FLAG_CHAR) {
-                HUNSPELL_WARNING(stderr, "error: duplicate FLAG parameter\n");
+                HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of the FLAG affix file parameter\n", afflst->getlinenum());
             }
             if (strstr(line, "long")) flag_mode = FLAG_LONG;
             if (strstr(line, "num")) flag_mode = FLAG_NUM;
             if (strstr(line, "UTF-8")) flag_mode = FLAG_UNI;
             if (flag_mode == FLAG_CHAR) {
-                HUNSPELL_WARNING(stderr, "error: FLAG need `num', `long' or `UTF-8' parameter: %s\n", line);
+                HUNSPELL_WARNING(stderr, "error: line %d: FLAG needs `num', `long' or `UTF-8' parameter\n", afflst->getlinenum());
             }
         }
         if (strncmp(line,"FORBIDDENWORD",13) == 0) {
           char * st = NULL;
-          if (parse_string(line, &st, "FORBIDDENWORD")) {
-             fclose(afflst);
+          if (parse_string(line, &st, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
           forbiddenword = decode_flag(st);
           free(st);
         }
         if (strncmp(line, "SET", 3) == 0) {
-    	  if (parse_string(line, &enc, "SET")) {
-             fclose(afflst);
+    	  if (parse_string(line, &enc, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }    	    
     	  if (strcmp(enc, "UTF-8") == 0) {
     	    utf8 = 1;
 #ifndef OPENOFFICEORG
 #ifndef MOZILLA_CLIENT
     	    initialize_utf_tbl();
 #endif
 #endif
     	  } else csconv = get_current_cs(enc);
     	}
-        if (strncmp(line, "LANG", 4) == 0) {    	
-    	  if (parse_string(line, &lang, "LANG")) {
-             fclose(afflst);
+        if (strncmp(line, "LANG", 4) == 0) {
+    	  if (parse_string(line, &lang, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }    	    
     	  langnum = get_lang_num(lang);
     	}
 
        /* parse in the ignored characters (for example, Arabic optional diacritics characters */
        if (strncmp(line,"IGNORE",6) == 0) {
-          if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, "IGNORE", utf8)) {
-             fclose(afflst);
+          if (parse_array(line, &ignorechars, &ignorechars_utf16,
+                 &ignorechars_utf16_len, utf8, afflst->getlinenum())) {
+             delete afflst;
              return 1;
           }
        }
 
        if ((strncmp(line,"AF",2) == 0) && isspace(line[2])) {
           if (parse_aliasf(line, afflst)) {
-             fclose(afflst);
+             delete afflst;
+             return 1;
+          }
+       }
+
+       if ((strncmp(line,"AM",2) == 0) && isspace(line[2])) {
+          if (parse_aliasm(line, afflst)) {
+             delete afflst;
              return 1;
           }
        }
 
-#ifdef HUNSPELL_EXPERIMENTAL
-       if ((strncmp(line,"AM",2) == 0) && isspace(line[2])) {
-          if (parse_aliasm(line, afflst)) {
-             fclose(afflst);
-             return 1;
-          }
-       }
-#endif
-        if (strncmp(line,"COMPLEXPREFIXES",15) == 0) complexprefixes = 1;
-        if (((strncmp(line,"SFX",3) == 0) || (strncmp(line,"PFX",3) == 0)) && isspace(line[3])) break;
+       if (strncmp(line,"COMPLEXPREFIXES",15) == 0) complexprefixes = 1;
+       if (((strncmp(line,"SFX",3) == 0) || (strncmp(line,"PFX",3) == 0)) && isspace(line[3])) break;
     }
-    if (csconv == NULL) csconv = get_current_cs("ISO8859-1");
-    fclose(afflst);
+    if (csconv == NULL) csconv = get_current_cs(SPELL_ENCODING);
+    delete afflst;
     return 0;
 }
 
 /* parse in the ALIAS table */
-int  HashMgr::parse_aliasf(char * line, FILE * af)
+int  HashMgr::parse_aliasf(char * line, FileMgr * af)
 {
    if (numaliasf != 0) {
-      HUNSPELL_WARNING(stderr, "error: duplicate AF (alias for flag vector) tables used\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
       return 1;
    }
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    piece = mystrsep(&tp, 0);
    while (piece) {
@@ -730,18 +773,17 @@ int  HashMgr::parse_aliasf(char * line, 
           switch(i) {
              case 0: { np++; break; }
              case 1: { 
                        numaliasf = atoi(piece);
                        if (numaliasf < 1) {
                           numaliasf = 0;
                           aliasf = NULL;
                           aliasflen = NULL;
-                          HUNSPELL_WARNING(stderr, "incorrect number of entries in AF table\n");
-                          free(piece);
+                          HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
                           return 1;
                        }
                        aliasf = (unsigned short **) malloc(numaliasf * sizeof(unsigned short *));
                        aliasflen = (unsigned short *) malloc(numaliasf * sizeof(short));
                        if (!aliasf || !aliasflen) {
                           numaliasf = 0;
                           if (aliasf) free(aliasf);
                           if (aliasflen) free(aliasflen);
@@ -751,191 +793,193 @@ int  HashMgr::parse_aliasf(char * line, 
                        }
                        np++;
                        break;
                      }
              default: break;
           }
           i++;
        }
-       free(piece);
        piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
       numaliasf = 0;
       free(aliasf);
       free(aliasflen);
       aliasf = NULL;
       aliasflen = NULL;
-      HUNSPELL_WARNING(stderr, "error: missing AF table information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
       return 1;
    } 
  
    /* now parse the numaliasf lines to read in the remainder of the table */
-   char * nl = line;
+   char * nl;
    for (int j=0; j < numaliasf; j++) {
-        if (!fgets(nl,MAXDELEN,af)) return 1;
+        if (!(nl = af->getline())) return 1;
         mychomp(nl);
         tp = nl;
         i = 0;
         aliasf[j] = NULL;
         aliasflen[j] = 0;
         piece = mystrsep(&tp, 0);
         while (piece) {
            if (*piece != '\0') {
                switch(i) {
                   case 0: {
                              if (strncmp(piece,"AF",2) != 0) {
                                  numaliasf = 0;
                                  free(aliasf);
                                  free(aliasflen);
                                  aliasf = NULL;
                                  aliasflen = NULL;
-                                 HUNSPELL_WARNING(stderr, "error: AF table is corrupt\n");
-                                 free(piece);
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
                                  return 1;
                              }
                              break;
                           }
                   case 1: {
-                            aliasflen[j] = (unsigned short) decode_flags(&(aliasf[j]), piece);
+                            aliasflen[j] = (unsigned short) decode_flags(&(aliasf[j]), piece, af);
                             flag_qsort(aliasf[j], 0, aliasflen[j]);
                             break; 
                           }
                   default: break;
                }
                i++;
            }
-           free(piece);
            piece = mystrsep(&tp, 0);
         }
         if (!aliasf[j]) {
              free(aliasf);
              free(aliasflen);
              aliasf = NULL;
              aliasflen = NULL;
              numaliasf = 0;
-             HUNSPELL_WARNING(stderr, "error: AF table is corrupt\n");
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
              return 1;
         }
    }
    return 0;
 }
 
 int HashMgr::is_aliasf() {
     return (aliasf != NULL);
 }
 
-int HashMgr::get_aliasf(int index, unsigned short ** fvec) {
+int HashMgr::get_aliasf(int index, unsigned short ** fvec, FileMgr * af) {
     if ((index > 0) && (index <= numaliasf)) {
         *fvec = aliasf[index - 1];
         return aliasflen[index - 1];
     }
-    HUNSPELL_WARNING(stderr, "error: bad flag alias index: %d\n", index);
+    HUNSPELL_WARNING(stderr, "error: line %d: bad flag alias index: %d\n", af->getlinenum(), index);
     *fvec = NULL;
     return 0;
 }
 
-#ifdef HUNSPELL_EXPERIMENTAL
 /* parse morph alias definitions */
-int  HashMgr::parse_aliasm(char * line, FILE * af)
+int  HashMgr::parse_aliasm(char * line, FileMgr * af)
 {
    if (numaliasm != 0) {
-      HUNSPELL_WARNING(stderr, "error: duplicate AM (aliases for morphological descriptions) tables used\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum());
       return 1;
    }
    char * tp = line;
    char * piece;
    int i = 0;
    int np = 0;
    piece = mystrsep(&tp, 0);
    while (piece) {
        if (*piece != '\0') {
           switch(i) {
              case 0: { np++; break; }
              case 1: { 
                        numaliasm = atoi(piece);
                        if (numaliasm < 1) {
-                          HUNSPELL_WARNING(stderr, "incorrect number of entries in AM table\n");
-                          free(piece);
+                          HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum());
                           return 1;
                        }
                        aliasm = (char **) malloc(numaliasm * sizeof(char *));
                        if (!aliasm) {
                           numaliasm = 0;
                           return 1;
                        }
                        np++;
                        break;
                      }
              default: break;
           }
           i++;
        }
-       free(piece);
        piece = mystrsep(&tp, 0);
    }
    if (np != 2) {
       numaliasm = 0;
       free(aliasm);
       aliasm = NULL;
-      HUNSPELL_WARNING(stderr, "error: missing AM alias information\n");
+      HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum());
       return 1;
    } 
 
    /* now parse the numaliasm lines to read in the remainder of the table */
    char * nl = line;
    for (int j=0; j < numaliasm; j++) {
-        if (!fgets(nl,MAXDELEN,af)) return 1;
+        if (!(nl = af->getline())) return 1;
         mychomp(nl);
         tp = nl;
         i = 0;
         aliasm[j] = NULL;
-        piece = mystrsep(&tp, 0);
+        piece = mystrsep(&tp, ' ');
         while (piece) {
            if (*piece != '\0') {
                switch(i) {
                   case 0: {
                              if (strncmp(piece,"AM",2) != 0) {
-                                 HUNSPELL_WARNING(stderr, "error: AM table is corrupt\n");
-                                 free(piece);
+                                 HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
                                  numaliasm = 0;
                                  free(aliasm);
                                  aliasm = NULL;
                                  return 1;
                              }
                              break;
                           }
                   case 1: {
+                            // add the remaining of the line
+                            if (*tp) {
+                                *(tp - 1) = ' ';
+                                tp = tp + strlen(tp);
+                            }
                             if (complexprefixes) {
                                 if (utf8) reverseword_utf(piece);
                                     else reverseword(piece);
                             }
                             aliasm[j] = mystrdup(piece);
+                            if (!aliasm[j]) {
+                                 numaliasm = 0;
+                                 free(aliasm);
+                                 aliasm = NULL;
+                                 return 1;
+                            }
                             break; }
                   default: break;
                }
                i++;
            }
-           free(piece);
-           piece = mystrsep(&tp, 0);
+           piece = mystrsep(&tp, ' ');
         }
         if (!aliasm[j]) {
              numaliasm = 0;
              free(aliasm);
              aliasm = NULL;
-             HUNSPELL_WARNING(stderr, "error: map table is corrupt\n");
+             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum());
              return 1;
         }
    }
    return 0;
 }
 
 int HashMgr::is_aliasm() {
     return (aliasm != NULL);
 }
 
 char * HashMgr::get_aliasm(int index) {
     if ((index > 0) && (index <= numaliasm)) return aliasm[index - 1];
     HUNSPELL_WARNING(stderr, "error: bad morph. alias index: %d\n", index);
     return NULL;
 }
-#endif
--- a/extensions/spellcheck/hunspell/src/hashmgr.hxx
+++ b/extensions/spellcheck/hunspell/src/hashmgr.hxx
@@ -58,72 +58,70 @@
 #define _HASHMGR_HXX_
 
 #ifndef MOZILLA_CLIENT
 #include <cstdio>
 #else
 #include <stdio.h>
 #endif
 
+#include "filemgr.hxx"
 #include "htypes.hxx"
 
 enum flag { FLAG_CHAR, FLAG_LONG, FLAG_NUM, FLAG_UNI };
 
 class HashMgr
 {
-  int             tablesize;
-  struct hentry ** tableptr;
-  int             userword;
-  flag            flag_mode;
-  int             complexprefixes;
-  int             utf8;
-  unsigned short  forbiddenword;
-  int 		  langnum;
-  char *          enc;
-  char *          lang;
-  struct cs_info * csconv;
-  char *          ignorechars;
-  unsigned short * ignorechars_utf16;
-  int             ignorechars_utf16_len;
-  int                 numaliasf; // flag vector `compression' with aliases
-  unsigned short **   aliasf;
-  unsigned short *    aliasflen;
-  int                 numaliasm; // morphological desciption `compression' with aliases
-  char **             aliasm;
+  int               tablesize;
+  struct hentry **  tableptr;
+  int               userword;
+  flag              flag_mode;
+  int               complexprefixes;
+  int               utf8;
+  unsigned short    forbiddenword;
+  int               langnum;
+  char *            enc;
+  char *            lang;
+  struct cs_info *  csconv;
+  char *            ignorechars;
+  unsigned short *  ignorechars_utf16;
+  int               ignorechars_utf16_len;
+  int               numaliasf; // flag vector `compression' with aliases
+  unsigned short ** aliasf;
+  unsigned short *  aliasflen;
+  int               numaliasm; // morphological desciption `compression' with aliases
+  char **           aliasm;
 
 
 public:
-  HashMgr(const char * tpath, const char * apath);
+  HashMgr(const char * tpath, const char * apath, const char * key = NULL);
   ~HashMgr();
 
   struct hentry * lookup(const char *) const;
   int hash(const char *) const;
   struct hentry * walk_hashtable(int & col, struct hentry * hp) const;
 
-  int put_word(const char * word, char * ap);
-  int put_word_pattern(const char * word, const char * pattern);
-  int decode_flags(unsigned short ** result, char * flags);
+  int add(const char * word);
+  int add_with_affix(const char * word, const char * pattern);
+  int remove(const char * word);
+  int decode_flags(unsigned short ** result, char * flags, FileMgr * af);
   unsigned short        decode_flag(const char * flag);
   char *                encode_flag(unsigned short flag);
   int is_aliasf();
-  int get_aliasf(int index, unsigned short ** fvec);
-#ifdef HUNSPELL_EXPERIMENTAL
+  int get_aliasf(int index, unsigned short ** fvec, FileMgr * af);
   int is_aliasm();
   char * get_aliasm(int index);
-#endif
 
-  
 private:
   int get_clen_and_captype(const char * word, int wbl, int * captype);
-  int load_tables(const char * tpath);
+  int load_tables(const char * tpath, const char * key);
   int add_word(const char * word, int wbl, int wcl, unsigned short * ap,
     int al, const char * desc, bool onlyupcase);
-  int load_config(const char * affpath);
-  int parse_aliasf(char * line, FILE * af);
+  int load_config(const char * affpath, const char * key);
+  int parse_aliasf(char * line, FileMgr * af);
   int add_hidden_capitalized_word(char * word, int wbl, int wcl,
     unsigned short * flags, int al, char * dp, int captype);
-#ifdef HUNSPELL_EXPERIMENTAL
-  int parse_aliasm(char * line, FILE * af);
-#endif
+  int parse_aliasm(char * line, FileMgr * af);
+  int remove_forbidden_flag(const char * word);
 
 };
 
 #endif
--- a/extensions/spellcheck/hunspell/src/htypes.hxx
+++ b/extensions/spellcheck/hunspell/src/htypes.hxx
@@ -52,34 +52,37 @@
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  ******* END LICENSE BLOCK *******/
 
 #ifndef _HTYPES_HXX_
 #define _HTYPES_HXX_
 
-#define MAXDELEN    8192
-
 #define ROTATE_LEN   5
 
 #define ROTATE(v,q) \
    (v) = ((v) << (q)) | (((v) >> (32 - q)) & ((1 << (q))-1));
 
+// hentry options
+#define H_OPT        (1 << 0)
+#define H_OPT_ALIASM (1 << 1)
+#define H_OPT_PHON   (1 << 2)
+
+// see also csutil.hxx
+#define HENTRY_WORD(h) &(h->word)
+
 // approx. number  of user defined words
 #define USERWORD 1000
 
 struct hentry
 {
   unsigned char blen; // word length in bytes
   unsigned char clen; // word length in characters (different for UTF-8 enc.)
   short    alen;      // length of affix flag vector
   unsigned short * astr;  // affix flag vector
   struct   hentry * next; // next word with same hash code
   struct   hentry * next_homonym; // next homonym word (with same hash code)
-#ifdef HUNSPELL_EXPERIMENTAL
-  char *   description; // morphological data (optional)
-#endif
   char     var;       // variable fields (only for special pronounciation yet)
   char     word;      // variable-length word (8-bit or UTF-8 encoding)
 };
 
 #endif
--- a/extensions/spellcheck/hunspell/src/hunspell.cpp
+++ b/extensions/spellcheck/hunspell/src/hunspell.cpp
@@ -54,108 +54,120 @@
  *
  ******* END LICENSE BLOCK *******/
 
 #ifndef MOZILLA_CLIENT
 #include <cstdlib>
 #include <cstring>
 #include <cstdio>
 #else
-#include <stdlib.h> 
+#include <stdlib.h>
 #include <string.h>
-#include <stdio.h> 
+#include <stdio.h>
 #endif
 
+#include "csutil.hxx"
+#include "hunspell.h"
 #include "hunspell.hxx"
-#include "hunspell.h"
 
 #ifndef MOZILLA_CLIENT
 #ifndef W32
 using namespace std;
 #endif
 #endif
 
-Hunspell::Hunspell(const char * affpath, const char * dpath)
+Hunspell::Hunspell(const char * affpath, const char * dpath, const char * key)
 {
     encoding = NULL;
     csconv = NULL;
     utf8 = 0;
     complexprefixes = 0;
+    affixpath = mystrdup(affpath);
+    maxdic = 0;
 
     /* first set up the hash manager */
-    pHMgr = new HashMgr(dpath, affpath);
+    pHMgr[0] = new HashMgr(dpath, affpath, key);
+    if (pHMgr[0]) maxdic = 1;
 
     /* next set up the affix manager */
     /* it needs access to the hash manager lookup methods */
-    pAMgr = new AffixMgr(affpath,pHMgr);
+    pAMgr = new AffixMgr(affpath, pHMgr, &maxdic, key);
 
     /* get the preferred try string and the dictionary */
     /* encoding from the Affix Manager for that dictionary */
     char * try_string = pAMgr->get_try_string();
     encoding = pAMgr->get_encoding();
     csconv = get_current_cs(encoding);
     langnum = pAMgr->get_langnum();
     utf8 = pAMgr->get_utf8();
     complexprefixes = pAMgr->get_complexprefixes();
     wordbreak = pAMgr->get_breaktable();
 
     /* and finally set up the suggestion manager */
     pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr);
     if (try_string) free(try_string);
-
 }
 
 Hunspell::~Hunspell()
 {
     if (pSMgr) delete pSMgr;
     if (pAMgr) delete pAMgr;
-    if (pHMgr) delete pHMgr;
+    for (int i = 0; i < maxdic; i++) delete pHMgr[i];
+    maxdic = 0;
     pSMgr = NULL;
     pAMgr = NULL;
-    pHMgr = NULL;
 #ifdef MOZILLA_CLIENT
     free(csconv);
 #endif
     csconv= NULL;
     if (encoding) free(encoding);
     encoding = NULL;
+    if (affixpath) free(affixpath);
+    affixpath = NULL;
 }
 
+// load extra dictionaries
+int Hunspell::add_dic(const char * dpath, const char * key) {
+    if (maxdic == MAXDIC || !affixpath) return 1;
+    pHMgr[maxdic] = new HashMgr(dpath, affixpath, key);
+    if (pHMgr[maxdic]) maxdic++; else return 1;
+    return 0;
+}
 
 // make a copy of src at destination while removing all leading
 // blanks and removing any trailing periods after recording
 // their presence with the abbreviation flag
-// also since already going through character by character, 
+// also since already going through character by character,
 // set the capitalization type
 // return the length of the "cleaned" (and UTF-8 encoded) word
 
-int Hunspell::cleanword2(char * dest, const char * src, 
+int Hunspell::cleanword2(char * dest, const char * src,
     w_char * dest_utf, int * nc, int * pcaptype, int * pabbrev)
-{ 
+{
    unsigned char * p = (unsigned char *) dest;
    const unsigned char * q = (const unsigned char * ) src;
 
    // first skip over any leading blanks
    while ((*q != '\0') && (*q == ' ')) q++;
-   
+
    // now strip off any trailing periods (recording their presence)
    *pabbrev = 0;
    int nl = strlen((const char *)q);
    while ((nl > 0) && (*(q+nl-1)=='.')) {
        nl--;
        (*pabbrev)++;
    }
-   
+
    // if no characters are left it can't be capitalized
-   if (nl <= 0) { 
+   if (nl <= 0) {
        *pcaptype = NOCAP;
        *p = '\0';
        return 0;
    }
-   
+
    strncpy(dest, (char *) q, nl);
    *(dest + nl) = '\0';
    nl = strlen(dest);
    if (utf8) {
       *nc = u8_u16(dest_utf, MAXWORDLEN, dest);
       // don't check too long words
       if (*nc >= MAXWORDLEN) return 0;
       if (*nc == -1) { // big Unicode character (non BMP area)
@@ -163,39 +175,38 @@ int Hunspell::cleanword2(char * dest, co
          return nl;
       }
      *pcaptype = get_captype_utf8(dest_utf, *nc, langnum);
    } else {
      *pcaptype = get_captype(dest, nl, csconv);
      *nc = nl;
    }
    return nl;
-} 
+}
 
-#ifdef HUNSPELL_EXPERIMENTAL
-int Hunspell::cleanword(char * dest, const char * src, 
+int Hunspell::cleanword(char * dest, const char * src,
     int * pcaptype, int * pabbrev)
-{ 
+{
    unsigned char * p = (unsigned char *) dest;
    const unsigned char * q = (const unsigned char * ) src;
    int firstcap = 0;
 
    // first skip over any leading blanks
    while ((*q != '\0') && (*q == ' ')) q++;
-   
+
    // now strip off any trailing periods (recording their presence)
    *pabbrev = 0;
    int nl = strlen((const char *)q);
    while ((nl > 0) && (*(q+nl-1)=='.')) {
        nl--;
        (*pabbrev)++;
    }
-   
+
    // if no characters are left it can't be capitalized
-   if (nl <= 0) { 
+   if (nl <= 0) {
        *pcaptype = NOCAP;
        *p = '\0';
        return 0;
    }
 
    // now determine the capitalization type of the first nl letters
    int ncap = 0;
    int nneutral = 0;
@@ -237,35 +248,34 @@ int Hunspell::cleanword(char * dest, con
    } else if ((ncap == nc) || ((ncap + nneutral) == nc)){
         *pcaptype = ALLCAP;
    } else if ((ncap > 1) && firstcap) {
         *pcaptype = HUHINITCAP;
    } else {
         *pcaptype = HUHCAP;
    }
    return strlen(dest);
-} 
-#endif       
+}
 
 void Hunspell::mkallcap(char * p)
 {
   if (utf8) {
       w_char u[MAXWORDLEN];
       int nc = u8_u16(u, MAXWORDLEN, p);
       unsigned short idx;
       for (int i = 0; i < nc; i++) {
          idx = (u[i].h << 8) + u[i].l;
          if (idx != unicodetoupper(idx, langnum)) {
             u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);
             u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);
          }
       }
       u16_u8(p, MAXWORDUTF8LEN, u, nc);
   } else {
-    while (*p != '\0') { 
+    while (*p != '\0') {
         *p = csconv[((unsigned char) *p)].cupper;
         p++;
     }
   }
 }
 
 int Hunspell::mkallcap2(char * p, w_char * u, int nc)
 {
@@ -275,30 +285,30 @@ int Hunspell::mkallcap2(char * p, w_char
          idx = (u[i].h << 8) + u[i].l;
          unsigned short up = unicodetoupper(idx, langnum);
          if (idx != up) {
             u[i].h = (unsigned char) (up >> 8);
             u[i].l = (unsigned char) (up & 0x00FF);
          }
       }
       u16_u8(p, MAXWORDUTF8LEN, u, nc);
-      return strlen(p);  
+      return strlen(p);
   } else {
-    while (*p != '\0') { 
+    while (*p != '\0') {
         *p = csconv[((unsigned char) *p)].cupper;
         p++;
     }
   }
   return nc;
 }
 
 
 void Hunspell::mkallsmall(char * p)
 {
-    while (*p != '\0') { 
+    while (*p != '\0') {
         *p = csconv[((unsigned char) *p)].clower;
         p++;
     }
 }
 
 int Hunspell::mkallsmall2(char * p, w_char * u, int nc)
 {
   if (utf8) {
@@ -309,17 +319,17 @@ int Hunspell::mkallsmall2(char * p, w_ch
          if (idx != low) {
             u[i].h = (unsigned char) (low >> 8);
             u[i].l = (unsigned char) (low & 0x00FF);
          }
       }
       u16_u8(p, MAXWORDUTF8LEN, u, nc);
       return strlen(p);
   } else {
-    while (*p != '\0') { 
+    while (*p != '\0') {
         *p = csconv[((unsigned char) *p)].clower;
         p++;
     }
   }
   return nc;
 }
 
 // convert UTF-8 sharp S codes to latin 1
@@ -353,77 +363,79 @@ hentry * Hunspell::spellsharps(char * ba
     return NULL;
 }
 
 int Hunspell::is_keepcase(const hentry * rv) {
     return pAMgr && rv->astr && pAMgr->get_keepcase() &&
         TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen);
 }
 
-/* insert a word to beginning of the suggestion array and return ns */
+/* insert a word to the beginning of the suggestion array and return ns */
 int Hunspell::insert_sug(char ***slst, char * word, int ns) {
+    char * dup = mystrdup(word);
+    if (!dup) return ns;
     if (ns == MAXSUGGESTION) {
         ns--;
         free((*slst)[ns]);
     }
     for (int k = ns; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
-    (*slst)[0] = mystrdup(word);
+    (*slst)[0] = dup;
     return ns + 1;
 }
 
 int Hunspell::spell(const char * word, int * info, char ** root)
 {
   struct hentry * rv=NULL;
   // need larger vector. For example, Turkish capital letter I converted a
   // 2-byte UTF-8 character (dotless i) by mkallsmall.
   char cw[MAXWORDUTF8LEN];
   char wspace[MAXWORDUTF8LEN];
   w_char unicw[MAXWORDLEN];
+  // Hunspell supports XML input of the simplified API (see manual)
+  if (strcmp(word, SPELL_XML) == 0) return 1;
   int nc = strlen(word);
   int wl2 = 0;
   if (utf8) {
     if (nc >= MAXWORDUTF8LEN) return 0;
   } else {
     if (nc >= MAXWORDLEN) return 0;
   }
   int captype = 0;
   int abbv = 0;
-  int wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+  int wl = 0;
+
+  // input conversion
+  RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+  if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+  else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
   int info2 = 0;
-  if (wl == 0) return 1;
+  if (wl == 0 || maxdic == 0) return 1;
   if (root) *root = NULL;
 
-  // allow numbers with dots and commas (but forbid double separators: "..", ",," etc.)
+  // allow numbers with dots, dashes and commas (but forbid double separators: "..", "--" etc.)
   enum { NBEGIN, NNUM, NSEP };
   int nstate = NBEGIN;
   int i;
 
   for (i = 0; (i < wl); i++) {
     if ((cw[i] <= '9') && (cw[i] >= '0')) {
         nstate = NNUM;
     } else if ((cw[i] == ',') || (cw[i] == '.') || (cw[i] == '-')) {
         if ((nstate == NSEP) || (i == 0)) break;
         nstate = NSEP;
     } else break;
   }
   if ((i == wl) && (nstate == NNUM)) return 1;
   if (!info) info = &info2; else *info = 0;
 
-  // LANG_hu section: number(s) + (percent or degree) with suffixes
-  if (langnum == LANG_hu) {
-    if ((nstate == NNUM) && ((cw[i] == '%') || ((!utf8 && (cw[i] == '\xB0')) ||
-        (utf8 && (strncmp(cw + i, "\xC2\xB0", 2)==0))))
-               && checkword(cw + i, info, root)) return 1;
-  }
-  // END of LANG_hu section
-
   switch(captype) {
-     case HUHCAP: 
-     case HUHINITCAP: 
-     case NOCAP: { 
+     case HUHCAP:
+     case HUHINITCAP:
+     case NOCAP: {
             rv = checkword(cw, info, root);
             if ((abbv) && !(rv)) {
                 memcpy(wspace,cw,wl);
                 *(wspace+wl) = '.';
                 *(wspace+wl+1) = '\0';
                 rv = checkword(wspace, info, root);
             }
             break;
@@ -480,30 +492,30 @@ int Hunspell::spell(const char * word, i
                         *(wspace+wl2) = '.';
                         *(wspace+wl2+1) = '\0';
                         rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
                     }
                 }
                 if (rv) break;
             }
         }
-     case INITCAP: { 
+     case INITCAP: {
              wl = mkallsmall2(cw, unicw, nc);
              memcpy(wspace,cw,(wl+1));
              wl2 = mkinitcap2(cw, unicw, nc);
     	     if (captype == INITCAP) *info += SPELL_INITCAP;
              rv = checkword(cw, info, root);
     	     if (captype == INITCAP) *info -= SPELL_INITCAP;
              // forbid bad capitalization
              // (for example, ijs -> Ijs instead of IJs in Dutch)
              // use explicit forms in dic: Ijs/F (F = FORBIDDENWORD flag)
              if (*info & SPELL_FORBIDDEN) {
                 rv = NULL;
                 break;
-             }             
+             }
              if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL;
              if (rv) break;
 
              rv = checkword(wspace, info, root);
              if (abbv && !rv) {
 
                  *(wspace+wl) = '.';
                  *(wspace+wl+1) = '\0';
@@ -520,105 +532,77 @@ int Hunspell::spell(const char * word, i
                  }
              }
              if (rv && is_keepcase(rv) &&
                 ((captype == ALLCAP) ||
                    // if CHECKSHARPS: KEEPCASE words with \xDF  are allowed
                    // in INITCAP form, too.
                    !(pAMgr->get_checksharps() &&
                       ((utf8 && strstr(wspace, "\xC3\x9F")) ||
-                      (!utf8 && strchr(wspace, '\xDF')))))) rv = NULL;             
+                      (!utf8 && strchr(wspace, '\xDF')))))) rv = NULL;
              break;
-           }               
+           }
   }
-  
+
   if (rv) return 1;
 
-  // recursive breaking at break points (not good for morphological analysis)
+  // recursive breaking at break points
   if (wordbreak) {
     char * s;
     char r;
     int corr = 0;
-    // German words beginning with "-" are not accepted
-    if (langnum == LANG_de) corr = 1;
+    wl = strlen(cw);
     int numbreak = pAMgr ? pAMgr->get_numbreak() : 0;
+    // check boundary patterns (^begin and end$)
     for (int j = 0; j < numbreak; j++) {
-      s=(char *) strstr(cw + corr, wordbreak[j]);
-      if (s) {
+      int plen = strlen(wordbreak[j]);
+      if (plen == 1 || plen > wl) continue;
+      if (wordbreak[j][0] == '^' && strncmp(cw, wordbreak[j] + 1, plen - 1) == 0
+        && spell(cw + plen - 1)) return 1;
+      if (wordbreak[j][plen - 1] == '$' &&
+        strncmp(cw + wl - plen + 1, wordbreak[j], plen - 1) == 0) {
+	    r = cw[wl - plen + 1];
+	    cw[wl - plen + 1] = '\0';
+    	    if (spell(cw)) return 1;
+	    cw[wl - plen + 1] = r;
+	}
+    }
+    // other patterns
+    for (int j = 0; j < numbreak; j++) {
+      int result = 0;
+      int plen = strlen(wordbreak[j]);
+      s=(char *) strstr(cw, wordbreak[j]);
+      if (s && (s > cw) && (s < cw + wl - plen)) {
+	if (!spell(s + plen)) continue;
         r = *s;
         *s = '\0';
         // examine 2 sides of the break point
-        if (spell(cw) && spell(s + strlen(wordbreak[j]))) {
-            *s = r;
-            return 1;
-        }
+        if (spell(cw)) return 1;
         *s = r;
+
+        // LANG_hu: spec. dash rule
+	if (langnum == LANG_hu && strcmp(wordbreak[j], "-") == 0) {
+	  r = s[1];
+	  s[1] = '\0';
+          if (spell(cw)) return 1; // check the first part with dash
+          s[1] = r;
+	}
+        // end of LANG speficic region
+
       }
     }
   }
 
-  // LANG_hu: compoundings with dashes and n-dashes XXX deprecated!
-  if (langnum == LANG_hu) {
-    int n;
-    // compound word with dash (HU) I18n
-    char * dash;
-    int result = 0;
-    // n-dash
-    dash = (char *) strstr(cw,"\xE2\x80\x93");
-    if (dash && !wordbreak) {
-        *dash = '\0';
-        // examine 2 sides of the dash
-        if (spell(cw) && spell(dash + 3)) {
-            *dash = '\xE2';
-            return 1;
-        }
-        *dash = '\xE2';
-    }
-    dash = (char *) strchr(cw,'-');
-    if (dash) {
-        *dash='\0';      
-        // examine 2 sides of the dash
-        if (dash[1] == '\0') { // base word ending with dash
-            if (spell(cw)) return 1;
-        } else {
-            // first word ending with dash: word-
-            char r2 = *(dash + 1);
-            dash[0]='-';
-            dash[1]='\0';
-            result = spell(cw);
-            dash[1] = r2;
-            dash[0]='\0';
-            if (result && spell(dash+1) && ((strlen(dash+1) > 1) || (dash[1] == 'e') ||
-                ((dash[1] > '0') && (dash[1] < '9')))) return 1;
-        }
-        // affixed number in correct word
-        if (result && (dash > cw) && (((*(dash-1)<='9') && (*(dash-1)>='0')) || (*(dash-1)>='.'))) {
-            *dash='-';
-            n = 1;
-            if (*(dash - n) == '.') n++;
-            // search first not a number character to left from dash
-            while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {
-                n++;
-            }
-            if ((dash - n) < cw) n--;
-            // numbers: deprecated
-            for(; n >= 1; n--) {
-                if ((*(dash - n) >= '0') && (*(dash - n) <= '9') &&
-                    checkword(dash - n, info, root)) return 1;
-            }
-        }
-    }
-  }
   return 0;
 }
 
 struct hentry * Hunspell::checkword(const char * w, int * info, char ** root)
 {
   struct hentry * he = NULL;
-  int len;
+  int len, i;
   char w2[MAXWORDUTF8LEN];
   const char * word;
 
   char * ignoredchars = pAMgr->get_ignore();
   if (ignoredchars != NULL) {
      strcpy(w2, w);
      if (utf8) {
         int ignoredchars_utf16_len;
@@ -635,81 +619,82 @@ struct hentry * Hunspell::checkword(cons
     if (word != w2) {
       strcpy(w2, word);
       word = w2;
     }
     if (utf8) reverseword_utf(w2); else reverseword(w2);
   }
 
   // look word in hash table
-  if (pHMgr) he = pHMgr->lookup(word);
+  for (i = 0; (i < maxdic) && !he; i ++) {
+  he = (pHMgr[i])->lookup(word);
 
   // check forbidden and onlyincompound words
   if ((he) && (he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
     if (info) *info += SPELL_FORBIDDEN;
     // LANG_hu section: set dash information for suggestions
     if (langnum == LANG_hu) {
         if (pAMgr->get_compoundflag() &&
             TESTAFF(he->astr, pAMgr->get_compoundflag(), he->alen)) {
                 if (info) *info += SPELL_COMPOUND;
         }
     }
     return NULL;
   }
 
-  // he = next not pseudoroot, onlyincompound homonym or onlyupcase word
+  // he = next not needaffix, onlyincompound homonym or onlyupcase word
   while (he && (he->astr) &&
-    ((pAMgr->get_pseudoroot() && TESTAFF(he->astr, pAMgr->get_pseudoroot(), he->alen)) ||
+    ((pAMgr->get_needaffix() && TESTAFF(he->astr, pAMgr->get_needaffix(), he->alen)) ||
        (pAMgr->get_onlyincompound() && TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) ||
        (info && (*info & SPELL_INITCAP) && TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen))
     )) he = he->next_homonym;
+  }
 
   // check with affixes
   if (!he && pAMgr) {
      // try stripping off affixes */
      len = strlen(word);
      he = pAMgr->affix_check(word, len, 0);
 
      // check compound restriction and onlyupcase
      if (he && he->astr && (
-        (pAMgr->get_onlyincompound() && 
-    	    TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) || 
+        (pAMgr->get_onlyincompound() &&
+    	    TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) ||
         (info && (*info & SPELL_INITCAP) &&
     	    TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen)))) {
     	    he = NULL;
      }
 
      if (he) {
         if ((he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
             if (info) *info += SPELL_FORBIDDEN;
             return NULL;
         }
         if (root) {
             *root = mystrdup(&(he->word));
-            if (complexprefixes) {
+            if (*root && complexprefixes) {
                 if (utf8) reverseword_utf(*root); else reverseword(*root);
             }
         }
      // try check compound word
      } else if (pAMgr->get_compound()) {
-          he = pAMgr->compound_check(word, len, 
-                                  0,0,100,0,NULL,0,NULL,NULL,0);
+          he = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 0);
           // LANG_hu section: `moving rule' with last dash
-          if ((!he) && (langnum == LANG_hu) && (word[len-1]=='-')) {
+          if ((!he) && (langnum == LANG_hu) && (word[len-1] == '-')) {
              char * dup = mystrdup(word);
+             if (!dup) return NULL;
              dup[len-1] = '\0';
-             he = pAMgr->compound_check(dup, len-1, 
-                                  -5,0,100,0,NULL,1,NULL,NULL,0);
+             he = pAMgr->compound_check(dup, len-1, -5, 0, 100, 0, NULL, 1, 0);
              free(dup);
           }
-          // end of LANG speficic region          
+          // end of LANG speficic region
           if (he) {
                 if (root) {
                     *root = mystrdup(&(he->word));
-                    if (complexprefixes) {
+                    if (*root && complexprefixes) {
                         if (utf8) reverseword_utf(*root); else reverseword(*root);
                     }
                 }
                 if (info) *info += SPELL_COMPOUND;
           }
      }
 
   }
@@ -717,65 +702,75 @@ struct hentry * Hunspell::checkword(cons
   return he;
 }
 
 int Hunspell::suggest(char*** slst, const char * word)
 {
   int onlycmpdsug = 0;
   char cw[MAXWORDUTF8LEN];
   char wspace[MAXWORDUTF8LEN];
-  if (! pSMgr) return 0;
+  if (!pSMgr || maxdic == 0) return 0;
   w_char unicw[MAXWORDLEN];
+  *slst = NULL;
+  // process XML input of the simplified API (see manual)
+  if (strncmp(word, SPELL_XML, sizeof(SPELL_XML) - 3) == 0) {
+     return spellml(slst, word);
+  }
   int nc = strlen(word);
   if (utf8) {
     if (nc >= MAXWORDUTF8LEN) return 0;
   } else {
     if (nc >= MAXWORDLEN) return 0;
   }
   int captype = 0;
   int abbv = 0;
-  int wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+  int wl = 0;
+
+  // input conversion
+  RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+  if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+  else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
   if (wl == 0) return 0;
   int ns = 0;
-  *slst = NULL;
   int capwords = 0;
 
   switch(captype) {
-     case NOCAP:   { 
+     case NOCAP:   {
                      ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
                      break;
                    }
 
-     case INITCAP: { 
+     case INITCAP: {
                      capwords = 1;
                      ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
                      if (ns == -1) break;
                      memcpy(wspace,cw,(wl+1));
                      mkallsmall2(wspace, unicw, nc);
                      ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
                      break;
                    }
      case HUHINITCAP:
                     capwords = 1;
-     case HUHCAP: { 
+     case HUHCAP: {
                      ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
                      if (ns != -1) {
                         int prevns;
     		        // something.The -> something. The
                         char * dot = strchr(cw, '.');
 		        if (dot && (dot > cw)) {
 		            int captype_;
 		            if (utf8) {
 		               w_char w_[MAXWORDLEN];
 			       int wl_ = u8_u16(w_, MAXWORDLEN, dot + 1);
 		               captype_ = get_captype_utf8(w_, wl_, langnum);
 		            } else captype_ = get_captype(dot+1, strlen(dot+1), csconv);
 		    	    if (captype_ == INITCAP) {
                         	char * st = mystrdup(cw);
-                        	st = (char *) realloc(st, wl + 2);
+                        	if (st) st = (char *) realloc(st, wl + 2);
 				if (st) {
                         		st[(dot - cw) + 1] = ' ';
                         		strcpy(st + (dot - cw) + 2, dot + 1);
                     			ns = insert_sug(slst, st, ns);
 					free(st);
 				}
 		    	    }
 		        }
@@ -812,17 +807,17 @@ int Hunspell::suggest(char*** slst, cons
                                     (*slst)[0] = r;
                                 }
                            }
                         }
                      }
                      break;
                    }
 
-     case ALLCAP: { 
+     case ALLCAP: {
                      memcpy(wspace, cw, (wl+1));
                      mkallsmall2(wspace, unicw, nc);
                      ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
                      if (ns == -1) break;
                      if (pAMgr && pAMgr->get_keepcase() && spell(wspace))
                         ns = insert_sug(slst, wspace, ns);
                      mkinitcap2(wspace, unicw, nc);
                      ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
@@ -864,49 +859,88 @@ int Hunspell::suggest(char*** slst, cons
               spell(w, &info, NULL);
               if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
                   *pos = ' ';
               } else *pos = '-';
           }
       }
   }
   // END OF LANG_hu section
-  
+
   // try ngram approach since found nothing
   if ((ns == 0 || onlycmpdsug) && pAMgr && (pAMgr->get_maxngramsugs() != 0)) {
       switch(captype) {
           case NOCAP: {
-              ns = pSMgr->ngsuggest(*slst, cw, ns, pHMgr);
+              ns = pSMgr->ngsuggest(*slst, cw, ns, pHMgr, maxdic);
               break;
           }
+	  case HUHINITCAP:
+              capwords = 1;
           case HUHCAP: {
               memcpy(wspace,cw,(wl+1));
               mkallsmall2(wspace, unicw, nc);
-              ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr);
-              break;
+              ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
+	      break;
           }
-          case INITCAP: { 
+         case INITCAP: {
               capwords = 1;
               memcpy(wspace,cw,(wl+1));
               mkallsmall2(wspace, unicw, nc);
-              ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr);
+              ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
               break;
           }
           case ALLCAP: {
               memcpy(wspace,cw,(wl+1));
               mkallsmall2(wspace, unicw, nc);
 	      int oldns = ns;
-              ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr);
-              for (int j = oldns; j < ns; j++) 
+              ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
+              for (int j = oldns; j < ns; j++)
                   mkallcap((*slst)[j]);
               break;
          }
       }
   }
 
+  // try dash suggestion (Afo-American -> Afro-American)
+  if (strchr(cw, '-')) {
+     char * pos = strchr(cw, '-');
+     char * ppos = cw;
+     int nodashsug = 1;
+     char ** nlst = NULL;
+     int nn = 0;
+     int last = 0;
+     for (int j = 0; j < ns && nodashsug == 1; j++) {
+        if (strchr((*slst)[j], '-')) nodashsug = 0;
+     }
+     while (nodashsug && !last) {
+	if (*pos == '\0') last = 1; else *pos = '\0';
+        if (!spell(ppos)) {
+          nn = suggest(&nlst, ppos);
+          for (int j = nn - 1; j >= 0; j--) {
+            strncpy(wspace, cw, ppos - cw);
+            strcpy(wspace + (ppos - cw), nlst[j]);
+            if (!last) {
+            	strcat(wspace, "-");
+		strcat(wspace, pos + 1);
+	    }
+            ns = insert_sug(slst, wspace, ns);
+            free(nlst[j]);
+          }
+          if (nlst != NULL) free(nlst);
+          nodashsug = 0;
+        }
+	if (!last) {
+          *pos = '-';
+          ppos = pos + 1;
+          pos = strchr(ppos, '-');
+        }
+	if (!pos) pos = cw + strlen(cw);
+     }
+  }
+
   // word reversing wrapper for complex prefixes
   if (complexprefixes) {
     for (int j = 0; j < ns; j++) {
       if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
     }
   }
 
   // capitalize
@@ -935,31 +969,31 @@ int Hunspell::suggest(char*** slst, cons
           int len;
           if (utf8) {
             len = u8_u16(w, MAXSWL, (*slst)[j]);
           } else {
             strcpy(s, (*slst)[j]);
             len = strlen(s);
           }
           mkallsmall2(s, w, len);
-          free((*slst)[j]);          
+          free((*slst)[j]);
           if (spell(s)) {
             (*slst)[l] = mystrdup(s);
-            l++;
+            if ((*slst)[l]) l++;
           } else {
             mkinitcap2(s, w, len);
             if (spell(s)) {
               (*slst)[l] = mystrdup(s);
-              l++;
+              if ((*slst)[l]) l++;
             }
           }
         } else {
           (*slst)[l] = (*slst)[j];
           l++;
-        }    
+        }
       }
       ns = l;
     }
   }
   }
 
   // remove duplications
   int l = 0;
@@ -968,73 +1002,93 @@ int Hunspell::suggest(char*** slst, cons
     for (int k = 0; k < l; k++) {
       if (strcmp((*slst)[k], (*slst)[j]) == 0) {
         free((*slst)[j]);
         l--;
       }
     }
     l++;
   }
+
+  // output conversion
+  rl = (pAMgr) ? pAMgr->get_oconvtable() : NULL;
+  for (int j = 0; rl && j < ns; j++) {
+    if (rl->conv((*slst)[j], wspace)) {
+      free((*slst)[j]);
+      (*slst)[j] = mystrdup(wspace);
+    }
+  }
+
+  // if suggestions removed by nosuggest, onlyincompound parameters
+  if (l == 0 && *slst) {
+    free(*slst);
+    *slst = NULL;
+  }
   return l;
 }
 
+void Hunspell::free_list(char *** slst, int n) {
+        freelist(slst, n);
+}
+
 char * Hunspell::get_dic_encoding()
 {
   return encoding;
 }
 
 #ifdef HUNSPELL_EXPERIMENTAL
 // XXX need UTF-8 support
 int Hunspell::suggest_auto(char*** slst, const char * word)
 {
   char cw[MAXWORDUTF8LEN];
   char wspace[MAXWORDUTF8LEN];
-  if (! pSMgr) return 0;
+  if (!pSMgr || maxdic == 0) return 0;
   int wl = strlen(word);
   if (utf8) {
     if (wl >= MAXWORDUTF8LEN) return 0;
   } else {
     if (wl >= MAXWORDLEN) return 0;
   }
   int captype = 0;
   int abbv = 0;
   wl = cleanword(cw, word, &captype, &abbv);
   if (wl == 0) return 0;
   int ns = 0;
   *slst = NULL; // HU, nsug in pSMgr->suggest
-  
+
   switch(captype) {
-     case NOCAP:   { 
+     case NOCAP:   {
                      ns = pSMgr->suggest_auto(slst, cw, ns);
                      if (ns>0) break;
                      break;
                    }
 
-     case INITCAP: { 
+     case INITCAP: {
                      memcpy(wspace,cw,(wl+1));
                      mkallsmall(wspace);
                      ns = pSMgr->suggest_auto(slst, wspace, ns);
                      for (int j=0; j < ns; j++)
                        mkinitcap((*slst)[j]);
                      ns = pSMgr->suggest_auto(slst, cw, ns);
                      break;
-                     
+
                    }
 
-     case HUHCAP: { 
+     case HUHINITCAP:
+     case HUHCAP: {
                      ns = pSMgr->suggest_auto(slst, cw, ns);
                      if (ns == 0) {
                         memcpy(wspace,cw,(wl+1));
                         mkallsmall(wspace);
                         ns = pSMgr->suggest_auto(slst, wspace, ns);
                      }
                      break;
                    }
 
-     case ALLCAP: { 
+     case ALLCAP: {
                      memcpy(wspace,cw,(wl+1));
                      mkallsmall(wspace);
                      ns = pSMgr->suggest_auto(slst, wspace, ns);
 
                      mkinitcap(wspace);
                      ns = pSMgr->suggest_auto(slst, wspace, ns);
 
                      for (int j=0; j < ns; j++)
@@ -1070,161 +1124,147 @@ int Hunspell::suggest_auto(char*** slst,
               strcat(w, pos + 1);
               spell(w, &info, NULL);
               if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
                   *pos = ' ';
               } else *pos = '-';
           }
       }
   }
-  // END OF LANG_hu section  
+  // END OF LANG_hu section
   return ns;
 }
+#endif
 
-// XXX need UTF-8 support
+int Hunspell::stem(char*** slst, char ** desc, int n)
+{
+  char result[MAXLNLEN];
+  char result2[MAXLNLEN];
+  *slst = NULL;
+  if (n == 0) return 0;
+  *result2 = '\0';
+  for (int i = 0; i < n; i++) {
+    *result = '\0';
+    // add compound word parts (except the last one)
+    char * s = (char *) desc[i];
+    char * part = strstr(s, MORPH_PART);
+    if (part) {
+        char * nextpart = strstr(part + 1, MORPH_PART);
+        while (nextpart) {
+            copy_field(result + strlen(result), part, MORPH_PART);
+            part = nextpart;
+            nextpart = strstr(part + 1, MORPH_PART);
+        }
+        s = part;
+    }
+
+    char **pl;
+    char tok[MAXLNLEN];
+    strcpy(tok, s);
+    char * alt = strstr(tok, " | ");
+    while (alt) {
+        alt[1] = MSEP_ALT;
+        alt = strstr(alt, " | ");
+    }
+    int pln = line_tok(tok, &pl, MSEP_ALT);
+    for (int k = 0; k < pln; k++) {
+        // add derivational suffixes
+        if (strstr(pl[k], MORPH_DERI_SFX)) {
+            // remove inflectional suffixes
+            char * is = strstr(pl[k], MORPH_INFL_SFX);
+            if (is) *is = '\0';
+            char * sg = pSMgr->suggest_gen(&(pl[k]), 1, pl[k]);
+            if (sg) {
+                char ** gen;
+                int genl = line_tok(sg, &gen, MSEP_REC);
+                free(sg);
+                for (int j = 0; j < genl; j++) {
+                    sprintf(result2 + strlen(result2), "%c%s%s",
+                            MSEP_REC, result, gen[j]);
+                }
+                freelist(&gen, genl);
+            }
+        } else {
+            sprintf(result2 + strlen(result2), "%c%s", MSEP_REC, result);
+            if (strstr(pl[k], MORPH_SURF_PFX)) {
+                copy_field(result2 + strlen(result2), pl[k], MORPH_SURF_PFX);
+            }
+            copy_field(result2 + strlen(result2), pl[k], MORPH_STEM);
+        }
+    }
+    freelist(&pl, pln);
+  }
+  int sln = line_tok(result2, slst, MSEP_REC);
+  return uniqlist(*slst, sln);
+
+}
+
 int Hunspell::stem(char*** slst, const char * word)
 {
+  char ** pl;
+  int pln = analyze(&pl, word);
+  int pln2 = stem(slst, pl, pln);
+  freelist(&pl, pln);
+  return pln2;
+}
+
+#ifdef HUNSPELL_EXPERIMENTAL
+int Hunspell::suggest_pos_stems(char*** slst, const char * word)
+{
   char cw[MAXWORDUTF8LEN];
   char wspace[MAXWORDUTF8LEN];
-  if (! pSMgr) return 0;
+  if (! pSMgr || maxdic == 0) return 0;
   int wl = strlen(word);
   if (utf8) {
     if (wl >= MAXWORDUTF8LEN) return 0;
   } else {
     if (wl >= MAXWORDLEN) return 0;
   }
   int captype = 0;
   int abbv = 0;
   wl = cleanword(cw, word, &captype, &abbv);
   if (wl == 0) return 0;
-  
-  int ns = 0;
 
-  *slst = NULL; // HU, nsug in pSMgr->suggest
-  
-  switch(captype) {
-     case HUHCAP:
-     case NOCAP:   { 
-                     ns = pSMgr->suggest_stems(slst, cw, ns);
-
-                     if ((abbv) && (ns == 0)) {
-                         memcpy(wspace,cw,wl);
-                         *(wspace+wl) = '.';
-                         *(wspace+wl+1) = '\0';
-                         ns = pSMgr->suggest_stems(slst, wspace, ns);
-                     }
-
-                     break;
-                   }
-
-     case INITCAP: { 
-
-                     ns = pSMgr->suggest_stems(slst, cw, ns);
-
-                     if (ns == 0) {
-                        memcpy(wspace,cw,(wl+1));
-                        mkallsmall(wspace);
-                        ns = pSMgr->suggest_stems(slst, wspace, ns);
-
-                     }
-
-                     if ((abbv) && (ns == 0)) {
-                         memcpy(wspace,cw,wl);
-                         mkallsmall(wspace);
-                         *(wspace+wl) = '.';
-                         *(wspace+wl+1) = '\0';
-                         ns = pSMgr->suggest_stems(slst, wspace, ns);
-                     }
-                     
-                     break;
-                     
-                   }
-
-     case ALLCAP: { 
-                     ns = pSMgr->suggest_stems(slst, cw, ns);
-                     if (ns != 0) break;
-                     
-                     memcpy(wspace,cw,(wl+1));
-                     mkallsmall(wspace);
-                     ns = pSMgr->suggest_stems(slst, wspace, ns);
-
-                     if (ns == 0) {
-                         mkinitcap(wspace);
-                         ns = pSMgr->suggest_stems(slst, wspace, ns);
-                     }
-
-                     if ((abbv) && (ns == 0)) {
-                         memcpy(wspace,cw,wl);
-                         mkallsmall(wspace);
-                         *(wspace+wl) = '.';
-                         *(wspace+wl+1) = '\0';
-                         ns = pSMgr->suggest_stems(slst, wspace, ns);
-                     }
-
-
-                     break;
-                   }
-  }
-  
-  return ns;
-}
-
-int Hunspell::suggest_pos_stems(char*** slst, const char * word)
-{
-  char cw[MAXWORDUTF8LEN];
-  char wspace[MAXWORDUTF8LEN];
-  if (! pSMgr) return 0;
-  int wl = strlen(word);
-  if (utf8) {
-    if (wl >= MAXWORDUTF8LEN) return 0;
-  } else {
-    if (wl >= MAXWORDLEN) return 0;
-  }
-  int captype = 0;
-  int abbv = 0;
-  wl = cleanword(cw, word, &captype, &abbv);
-  if (wl == 0) return 0;
-  
   int ns = 0; // ns=0 = normalized input
 
   *slst = NULL; // HU, nsug in pSMgr->suggest
-  
+
   switch(captype) {
      case HUHCAP:
-     case NOCAP:   { 
+     case NOCAP:   {
                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);
 
                      if ((abbv) && (ns == 0)) {
                          memcpy(wspace,cw,wl);
                          *(wspace+wl) = '.';
                          *(wspace+wl+1) = '\0';
                          ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
                      }
 
                      break;
                    }
 
-     case INITCAP: { 
+     case INITCAP: {
 
                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);
 
                      if (ns == 0 || ((*slst)[0][0] == '#')) {
                         memcpy(wspace,cw,(wl+1));
                         mkallsmall(wspace);
                         ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
                      }
-                     
+
                      break;
-                     
+
                    }
 
-     case ALLCAP: { 
+     case ALLCAP: {
                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);
                      if (ns != 0) break;
-                     
+
                      memcpy(wspace,cw,(wl+1));
                      mkallsmall(wspace);
                      ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
 
                      if (ns == 0) {
                          mkinitcap(wspace);
                          ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
                      }
@@ -1284,513 +1324,605 @@ int Hunspell::mkinitsmall2(char * p, w_c
       u[0].h = (unsigned char) (i >> 8);
       u[0].l = (unsigned char) (i & 0x00FF);
       u16_u8(p, MAXWORDUTF8LEN, u, nc);
       return strlen(p);
   }
   return nc;
 }
 
-int Hunspell::put_word(const char * word)
+int Hunspell::add(const char * word)
 {
-    if (pHMgr) return pHMgr->put_word(word, NULL);
+    if (pHMgr[0]) return (pHMgr[0])->add(word);
     return 0;
 }
 
-int Hunspell::put_word_pattern(const char * word, const char * pattern)
+int Hunspell::add_with_affix(const char * word, const char * example)
 {
-    if (pHMgr) return pHMgr->put_word_pattern(word, pattern);
+    if (pHMgr[0]) return (pHMgr[0])->add_with_affix(word, example);
+    return 0;
+}
+
+int Hunspell::remove(const char * word)
+{
+    if (pHMgr[0]) return (pHMgr[0])->remove(word);
     return 0;
 }
 
 const char * Hunspell::get_version()
 {
   return pAMgr->get_version();
 }
 
 struct cs_info * Hunspell::get_csconv()
 {
   return csconv;
 }
 
-#ifdef HUNSPELL_EXPERIMENTAL
-// XXX need UTF-8 support
-char * Hunspell::morph(const char * word)
+void Hunspell::cat_result(char * result, char * st)
+{
+    if (st) {
+        if (*result) mystrcat(result, "\n", MAXLNLEN);
+        mystrcat(result, st, MAXLNLEN);
+        free(st);
+    }
+}
+
+int Hunspell::analyze(char*** slst, const char * word)
 {
   char cw[MAXWORDUTF8LEN];
   char wspace[MAXWORDUTF8LEN];
-  if (! pSMgr) return 0;
-  int wl = strlen(word);
+  w_char unicw[MAXWORDLEN];
+  int wl2 = 0;
+  *slst = NULL;
+  if (! pSMgr || maxdic == 0) return 0;
+  int nc = strlen(word);
   if (utf8) {
-    if (wl >= MAXWORDUTF8LEN) return 0;
+    if (nc >= MAXWORDUTF8LEN) return 0;
   } else {
-    if (wl >= MAXWORDLEN) return 0;
+    if (nc >= MAXWORDLEN) return 0;
   }
   int captype = 0;
   int abbv = 0;
-  wl = cleanword(cw, word, &captype, &abbv);
+  int wl = 0;
+
+  // input conversion
+  RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
+  if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
+  else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
+
   if (wl == 0) {
       if (abbv) {
           for (wl = 0; wl < abbv; wl++) cw[wl] = '.';
           cw[wl] = '\0';
           abbv = 0;
       } else return 0;
   }
 
   char result[MAXLNLEN];
   char * st = NULL;
-  
+
   *result = '\0';
 
   int n = 0;
   int n2 = 0;
   int n3 = 0;
 
   // test numbers
   // LANG_hu section: set dash information for suggestions
   if (langnum == LANG_hu) {
-  while ((n < wl) && 
+  while ((n < wl) &&
         (((cw[n] <= '9') && (cw[n] >= '0')) || (((cw[n] == '.') || (cw[n] == ',')) && (n > 0)))) {
         n++;
         if ((cw[n] == '.') || (cw[n] == ',')) {
-                if (((n2 == 0) && (n > 3)) || 
+                if (((n2 == 0) && (n > 3)) ||
                         ((n2 > 0) && ((cw[n-1] == '.') || (cw[n-1] == ',')))) break;
                 n2++;
                 n3 = n;
         }
   }
 
-  if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return NULL;
+  if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return 0;
   if ((n == wl) || ((n>0) && ((cw[n]=='%') || (cw[n]=='\xB0')) && checkword(cw+n, NULL, NULL))) {
-        strcat(result, cw);
+        mystrcat(result, cw, MAXLNLEN);
         result[n - 1] = '\0';
-        if (n == wl) {
-                st = pSMgr->suggest_morph(cw + n - 1);
-                if (st) {
-                        strcat(result, st);
-                        free(st);
-                }
-        } else {
+        if (n == wl) cat_result(result, pSMgr->suggest_morph(cw + n - 1));
+        else {
                 char sign = cw[n];
                 cw[n] = '\0';
-                st = pSMgr->suggest_morph(cw + n - 1);
-                if (st) {
-                        strcat(result, st);
-                        free(st);
-                }
-                strcat(result, "+"); // XXX SPEC. MORPHCODE
+                cat_result(result, pSMgr->suggest_morph(cw + n - 1));
+                mystrcat(result, "+", MAXLNLEN); // XXX SPEC. MORPHCODE
                 cw[n] = sign;
-                st = pSMgr->suggest_morph(cw + n);
-                if (st) {
-                        strcat(result, st);
-                        free(st);
-                }
+                cat_result(result, pSMgr->suggest_morph(cw + n));
         }
-        return mystrdup(result);
+        return line_tok(result, slst, MSEP_REC);
   }
   }
   // END OF LANG_hu section
-  
+
   switch(captype) {
-     case NOCAP:   { 
-                     st = pSMgr->suggest_morph(cw);
-                     if (st) {
-                        strcat(result, st);
-                        free(st);
-                     }
-                                         if (abbv) {
-                                        memcpy(wspace,cw,wl);
+     case HUHCAP:
+     case HUHINITCAP:
+     case NOCAP:  {
+                    cat_result(result, pSMgr->suggest_morph(cw));
+                    if (abbv) {
+                        memcpy(wspace,cw,wl);
+                        *(wspace+wl) = '.';
+                        *(wspace+wl+1) = '\0';
+                        cat_result(result, pSMgr->suggest_morph(wspace));
+                    }
+                    break;
+                }
+     case INITCAP: {
+                     wl = mkallsmall2(cw, unicw, nc);
+                     memcpy(wspace,cw,(wl+1));
+                     wl2 = mkinitcap2(cw, unicw, nc);
+                     cat_result(result, pSMgr->suggest_morph(wspace));
+                     cat_result(result, pSMgr->suggest_morph(cw));
+                     if (abbv) {
                          *(wspace+wl) = '.';
                          *(wspace+wl+1) = '\0';
-                         st = pSMgr->suggest_morph(wspace);
-                         if (st) {
-                            if (*result) strcat(result, "\n");
-                            strcat(result, st);
-                            free(st);
-                                                 }
-                     }
-                                         break;
-                   }
-     case INITCAP: { 
-                     memcpy(wspace,cw,(wl+1));
-                     mkallsmall(wspace);
-                     st = pSMgr->suggest_morph(wspace);
-                     if (st) {
-                        strcat(result, st);
-                        free(st);
-                     }                                   
-                         st = pSMgr->suggest_morph(cw);
-                     if (st) {
-                        if (*result) strcat(result, "\n");
-                        strcat(result, st);
-                        free(st);
-                     }
-                                         if (abbv) {
-                                         memcpy(wspace,cw,wl);
-                         *(wspace+wl) = '.';
-                         *(wspace+wl+1) = '\0';
-                         mkallsmall(wspace);
-                         st = pSMgr->suggest_morph(wspace);
-                         if (st) {
-                            if (*result) strcat(result, "\n");
-                            strcat(result, st);
-                            free(st);
-                                                 }
-                         mkinitcap(wspace);
-                         st = pSMgr->suggest_morph(wspace);
-                         if (st) {
-                            if (*result) strcat(result, "\n");
-                            strcat(result, st);
-                            free(st);
-                                                 }
+                         cat_result(result, pSMgr->suggest_morph(wspace));
+
+                         memcpy(wspace, cw, wl2);
+                         *(wspace+wl2) = '.';
+                         *(wspace+wl2+1) = '\0';
+
+                         cat_result(result, pSMgr->suggest_morph(wspace));
                      }
                      break;
                    }
-     case HUHCAP: { 
-                     st = pSMgr->suggest_morph(cw);
-                     if (st) {
-                        strcat(result, st);
-                        free(st);
-                     }
-#if 0
-                     memcpy(wspace,cw,(wl+1));
-                     mkallsmall(wspace);
-                     st = pSMgr->suggest_morph(wspace);
-                     if (st) {
-                        if (*result) strcat(result, "\n");
-                        strcat(result, st);
-                        free(st);
-                     }
-#endif
-                     break;
-                 }
-     case ALLCAP: { 
-                     memcpy(wspace,cw,(wl+1));
-                     st = pSMgr->suggest_morph(wspace);
-                     if (st) {
-                        strcat(result, st);
-                        free(st);
-                     }               
-                     mkallsmall(wspace);
-                     st = pSMgr->suggest_morph(wspace);
-                     if (st) {
-                        if (*result) strcat(result, "\n");
-                        strcat(result, st);
-                        free(st);
+     case ALLCAP: {
+                     cat_result(result, pSMgr->suggest_morph(cw));
+                     if (abbv) {
+                         memcpy(wspace,cw,wl);
+                         *(wspace+wl) = '.';
+                         *(wspace+wl+1) = '\0';
+                         cat_result(result, pSMgr->suggest_morph(cw));
                      }
-                             mkinitcap(wspace);
-                             st = pSMgr->suggest_morph(wspace);
-                     if (st) {
-                        if (*result) strcat(result, "\n");
-                        strcat(result, st);
-                        free(st);
+                     wl = mkallsmall2(cw, unicw, nc);
+                     memcpy(wspace,cw,(wl+1));
+                     wl2 = mkinitcap2(cw, unicw, nc);
+
+                     cat_result(result, pSMgr->suggest_morph(wspace));
+                     cat_result(result, pSMgr->suggest_morph(cw));
+                     if (abbv) {
+                         *(wspace+wl) = '.';
+                         *(wspace+wl+1) = '\0';
+                         cat_result(result, pSMgr->suggest_morph(wspace));
+
+                         memcpy(wspace, cw, wl2);
+                         *(wspace+wl2) = '.';
+                         *(wspace+wl2+1) = '\0';
+
+                         cat_result(result, pSMgr->suggest_morph(wspace));
                      }
-                                         if (abbv) {
-                        memcpy(wspace,cw,(wl+1));
-                        *(wspace+wl) = '.';
-                        *(wspace+wl+1) = '\0';
-                        if (*result) strcat(result, "\n");
-                        st = pSMgr->suggest_morph(wspace);
-                        if (st) {
-                                strcat(result, st);
-                                free(st);
-                        }                    
-                        mkallsmall(wspace);
-                        st = pSMgr->suggest_morph(wspace);
-                        if (st) {
-                          if (*result) strcat(result, "\n");
-                          strcat(result, st);
-                          free(st);
-                        }
-                                mkinitcap(wspace);
-                                st = pSMgr->suggest_morph(wspace);
-                        if (st) {
-                          if (*result) strcat(result, "\n");
-                          strcat(result, st);
-                          free(st);
-                        }
-                                         }
                      break;
                    }
   }
 
-  if (result && (*result)) {
+  if (*result) {
     // word reversing wrapper for complex prefixes
     if (complexprefixes) {
       if (utf8) reverseword_utf(result); else reverseword(result);
     }
-    return mystrdup(result);
+    return line_tok(result, slst, MSEP_REC);
+
   }
 
   // compound word with dash (HU) I18n
   char * dash = NULL;
   int nresult = 0;
   // LANG_hu section: set dash information for suggestions
   if (langnum == LANG_hu) dash = (char *) strchr(cw,'-');
   if ((langnum == LANG_hu) && dash) {
-      *dash='\0';      
+      *dash='\0';
       // examine 2 sides of the dash
       if (dash[1] == '\0') { // base word ending with dash
-        if (spell(cw)) return pSMgr->suggest_morph(cw);
+        if (spell(cw)) return line_tok(pSMgr->suggest_morph(cw), slst, MSEP_REC);
       } else if ((dash[1] == 'e') && (dash[2] == '\0')) { // XXX (HU) -e hat.
         if (spell(cw) && (spell("-e"))) {
                         st = pSMgr->suggest_morph(cw);
                         if (st) {
-                                strcat(result, st);
+                                mystrcat(result, st, MAXLNLEN);
                                 free(st);
                         }
-                        strcat(result,"+"); // XXX spec. separator in MORPHCODE
+                        mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE
                         st = pSMgr->suggest_morph("-e");
                         if (st) {
-                                strcat(result, st);
+                                mystrcat(result, st, MAXLNLEN);
                                 free(st);
                         }
-                        return mystrdup(result);
+                        return line_tok(result, slst, MSEP_REC);
                 }
       } else {
       // first word ending with dash: word- XXX ???
         char r2 = *(dash + 1);
         dash[0]='-';
         dash[1]='\0';
         nresult = spell(cw);
         dash[1] = r2;
         dash[0]='\0';
         if (nresult && spell(dash+1) && ((strlen(dash+1) > 1) ||
                 ((dash[1] > '0') && (dash[1] < '9')))) {
-                            st = morph(cw);
+                            st = pSMgr->suggest_morph(cw);
                             if (st) {
-                                strcat(result, st);
+                                mystrcat(result, st, MAXLNLEN);
                                     free(st);
-                                strcat(result,"+"); // XXX spec. separator in MORPHCODE
+                                mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE
                             }
-                            st = morph(dash+1);
+                            st = pSMgr->suggest_morph(dash+1);
                             if (st) {
-                                    strcat(result, st);
+                                    mystrcat(result, st, MAXLNLEN);
                                     free(st);
                             }
-                            return mystrdup(result);                    
+                            return line_tok(result, slst, MSEP_REC);
                         }
       }
       // affixed number in correct word
-     if (nresult && (dash > cw) && (((*(dash-1)<='9') && 
+     if (nresult && (dash > cw) && (((*(dash-1)<='9') &&
                         (*(dash-1)>='0')) || (*(dash-1)=='.'))) {
          *dash='-';
          n = 1;
          if (*(dash - n) == '.') n++;
          // search first not a number character to left from dash
          while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {
             n++;
          }
          if ((dash - n) < cw) n--;
          // numbers: valami1000000-hoz
          // examine 100000-hoz, 10000-hoz 1000-hoz, 10-hoz,
          // 56-hoz, 6-hoz
          for(; n >= 1; n--) {
             if ((*(dash - n) >= '0') && (*(dash - n) <= '9') && checkword(dash - n, NULL, NULL)) {
-                    strcat(result, cw);
+                    mystrcat(result, cw, MAXLNLEN);
                     result[dash - cw - n] = '\0';
                         st = pSMgr->suggest_morph(dash - n);
                         if (st) {
-                        strcat(result, st);
+                        mystrcat(result, st, MAXLNLEN);
                                 free(st);
                         }
-                    return mystrdup(result);                    
+                        return line_tok(result, slst, MSEP_REC);
             }
          }
      }
   }
-  return NULL;
+  return 0;
+}
+
+int Hunspell::generate(char*** slst, const char * word, char ** pl, int pln)
+{
+  *slst = NULL;
+  if (!pSMgr || !pln) return 0;
+  char **pl2;
+  int pl2n = analyze(&pl2, word);
+  int captype = 0;
+  int abbv = 0;
+  char cw[MAXWORDUTF8LEN];
+  cleanword(cw, word, &captype, &abbv);
+  char result[MAXLNLEN];
+  *result = '\0';
+
+  for (int i = 0; i < pln; i++) {
+    cat_result(result, pSMgr->suggest_gen(pl2, pl2n, pl[i]));
+  }
+  freelist(&pl2, pl2n);
+
+  if (*result) {
+    // allcap
+    if (captype == ALLCAP) mkallcap(result);
+
+    // line split
+    int linenum = line_tok(result, slst, MSEP_REC);
+
+    // capitalize
+    if (captype == INITCAP || captype == HUHINITCAP) {
+        for (int j=0; j < linenum; j++) mkinitcap((*slst)[j]);
+    }
+
+    // temporary filtering of prefix related errors (eg.
+    // generate("undrinkable", "eats") --> "undrinkables" and "*undrinks")
+
+    int r = 0;
+    for (int j=0; j < linenum; j++) {
+        if (!spell((*slst)[j])) {
+            free((*slst)[j]);
+            (*slst)[j] = NULL;
+        } else {
+            if (r < j) (*slst)[r] = (*slst)[j];
+            r++;
+        }
+    }
+    if (r > 0) return r;
+    free(*slst);
+    *slst = NULL;
+  }
+  return 0;
+}
+
+int Hunspell::generate(char*** slst, const char * word, const char * pattern)
+{
+  char **pl;
+  int pln = analyze(&pl, pattern);
+  int n = generate(slst, word, pl, pln);
+  freelist(&pl, pln);
+  return uniqlist(*slst, n);
+}
+
+// minimal XML parser functions
+int Hunspell::get_xml_par(char * dest, const char * par, int max)
+{
+   char * d = dest;
+   if (!par) return 0;
+   char end = *par;
+   char * dmax = dest + max;
+   if (end == '>') end = '<';
+   else if (end != '\'' && end != '"') return 0; // bad XML
+   for (par++; d < dmax && *par != '\0' && *par != end; par++, d++) *d = *par;
+   *d = '\0';
+   mystrrep(dest, "&lt;", "<");
+   mystrrep(dest, "&amp;", "&");
+   return d - dest;
 }
 
+// return the beginning of the element (attr == NULL) or the attribute
+const char * Hunspell::get_xml_pos(const char * s, const char * attr)
+{
+  const char * end = strchr(s, '>');
+  const char * p = s;
+  if (attr == NULL) return end;
+  do {
+    p = strstr(p, attr);
+    if (!p || p >= end) return 0;
+  } while (*(p-1) != ' ' &&  *(p-1) != '\n');
+  return p + strlen(attr);
+}
+
+int Hunspell::check_xml_par(const char * q, const char * attr, const char * value) {
+  char cw[MAXWORDUTF8LEN];
+  if (get_xml_par(cw, get_xml_pos(q, attr), MAXWORDUTF8LEN - 1) &&
+    strcmp(cw, value) == 0) return 1;
+  return 0;
+}
+
+int Hunspell::get_xml_list(char ***slst, char * list, const char * tag) {
+    int n = 0;
+    char * p;
+    if (!list) return 0;
+    for (p = list; (p = strstr(p, tag)); p++) n++;
+    if (n == 0) return 0;
+    *slst = (char **) malloc(sizeof(char *) * n);
+    if (!*slst) return 0;
+    for (p = list, n = 0; (p = strstr(p, tag)); p++, n++) {
+        int l = strlen(p);
+        (*slst)[n] = (char *) malloc(l);
+        if (!(*slst)[n]) return (n > 0 ? n - 1 : 0);
+        get_xml_par((*slst)[n], p + strlen(tag) - 1, l);
+    }
+    return n;
+}
+
+int Hunspell::spellml(char*** slst, const char * word)
+{
+  char *q, *q2;
+  char cw[MAXWORDUTF8LEN], cw2[MAXWORDUTF8LEN];
+  q = (char *) strstr(word, "<query");
+  if (!q) return 0; // bad XML input
+  q2 = strchr(q, '>');
+  if (!q2) return 0; // bad XML input
+  q2 = strstr(q2, "<word");
+  if (!q2) return 0; // bad XML input
+  if (check_xml_par(q, "type=", "analyze")) {
+      int n = 0, s = 0;
+      if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN)) n = analyze(slst, cw);
+      if (n == 0) return 0;
+      // convert the result to <code><a>ana1</a><a>ana2</a></code> format
+      for (int i = 0; i < n; i++) s+= strlen((*slst)[i]);
+      char * r = (char *) malloc(6 + 5 * s + 7 * n + 7 + 1); // XXX 5*s->&->&amp;
+      if (!r) return 0;
+      strcpy(r, "<code>");
+      for (int i = 0; i < n; i++) {
+        int l = strlen(r);
+        strcpy(r + l, "<a>");
+        strcpy(r + l + 3, (*slst)[i]);
+        mystrrep(r + l + 3, "\t", " ");
+        mystrrep(r + l + 3, "<", "&lt;");
+        mystrrep(r + l + 3, "&", "&amp;");
+        strcat(r, "</a>");
+        free((*slst)[i]);
+      }
+      strcat(r, "</code>");
+      (*slst)[0] = r;
+      return 1;
+  } else if (check_xml_par(q, "type=", "stem")) {
+      if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN)) return stem(slst, cw);
+  } else if (check_xml_par(q, "type=", "generate")) {
+      int n = get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN);
+      if (n == 0) return 0;
+      char * q3 = strstr(q2 + 1, "<word");
+      if (q3) {
+        if (get_xml_par(cw2, strchr(q3, '>'), MAXWORDUTF8LEN)) {
+            return generate(slst, cw, cw2);
+        }
+      } else {
+        char ** slst2;
+        if ((q2 = strstr(q2 + 1, "<code")) &&
+          (n = get_xml_list(&slst2, strchr(q2, '>'), "<a>"))) {
+             int n2 = generate(slst, cw, slst2, n);
+             freelist(&slst2, n);
+             return uniqlist(*slst, n2);
+        }
+      }
+  }
+  return 0;
+}
+
+
+#ifdef HUNSPELL_EXPERIMENTAL
 // XXX need UTF-8 support
 char * Hunspell::morph_with_correction(const char * word)
 {
   char cw[MAXWORDUTF8LEN];
   char wspace[MAXWORDUTF8LEN];
-  if (! pSMgr) return 0;
+  if (! pSMgr || maxdic == 0) return NULL;
   int wl = strlen(word);
   if (utf8) {
-    if (wl >= MAXWORDUTF8LEN) return 0;
+    if (wl >= MAXWORDUTF8LEN) return NULL;
   } else {
-    if (wl >= MAXWORDLEN) return 0;
+    if (wl >= MAXWORDLEN) return NULL;
   }
   int captype = 0;
   int abbv = 0;
   wl = cleanword(cw, word, &captype, &abbv);
-  if (wl == 0) return 0;
+  if (wl == 0) return NULL;
 
   char result[MAXLNLEN];
   char * st = NULL;
-  
+
   *result = '\0';
-  
-  
+
+
   switch(captype) {
-     case NOCAP:   { 
+     case NOCAP:   {
                      st = pSMgr->suggest_morph_for_spelling_error(cw);
                      if (st) {
-                        strcat(result, st);
+                        mystrcat(result, st, MAXLNLEN);
                         free(st);
                      }
-                                         if (abbv) {
-                                        memcpy(wspace,cw,wl);
+                     if (abbv) {
+                         memcpy(wspace,cw,wl);
                          *(wspace+wl) = '.';
                          *(wspace+wl+1) = '\0';
                          st = pSMgr->suggest_morph_for_spelling_error(wspace);
                          if (st) {
-                            if (*result) strcat(result, "\n");
-                            strcat(result, st);
+                            if (*result) mystrcat(result, "\n", MAXLNLEN);
+                            mystrcat(result, st, MAXLNLEN);
                             free(st);
                                                  }
                      }
                                          break;
                    }
-     case INITCAP: { 
+     case INITCAP: {
                      memcpy(wspace,cw,(wl+1));
                      mkallsmall(wspace);
                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
                      if (st) {
-                        strcat(result, st);
-                        free(st);
-                     }                                   
-                         st = pSMgr->suggest_morph_for_spelling_error(cw);
-                     if (st) {
-                        if (*result) strcat(result, "\n");
-                        strcat(result, st);
+                        mystrcat(result, st, MAXLNLEN);
                         free(st);
                      }
-                                         if (abbv) {
-                                         memcpy(wspace,cw,wl);
+                     st = pSMgr->suggest_morph_for_spelling_error(cw);
+                     if (st) {
+                        if (*result) mystrcat(result, "\n", MAXLNLEN);
+                        mystrcat(result, st, MAXLNLEN);
+                        free(st);
+                     }
+                     if (abbv) {
+                         memcpy(wspace,cw,wl);
                          *(wspace+wl) = '.';
                          *(wspace+wl+1) = '\0';
                          mkallsmall(wspace);
                          st = pSMgr->suggest_morph_for_spelling_error(wspace);
                          if (st) {
-                            if (*result) strcat(result, "\n");
-                            strcat(result, st);
+                            if (*result) mystrcat(result, "\n", MAXLNLEN);
+                            mystrcat(result, st, MAXLNLEN);
                             free(st);
-                                                 }
+                         }
                          mkinitcap(wspace);
                          st = pSMgr->suggest_morph_for_spelling_error(wspace);
                          if (st) {
-                            if (*result) strcat(result, "\n");
-                            strcat(result, st);
+                            if (*result) mystrcat(result, "\n", MAXLNLEN);
+                            mystrcat(result, st, MAXLNLEN);
                             free(st);
-                                                 }
+                         }
                      }
                      break;
                    }
-     case HUHCAP: { 
+     case HUHCAP: {
                      st = pSMgr->suggest_morph_for_spelling_error(cw);
                      if (st) {
-                        strcat(result, st);
+                        mystrcat(result, st, MAXLNLEN);
                         free(st);
                      }
                      memcpy(wspace,cw,(wl+1));
                      mkallsmall(wspace);
                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
                      if (st) {
-                        if (*result) strcat(result, "\n");
-                        strcat(result, st);
+                        if (*result) mystrcat(result, "\n", MAXLNLEN);
+                        mystrcat(result, st, MAXLNLEN);
                         free(st);
-                     }               
+                     }
                      break;
                  }
-     case ALLCAP: { 
+     case ALLCAP: {
                      memcpy(wspace,cw,(wl+1));
                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
                      if (st) {
-                        strcat(result, st);
+                        mystrcat(result, st, MAXLNLEN);
                         free(st);
-                     }               
+                     }
                      mkallsmall(wspace);
                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
                      if (st) {
-                        if (*result) strcat(result, "\n");
-                        strcat(result, st);
+                        if (*result) mystrcat(result, "\n", MAXLNLEN);
+                        mystrcat(result, st, MAXLNLEN);
                         free(st);
                      }
-                             mkinitcap(wspace);
-                             st = pSMgr->suggest_morph_for_spelling_error(wspace);
+                     mkinitcap(wspace);
+                     st = pSMgr->suggest_morph_for_spelling_error(wspace);
                      if (st) {
-                        if (*result) strcat(result, "\n");
-                        strcat(result, st);
+                        if (*result) mystrcat(result, "\n", MAXLNLEN);
+                        mystrcat(result, st, MAXLNLEN);
                         free(st);
                      }
-                                         if (abbv) {
+                     if (abbv) {
                         memcpy(wspace,cw,(wl+1));
                         *(wspace+wl) = '.';
                         *(wspace+wl+1) = '\0';
-                        if (*result) strcat(result, "\n");
+                        if (*result) mystrcat(result, "\n", MAXLNLEN);
                         st = pSMgr->suggest_morph_for_spelling_error(wspace);
                         if (st) {
-                                strcat(result, st);
-                                free(st);
-                        }                    
+                            mystrcat(result, st, MAXLNLEN);
+                            free(st);
+                        }
                         mkallsmall(wspace);
                         st = pSMgr->suggest_morph_for_spelling_error(wspace);
                         if (st) {
-                          if (*result) strcat(result, "\n");
-                          strcat(result, st);
+                          if (*result) mystrcat(result, "\n", MAXLNLEN);
+                          mystrcat(result, st, MAXLNLEN);
                           free(st);
                         }
-                                mkinitcap(wspace);
-                                st = pSMgr->suggest_morph_for_spelling_error(wspace);
+                        mkinitcap(wspace);
+                        st = pSMgr->suggest_morph_for_spelling_error(wspace);
                         if (st) {
-                          if (*result) strcat(result, "\n");
-                          strcat(result, st);
+                          if (*result) mystrcat(result, "\n", MAXLNLEN);
+                          mystrcat(result, st, MAXLNLEN);
                           free(st);
                         }
-                                         }
+                     }
                      break;
                    }
   }
 
-  if (result) return mystrdup(result);
+  if (*result) return mystrdup(result);
   return NULL;
 }
 
-/* analyze word
- * return line count 
- * XXX need a better data structure for morphological analysis */
-int Hunspell::analyze(char ***out, const char *word) {
-  int  n = 0;
-  if (!word) return 0;
-  char * m = morph(word);
-  if(!m) return 0;
-  if (!out)
-  {
-     n = line_tok(m, out);
-     free(m);
-     return n;
-  }
-
-  // without memory allocation
-  /* BUG missing buffer size checking */
-  int i, p;
-  for(p = 0, i = 0; m[i]; i++) {
-     if(m[i] == '\n' || !m[i+1]) {
-       n++;
-       strncpy((*out)[n++], m + p, i - p + 1);
-       if (m[i] == '\n') (*out)[n++][i - p] = '\0';
-       if(!m[i+1]) break;
-       p = i + 1;        
-     }
-  }
-  free(m);
-  return n;
-}
-
 #endif // END OF HUNSPELL_EXPERIMENTAL CODE
 
 Hunhandle *Hunspell_create(const char * affpath, const char * dpath)
 {
         return (Hunhandle*)(new Hunspell(affpath, dpath));
 }
 
+Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath,
+    const char * key)
+{
+        return (Hunhandle*)(new Hunspell(affpath, dpath, key));
+}
+
 void Hunspell_destroy(Hunhandle *pHunspell)
 {
         delete (Hunspell*)(pHunspell);
 }
 
 int Hunspell_spell(Hunhandle *pHunspell, const char *word)
 {
         return ((Hunspell*)pHunspell)->spell(word);
@@ -1800,8 +1932,63 @@ char *Hunspell_get_dic_encoding(Hunhandl
 {
         return ((Hunspell*)pHunspell)->get_dic_encoding();
 }
 
 int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word