LCOV - code coverage report
Current view: directory - extensions/spellcheck/hunspell/src - hunspell.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 1029 303 29.4 %
Date: 2012-06-02 Functions: 55 11 20.0 %

       1                 : /******* BEGIN LICENSE BLOCK *******
       2                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       3                 :  * 
       4                 :  * The contents of this file are subject to the Mozilla Public License Version
       5                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       6                 :  * the License. You may obtain a copy of the License at
       7                 :  * http://www.mozilla.org/MPL/
       8                 :  * 
       9                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      10                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      11                 :  * for the specific language governing rights and limitations under the
      12                 :  * License.
      13                 :  * 
      14                 :  * The Initial Developers of the Original Code are Kevin Hendricks (MySpell)
      15                 :  * and László Németh (Hunspell). Portions created by the Initial Developers
      16                 :  * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved.
      17                 :  * 
      18                 :  * Contributor(s): Kevin Hendricks (kevin.hendricks@sympatico.ca)
      19                 :  *                 David Einstein (deinst@world.std.com)
      20                 :  *                 László Németh (nemethl@gyorsposta.hu)
      21                 :  *                 Caolan McNamara (caolanm@redhat.com)
      22                 :  *                 Davide Prina
      23                 :  *                 Giuseppe Modugno
      24                 :  *                 Gianluca Turconi
      25                 :  *                 Simon Brouwer
      26                 :  *                 Noll Janos
      27                 :  *                 Biro Arpad
      28                 :  *                 Goldman Eleonora
      29                 :  *                 Sarlos Tamas
      30                 :  *                 Bencsath Boldizsar
      31                 :  *                 Halacsy Peter
      32                 :  *                 Dvornik Laszlo
      33                 :  *                 Gefferth Andras
      34                 :  *                 Nagy Viktor
      35                 :  *                 Varga Daniel
      36                 :  *                 Chris Halls
      37                 :  *                 Rene Engelhard
      38                 :  *                 Bram Moolenaar
      39                 :  *                 Dafydd Jones
      40                 :  *                 Harri Pitkanen
      41                 :  *                 Andras Timar
      42                 :  *                 Tor Lillqvist
      43                 :  * 
      44                 :  * Alternatively, the contents of this file may be used under the terms of
      45                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      46                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      47                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      48                 :  * of those above. If you wish to allow use of your version of this file only
      49                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      50                 :  * use your version of this file under the terms of the MPL, indicate your
      51                 :  * decision by deleting the provisions above and replace them with the notice
      52                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      53                 :  * the provisions above, a recipient may use your version of this file under
      54                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      55                 :  *
      56                 :  ******* END LICENSE BLOCK *******/
      57                 : 
      58                 : #include <stdlib.h>
      59                 : #include <string.h>
      60                 : #include <stdio.h>
      61                 : 
      62                 : #include "hunspell.hxx"
      63                 : #include "hunspell.h"
      64                 : #ifndef MOZILLA_CLIENT
      65                 : #    include "config.h"
      66                 : #endif
      67                 : #include "csutil.hxx"
      68                 : 
      69             110 : Hunspell::Hunspell(const char * affpath, const char * dpath, const char * key)
      70                 : {
      71             110 :     encoding = NULL;
      72             110 :     csconv = NULL;
      73             110 :     utf8 = 0;
      74             110 :     complexprefixes = 0;
      75             110 :     affixpath = mystrdup(affpath);
      76             110 :     maxdic = 0;
      77                 : 
      78                 :     /* first set up the hash manager */
      79             110 :     pHMgr[0] = new HashMgr(dpath, affpath, key);
      80             110 :     if (pHMgr[0]) maxdic = 1;
      81                 : 
      82                 :     /* next set up the affix manager */
      83                 :     /* it needs access to the hash manager lookup methods */
      84             110 :     pAMgr = new AffixMgr(affpath, pHMgr, &maxdic, key);
      85                 : 
      86                 :     /* get the preferred try string and the dictionary */
      87                 :     /* encoding from the Affix Manager for that dictionary */
      88             110 :     char * try_string = pAMgr->get_try_string();
      89             110 :     encoding = pAMgr->get_encoding();
      90             110 :     langnum = pAMgr->get_langnum();
      91             110 :     utf8 = pAMgr->get_utf8();
      92             110 :     if (!utf8)
      93              82 :         csconv = get_current_cs(encoding);
      94             110 :     complexprefixes = pAMgr->get_complexprefixes();
      95             110 :     wordbreak = pAMgr->get_breaktable();
      96                 : 
      97                 :     /* and finally set up the suggestion manager */
      98             110 :     pSMgr = new SuggestMgr(try_string, MAXSUGGESTION, pAMgr);
      99             110 :     if (try_string) free(try_string);
     100             110 : }
     101                 : 
     102             110 : Hunspell::~Hunspell()
     103                 : {
     104             110 :     if (pSMgr) delete pSMgr;
     105             110 :     if (pAMgr) delete pAMgr;
     106             220 :     for (int i = 0; i < maxdic; i++) delete pHMgr[i];
     107             110 :     maxdic = 0;
     108             110 :     pSMgr = NULL;
     109             110 :     pAMgr = NULL;
     110                 : #ifdef MOZILLA_CLIENT
     111             110 :     delete [] csconv;
     112                 : #endif
     113             110 :     csconv= NULL;
     114             110 :     if (encoding) free(encoding);
     115             110 :     encoding = NULL;
     116             110 :     if (affixpath) free(affixpath);
     117             110 :     affixpath = NULL;
     118             110 : }
     119                 : 
     120                 : // load extra dictionaries
     121               0 : int Hunspell::add_dic(const char * dpath, const char * key) {
     122               0 :     if (maxdic == MAXDIC || !affixpath) return 1;
     123               0 :     pHMgr[maxdic] = new HashMgr(dpath, affixpath, key);
     124               0 :     if (pHMgr[maxdic]) maxdic++; else return 1;
     125               0 :     return 0;
     126                 : }
     127                 : 
     128                 : // make a copy of src at destination while removing all leading
     129                 : // blanks and removing any trailing periods after recording
     130                 : // their presence with the abbreviation flag
     131                 : // also since already going through character by character,
     132                 : // set the capitalization type
     133                 : // return the length of the "cleaned" (and UTF-8 encoded) word
     134                 : 
     135            1083 : int Hunspell::cleanword2(char * dest, const char * src,
     136                 :     w_char * dest_utf, int * nc, int * pcaptype, int * pabbrev)
     137                 : {
     138            1083 :    unsigned char * p = (unsigned char *) dest;
     139            1083 :    const unsigned char * q = (const unsigned char * ) src;
     140                 : 
     141                 :    // first skip over any leading blanks
     142            1083 :    while ((*q != '\0') && (*q == ' ')) q++;
     143                 : 
     144                 :    // now strip off any trailing periods (recording their presence)
     145            1083 :    *pabbrev = 0;
     146            1083 :    int nl = strlen((const char *)q);
     147            2188 :    while ((nl > 0) && (*(q+nl-1)=='.')) {
     148              22 :        nl--;
     149              22 :        (*pabbrev)++;
     150                 :    }
     151                 : 
     152                 :    // if no characters are left it can't be capitalized
     153            1083 :    if (nl <= 0) {
     154               2 :        *pcaptype = NOCAP;
     155               2 :        *p = '\0';
     156               2 :        return 0;
     157                 :    }
     158                 : 
     159            1081 :    strncpy(dest, (char *) q, nl);
     160            1081 :    *(dest + nl) = '\0';
     161            1081 :    nl = strlen(dest);
     162            1081 :    if (utf8) {
     163             236 :       *nc = u8_u16(dest_utf, MAXWORDLEN, dest);
     164                 :       // don't check too long words
     165             236 :       if (*nc >= MAXWORDLEN) return 0;
     166             236 :       if (*nc == -1) { // big Unicode character (non BMP area)
     167               5 :          *pcaptype = NOCAP;
     168               5 :          return nl;
     169                 :       }
     170             231 :      *pcaptype = get_captype_utf8(dest_utf, *nc, langnum);
     171                 :    } else {
     172             845 :      *pcaptype = get_captype(dest, nl, csconv);
     173             845 :      *nc = nl;
     174                 :    }
     175            1076 :    return nl;
     176                 : }
     177                 : 
     178               0 : int Hunspell::cleanword(char * dest, const char * src,
     179                 :     int * pcaptype, int * pabbrev)
     180                 : {
     181               0 :    unsigned char * p = (unsigned char *) dest;
     182               0 :    const unsigned char * q = (const unsigned char * ) src;
     183               0 :    int firstcap = 0;
     184                 : 
     185                 :    // first skip over any leading blanks
     186               0 :    while ((*q != '\0') && (*q == ' ')) q++;
     187                 : 
     188                 :    // now strip off any trailing periods (recording their presence)
     189               0 :    *pabbrev = 0;
     190               0 :    int nl = strlen((const char *)q);
     191               0 :    while ((nl > 0) && (*(q+nl-1)=='.')) {
     192               0 :        nl--;
     193               0 :        (*pabbrev)++;
     194                 :    }
     195                 : 
     196                 :    // if no characters are left it can't be capitalized
     197               0 :    if (nl <= 0) {
     198               0 :        *pcaptype = NOCAP;
     199               0 :        *p = '\0';
     200               0 :        return 0;
     201                 :    }
     202                 : 
     203                 :    // now determine the capitalization type of the first nl letters
     204               0 :    int ncap = 0;
     205               0 :    int nneutral = 0;
     206               0 :    int nc = 0;
     207                 : 
     208               0 :    if (!utf8) {
     209               0 :       while (nl > 0) {
     210               0 :          nc++;
     211               0 :          if (csconv[(*q)].ccase) ncap++;
     212               0 :          if (csconv[(*q)].cupper == csconv[(*q)].clower) nneutral++;
     213               0 :          *p++ = *q++;
     214               0 :          nl--;
     215                 :       }
     216                 :       // remember to terminate the destination string
     217               0 :       *p = '\0';
     218               0 :       firstcap = csconv[(unsigned char)(*dest)].ccase;
     219                 :    } else {
     220                 :       unsigned short idx;
     221                 :       w_char t[MAXWORDLEN];
     222               0 :       nc = u8_u16(t, MAXWORDLEN, src);
     223               0 :       for (int i = 0; i < nc; i++) {
     224               0 :          idx = (t[i].h << 8) + t[i].l;
     225               0 :          unsigned short low = unicodetolower(idx, langnum);
     226               0 :          if (idx != low) ncap++;
     227               0 :          if (unicodetoupper(idx, langnum) == low) nneutral++;
     228                 :       }
     229               0 :       u16_u8(dest, MAXWORDUTF8LEN, t, nc);
     230               0 :       if (ncap) {
     231               0 :          idx = (t[0].h << 8) + t[0].l;
     232               0 :          firstcap = (idx != unicodetolower(idx, langnum));
     233                 :       }
     234                 :    }
     235                 : 
     236                 :    // now finally set the captype
     237               0 :    if (ncap == 0) {
     238               0 :         *pcaptype = NOCAP;
     239               0 :    } else if ((ncap == 1) && firstcap) {
     240               0 :         *pcaptype = INITCAP;
     241               0 :    } else if ((ncap == nc) || ((ncap + nneutral) == nc)){
     242               0 :         *pcaptype = ALLCAP;
     243               0 :    } else if ((ncap > 1) && firstcap) {
     244               0 :         *pcaptype = HUHINITCAP;
     245                 :    } else {
     246               0 :         *pcaptype = HUHCAP;
     247                 :    }
     248               0 :    return strlen(dest);
     249                 : }
     250                 : 
     251               0 : void Hunspell::mkallcap(char * p)
     252                 : {
     253               0 :   if (utf8) {
     254                 :       w_char u[MAXWORDLEN];
     255               0 :       int nc = u8_u16(u, MAXWORDLEN, p);
     256                 :       unsigned short idx;
     257               0 :       for (int i = 0; i < nc; i++) {
     258               0 :          idx = (u[i].h << 8) + u[i].l;
     259               0 :          if (idx != unicodetoupper(idx, langnum)) {
     260               0 :             u[i].h = (unsigned char) (unicodetoupper(idx, langnum) >> 8);
     261               0 :             u[i].l = (unsigned char) (unicodetoupper(idx, langnum) & 0x00FF);
     262                 :          }
     263                 :       }
     264               0 :       u16_u8(p, MAXWORDUTF8LEN, u, nc);
     265                 :   } else {
     266               0 :     while (*p != '\0') {
     267               0 :         *p = csconv[((unsigned char) *p)].cupper;
     268               0 :         p++;
     269                 :     }
     270                 :   }
     271               0 : }
     272                 : 
     273               0 : int Hunspell::mkallcap2(char * p, w_char * u, int nc)
     274                 : {
     275               0 :   if (utf8) {
     276                 :       unsigned short idx;
     277               0 :       for (int i = 0; i < nc; i++) {
     278               0 :          idx = (u[i].h << 8) + u[i].l;
     279               0 :          unsigned short up = unicodetoupper(idx, langnum);
     280               0 :          if (idx != up) {
     281               0 :             u[i].h = (unsigned char) (up >> 8);
     282               0 :             u[i].l = (unsigned char) (up & 0x00FF);
     283                 :          }
     284                 :       }
     285               0 :       u16_u8(p, MAXWORDUTF8LEN, u, nc);
     286               0 :       return strlen(p);
     287                 :   } else {
     288               0 :     while (*p != '\0') {
     289               0 :         *p = csconv[((unsigned char) *p)].cupper;
     290               0 :         p++;
     291                 :     }
     292                 :   }
     293               0 :   return nc;
     294                 : }
     295                 : 
     296                 : 
     297               0 : void Hunspell::mkallsmall(char * p)
     298                 : {
     299               0 :     while (*p != '\0') {
     300               0 :         *p = csconv[((unsigned char) *p)].clower;
     301               0 :         p++;
     302                 :     }
     303               0 : }
     304                 : 
     305             145 : int Hunspell::mkallsmall2(char * p, w_char * u, int nc)
     306                 : {
     307             145 :   if (utf8) {
     308                 :       unsigned short idx;
     309             288 :       for (int i = 0; i < nc; i++) {
     310             256 :          idx = (u[i].h << 8) + u[i].l;
     311             256 :          unsigned short low = unicodetolower(idx, langnum);
     312             256 :          if (idx != low) {
     313             104 :             u[i].h = (unsigned char) (low >> 8);
     314             104 :             u[i].l = (unsigned char) (low & 0x00FF);
     315                 :          }
     316                 :       }
     317              32 :       u16_u8(p, MAXWORDUTF8LEN, u, nc);
     318              32 :       return strlen(p);
     319                 :   } else {
     320            1446 :     while (*p != '\0') {
     321            1220 :         *p = csconv[((unsigned char) *p)].clower;
     322            1220 :         p++;
     323                 :     }
     324                 :   }
     325             113 :   return nc;
     326                 : }
     327                 : 
     328                 : // convert UTF-8 sharp S codes to latin 1
     329              30 : char * Hunspell::sharps_u8_l1(char * dest, char * source) {
     330              30 :     char * p = dest;
     331              30 :     *p = *source;
     332             441 :     for (p++, source++; *(source - 1); p++, source++) {
     333             411 :         *p = *source;
     334             411 :         if (*source == '\x9F') *--p = '\xDF';
     335                 :     }
     336              30 :     return dest;
     337                 : }
     338                 : 
     339                 : // recursive search for right ss - sharp s permutations
     340             132 : hentry * Hunspell::spellsharps(char * base, char * pos, int n,
     341                 :         int repnum, char * tmp, int * info, char **root) {
     342             132 :     pos = strstr(pos, "ss");
     343             132 :     if (pos && (n < MAXSHARPS)) {
     344              60 :         *pos = '\xC3';
     345              60 :         *(pos + 1) = '\x9F';
     346              60 :         hentry * h = spellsharps(base, pos + 2, n + 1, repnum + 1, tmp, info, root);
     347              60 :         if (h) return h;
     348              50 :         *pos = 's';
     349              50 :         *(pos + 1) = 's';
     350              50 :         h = spellsharps(base, pos + 2, n + 1, repnum, tmp, info, root);
     351              50 :         if (h) return h;
     352              72 :     } else if (repnum > 0) {
     353              60 :         if (utf8) return checkword(base, info, root);
     354              30 :         return checkword(sharps_u8_l1(tmp, base), info, root);
     355                 :     }
     356              54 :     return NULL;
     357                 : }
     358                 : 
     359              85 : int Hunspell::is_keepcase(const hentry * rv) {
     360              51 :     return pAMgr && rv->astr && pAMgr->get_keepcase() &&
     361             136 :         TESTAFF(rv->astr, pAMgr->get_keepcase(), rv->alen);
     362                 : }
     363                 : 
     364                 : /* insert a word to the beginning of the suggestion array and return ns */
     365               0 : int Hunspell::insert_sug(char ***slst, char * word, int ns) {
     366               0 :     char * dup = mystrdup(word);
     367               0 :     if (!dup) return ns;
     368               0 :     if (ns == MAXSUGGESTION) {
     369               0 :         ns--;
     370               0 :         free((*slst)[ns]);
     371                 :     }
     372               0 :     for (int k = ns; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
     373               0 :     (*slst)[0] = dup;
     374               0 :     return ns + 1;
     375                 : }
     376                 : 
     377            1083 : int Hunspell::spell(const char * word, int * info, char ** root)
     378                 : {
     379            1083 :   struct hentry * rv=NULL;
     380                 :   // need larger vector. For example, Turkish capital letter I converted a
     381                 :   // 2-byte UTF-8 character (dotless i) by mkallsmall.
     382                 :   char cw[MAXWORDUTF8LEN];
     383                 :   char wspace[MAXWORDUTF8LEN];
     384                 :   w_char unicw[MAXWORDLEN];
     385                 :   // Hunspell supports XML input of the simplified API (see manual)
     386            1083 :   if (strcmp(word, SPELL_XML) == 0) return 1;
     387            1083 :   int nc = strlen(word);
     388            1083 :   int wl2 = 0;
     389            1083 :   if (utf8) {
     390             236 :     if (nc >= MAXWORDUTF8LEN) return 0;
     391                 :   } else {
     392             847 :     if (nc >= MAXWORDLEN) return 0;
     393                 :   }
     394            1083 :   int captype = 0;
     395            1083 :   int abbv = 0;
     396            1083 :   int wl = 0;
     397                 : 
     398                 :   // input conversion
     399            1083 :   RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
     400            1083 :   if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
     401            1080 :   else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
     402                 : 
     403            1083 :   int info2 = 0;
     404            1083 :   if (wl == 0 || maxdic == 0) return 1;
     405            1081 :   if (root) *root = NULL;
     406                 : 
     407                 :   // allow numbers with dots, dashes and commas (but forbid double separators: "..", "--" etc.)
     408                 :   enum { NBEGIN, NNUM, NSEP };
     409            1081 :   int nstate = NBEGIN;
     410                 :   int i;
     411                 : 
     412            1395 :   for (i = 0; (i < wl); i++) {
     413            1375 :     if ((cw[i] <= '9') && (cw[i] >= '0')) {
     414             297 :         nstate = NNUM;
     415            1078 :     } else if ((cw[i] == ',') || (cw[i] == '.') || (cw[i] == '-')) {
     416              23 :         if ((nstate == NSEP) || (i == 0)) break;
     417              17 :         nstate = NSEP;
     418            1055 :     } else break;
     419                 :   }
     420            1081 :   if ((i == wl) && (nstate == NNUM)) return 1;
     421            1061 :   if (!info) info = &info2; else *info = 0;
     422                 : 
     423            1061 :   switch(captype) {
     424                 :      case HUHCAP:
     425                 :      case HUHINITCAP:
     426              69 :             *info += SPELL_ORIGCAP;
     427                 :      case NOCAP: {
     428             909 :             rv = checkword(cw, info, root);
     429             909 :             if ((abbv) && !(rv)) {
     430               2 :                 memcpy(wspace,cw,wl);
     431               2 :                 *(wspace+wl) = '.';
     432               2 :                 *(wspace+wl+1) = '\0';
     433               2 :                 rv = checkword(wspace, info, root);
     434                 :             }
     435             909 :             break;
     436                 :          }
     437                 :      case ALLCAP: {
     438              36 :             *info += SPELL_ORIGCAP;
     439              36 :             rv = checkword(cw, info, root);
     440              36 :             if (rv) break;
     441              27 :             if (abbv) {
     442               5 :                 memcpy(wspace,cw,wl);
     443               5 :                 *(wspace+wl) = '.';
     444               5 :                 *(wspace+wl+1) = '\0';
     445               5 :                 rv = checkword(wspace, info, root);
     446               5 :                 if (rv) break;
     447                 :             }
     448                 :             // Spec. prefix handling for Catalan, French, Italian:
     449                 :             // prefixes separated by apostrophe (SANT'ELIA -> Sant'+Elia).
     450              27 :             if (pAMgr && strchr(cw, '\'')) {
     451               2 :                 wl = mkallsmall2(cw, unicw, nc);
     452                 :                 //There are no really sane circumstances where this could fail,
     453                 :                 //but anyway...
     454               2 :                 if (char * apostrophe = strchr(cw, '\'')) {
     455               2 :                     if (utf8) {
     456                 :                         w_char tmpword[MAXWORDLEN];
     457               0 :                         *apostrophe = '\0';
     458               0 :                         wl2 = u8_u16(tmpword, MAXWORDLEN, cw);
     459               0 :                         *apostrophe = '\'';
     460               0 :                         if (wl2 < nc) {
     461               0 :                             mkinitcap2(apostrophe + 1, unicw + wl2 + 1, nc - wl2 - 1);
     462               0 :                             rv = checkword(cw, info, root);
     463               0 :                             if (rv) break;
     464                 :                         }
     465                 :                     } else {
     466               2 :                         mkinitcap2(apostrophe + 1, unicw, nc);
     467               2 :                         rv = checkword(cw, info, root);
     468               2 :                         if (rv) break;
     469                 :                     }
     470                 :                 }
     471               2 :                 mkinitcap2(cw, unicw, nc);
     472               2 :                 rv = checkword(cw, info, root);
     473               2 :                 if (rv) break;
     474                 :             }
     475              27 :             if (pAMgr && pAMgr->get_checksharps() && strstr(cw, "SS")) {
     476                 :                 char tmpword[MAXWORDUTF8LEN];
     477              10 :                 wl = mkallsmall2(cw, unicw, nc);
     478              10 :                 memcpy(wspace,cw,(wl+1));
     479              10 :                 rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
     480              10 :                 if (!rv) {
     481               8 :                     wl2 = mkinitcap2(cw, unicw, nc);
     482               8 :                     rv = spellsharps(cw, cw, 0, 0, tmpword, info, root);
     483                 :                 }
     484              10 :                 if ((abbv) && !(rv)) {
     485               2 :                     *(wspace+wl) = '.';
     486               2 :                     *(wspace+wl+1) = '\0';
     487               2 :                     rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
     488               2 :                     if (!rv) {
     489               2 :                         memcpy(wspace, cw, wl2);
     490               2 :                         *(wspace+wl2) = '.';
     491               2 :                         *(wspace+wl2+1) = '\0';
     492               2 :                         rv = spellsharps(wspace, wspace, 0, 0, tmpword, info, root);
     493                 :                     }
     494                 :                 }
     495              10 :                 if (rv) break;
     496                 :             }
     497                 :         }
     498                 :      case INITCAP: {
     499             133 :              *info += SPELL_ORIGCAP;
     500             133 :              wl = mkallsmall2(cw, unicw, nc);
     501             133 :              memcpy(wspace,cw,(wl+1));
     502             133 :              wl2 = mkinitcap2(cw, unicw, nc);
     503             133 :              if (captype == INITCAP) *info += SPELL_INITCAP;
     504             133 :              rv = checkword(cw, info, root);
     505             133 :              if (captype == INITCAP) *info -= SPELL_INITCAP;
     506                 :              // forbid bad capitalization
     507                 :              // (for example, ijs -> Ijs instead of IJs in Dutch)
     508                 :              // use explicit forms in dic: Ijs/F (F = FORBIDDENWORD flag)
     509             133 :              if (*info & SPELL_FORBIDDEN) {
     510               2 :                 rv = NULL;
     511               2 :                 break;
     512                 :              }
     513             131 :              if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL;
     514             131 :              if (rv) break;
     515                 : 
     516              73 :              rv = checkword(wspace, info, root);
     517              73 :              if (abbv && !rv) {
     518                 : 
     519               4 :                  *(wspace+wl) = '.';
     520               4 :                  *(wspace+wl+1) = '\0';
     521               4 :                  rv = checkword(wspace, info, root);
     522               4 :                  if (!rv) {
     523               2 :                     memcpy(wspace, cw, wl2);
     524               2 :                     *(wspace+wl2) = '.';
     525               2 :                     *(wspace+wl2+1) = '\0';
     526               2 :                     if (captype == INITCAP) *info += SPELL_INITCAP;
     527               2 :                     rv = checkword(wspace, info, root);
     528               2 :                     if (captype == INITCAP) *info -= SPELL_INITCAP;
     529               2 :                     if (rv && is_keepcase(rv) && (captype == ALLCAP)) rv = NULL;
     530               2 :                     break;
     531                 :                  }
     532                 :              }
     533              81 :              if (rv && is_keepcase(rv) &&
     534                 :                 ((captype == ALLCAP) ||
     535                 :                    // if CHECKSHARPS: KEEPCASE words with \xDF  are allowed
     536                 :                    // in INITCAP form, too.
     537               5 :                    !(pAMgr->get_checksharps() &&
     538               1 :                       ((utf8 && strstr(wspace, "\xC3\x9F")) ||
     539               8 :                       (!utf8 && strchr(wspace, '\xDF')))))) rv = NULL;
     540              71 :              break;
     541                 :            }
     542                 :   }
     543                 : 
     544            1061 :   if (rv) {
     545             557 :       if (pAMgr && pAMgr->get_warn() && rv->astr &&
     546               1 :           TESTAFF(rv->astr, pAMgr->get_warn(), rv->alen)) {
     547               1 :               *info += SPELL_WARN;
     548               1 :               if (pAMgr->get_forbidwarn()) return 0;
     549               1 :               return HUNSPELL_OK_WARN;
     550                 :       }
     551             555 :       return HUNSPELL_OK;
     552                 :   }
     553                 : 
     554                 :   // recursive breaking at break points
     555             505 :   if (wordbreak) {
     556                 :     char * s;
     557                 :     char r;
     558             501 :     int nbr = 0;
     559             501 :     wl = strlen(cw);
     560             501 :     int numbreak = pAMgr ? pAMgr->get_numbreak() : 0;
     561                 : 
     562                 :     // calculate break points for recursion limit
     563            1957 :     for (int j = 0; j < numbreak; j++) {
     564            1456 :       s = cw;
     565            1504 :       do {
     566            1504 :         s = (char *) strstr(s, wordbreak[j]);
     567            1504 :         if (s) { 
     568              48 :                 nbr++;
     569              48 :                 s++;
     570                 :         }
     571                 :       } while (s);
     572                 :     } 
     573             501 :     if (nbr >= 10) return 0;
     574                 : 
     575                 :     // check boundary patterns (^begin and end$)
     576            1954 :     for (int j = 0; j < numbreak; j++) {
     577            1455 :       int plen = strlen(wordbreak[j]);
     578            1455 :       if (plen == 1 || plen > wl) continue;
     579             947 :       if (wordbreak[j][0] == '^' && strncmp(cw, wordbreak[j] + 1, plen - 1) == 0
     580               2 :         && spell(cw + plen - 1)) return 1;
     581            1401 :       if (wordbreak[j][plen - 1] == '$' &&
     582             456 :         strncmp(cw + wl - plen + 1, wordbreak[j], plen - 1) == 0) {
     583               5 :             r = cw[wl - plen + 1];
     584               5 :             cw[wl - plen + 1] = '\0';
     585               5 :             if (spell(cw)) return 1;
     586               4 :             cw[wl - plen + 1] = r;
     587                 :         }
     588                 :     }
     589                 : 
     590                 :     // other patterns
     591            1935 :     for (int j = 0; j < numbreak; j++) {
     592            1443 :       int plen = strlen(wordbreak[j]);
     593            1443 :       s=(char *) strstr(cw, wordbreak[j]);
     594            1443 :       if (s && (s > cw) && (s < cw + wl - plen)) {
     595              30 :         if (!spell(s + plen)) continue;
     596              17 :         r = *s;
     597              17 :         *s = '\0';
     598                 :         // examine 2 sides of the break point
     599              17 :         if (spell(cw)) return 1;
     600              10 :         *s = r;
     601                 : 
     602                 :         // LANG_hu: spec. dash rule
     603              10 :         if (langnum == LANG_hu && strcmp(wordbreak[j], "-") == 0) {
     604               0 :           r = s[1];
     605               0 :           s[1] = '\0';
     606               0 :           if (spell(cw)) return 1; // check the first part with dash
     607               0 :           s[1] = r;
     608                 :         }
     609                 :         // end of LANG speficic region
     610                 : 
     611                 :       }
     612                 :     }
     613                 :   }
     614                 : 
     615             496 :   return 0;
     616                 : }
     617                 : 
     618            1228 : struct hentry * Hunspell::checkword(const char * w, int * info, char ** root)
     619                 : {
     620            1228 :   struct hentry * he = NULL;
     621                 :   int len, i;
     622                 :   char w2[MAXWORDUTF8LEN];
     623                 :   const char * word;
     624                 : 
     625            1228 :   char * ignoredchars = pAMgr->get_ignore();
     626            1228 :   if (ignoredchars != NULL) {
     627              15 :      strcpy(w2, w);
     628              15 :      if (utf8) {
     629                 :         int ignoredchars_utf16_len;
     630              10 :         unsigned short * ignoredchars_utf16 = pAMgr->get_ignore_utf16(&ignoredchars_utf16_len);
     631              10 :         remove_ignored_chars_utf(w2, ignoredchars_utf16, ignoredchars_utf16_len);
     632                 :      } else {
     633               5 :         remove_ignored_chars(w2,ignoredchars);
     634                 :      }
     635              15 :      word = w2;
     636            1213 :   } else word = w;
     637                 : 
     638            1228 :   len = strlen(word);
     639                 : 
     640            1228 :   if (!len)
     641               0 :       return NULL;
     642                 : 
     643                 :   // word reversing wrapper for complex prefixes
     644            1228 :   if (complexprefixes) {
     645              12 :     if (word != w2) {
     646              12 :       strcpy(w2, word);
     647              12 :       word = w2;
     648                 :     }
     649              12 :     if (utf8) reverseword_utf(w2); else reverseword(w2);
     650                 :   }
     651                 : 
     652                 :   // look word in hash table
     653            2450 :   for (i = 0; (i < maxdic) && !he; i ++) {
     654            1228 :   he = (pHMgr[i])->lookup(word);
     655                 : 
     656                 :   // check forbidden and onlyincompound words
     657            1228 :   if ((he) && (he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
     658               6 :     if (info) *info += SPELL_FORBIDDEN;
     659                 :     // LANG_hu section: set dash information for suggestions
     660               6 :     if (langnum == LANG_hu) {
     661               0 :         if (pAMgr->get_compoundflag() &&
     662               0 :             TESTAFF(he->astr, pAMgr->get_compoundflag(), he->alen)) {
     663               0 :                 if (info) *info += SPELL_COMPOUND;
     664                 :         }
     665                 :     }
     666               6 :     return NULL;
     667                 :   }
     668                 : 
     669                 :   // he = next not needaffix, onlyincompound homonym or onlyupcase word
     670            2810 :   while (he && (he->astr) &&
     671             146 :     ((pAMgr->get_needaffix() && TESTAFF(he->astr, pAMgr->get_needaffix(), he->alen)) ||
     672             183 :        (pAMgr->get_onlyincompound() && TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) ||
     673              16 :        (info && (*info & SPELL_INITCAP) && TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen))
     674              21 :     )) he = he->next_homonym;
     675                 :   }
     676                 : 
     677                 :   // check with affixes
     678            1222 :   if (!he && pAMgr) {
     679                 :      // try stripping off affixes */
     680             992 :      he = pAMgr->affix_check(word, len, 0);
     681                 : 
     682                 :      // check compound restriction and onlyupcase
     683            1142 :      if (he && he->astr && (
     684             144 :         (pAMgr->get_onlyincompound() &&
     685               2 :             TESTAFF(he->astr, pAMgr->get_onlyincompound(), he->alen)) ||
     686                 :         (info && (*info & SPELL_INITCAP) &&
     687               4 :             TESTAFF(he->astr, ONLYUPCASEFLAG, he->alen)))) {
     688               0 :             he = NULL;
     689                 :      }
     690                 : 
     691             992 :      if (he) {
     692             144 :         if ((he->astr) && (pAMgr) && TESTAFF(he->astr, pAMgr->get_forbiddenword(), he->alen)) {
     693               2 :             if (info) *info += SPELL_FORBIDDEN;
     694               2 :             return NULL;
     695                 :         }
     696             142 :         if (root) {
     697               0 :             *root = mystrdup(he->word);
     698               0 :             if (*root && complexprefixes) {
     699               0 :                 if (utf8) reverseword_utf(*root); else reverseword(*root);
     700                 :             }
     701                 :         }
     702                 :      // try check compound word
     703             848 :      } else if (pAMgr->get_compound()) {
     704             470 :           he = pAMgr->compound_check(word, len, 0, 0, 100, 0, NULL, 0, 0, info);
     705                 :           // LANG_hu section: `moving rule' with last dash
     706             470 :           if ((!he) && (langnum == LANG_hu) && (word[len-1] == '-')) {
     707               0 :              char * dup = mystrdup(word);
     708               0 :              if (!dup) return NULL;
     709               0 :              dup[len-1] = '\0';
     710               0 :              he = pAMgr->compound_check(dup, len-1, -5, 0, 100, 0, NULL, 1, 0, info);
     711               0 :              free(dup);
     712                 :           }
     713                 :           // end of LANG speficic region
     714             470 :           if (he) {
     715             190 :                 if (root) {
     716               0 :                     *root = mystrdup(he->word);
     717               0 :                     if (*root && complexprefixes) {
     718               0 :                         if (utf8) reverseword_utf(*root); else reverseword(*root);
     719                 :                     }
     720                 :                 }
     721             190 :                 if (info) *info += SPELL_COMPOUND;
     722                 :           }
     723                 :      }
     724                 : 
     725                 :   }
     726                 : 
     727            1220 :   return he;
     728                 : }
     729                 : 
     730               0 : int Hunspell::suggest(char*** slst, const char * word)
     731                 : {
     732               0 :   int onlycmpdsug = 0;
     733                 :   char cw[MAXWORDUTF8LEN];
     734                 :   char wspace[MAXWORDUTF8LEN];
     735               0 :   if (!pSMgr || maxdic == 0) return 0;
     736                 :   w_char unicw[MAXWORDLEN];
     737               0 :   *slst = NULL;
     738                 :   // process XML input of the simplified API (see manual)
     739               0 :   if (strncmp(word, SPELL_XML, sizeof(SPELL_XML) - 3) == 0) {
     740               0 :      return spellml(slst, word);
     741                 :   }
     742               0 :   int nc = strlen(word);
     743               0 :   if (utf8) {
     744               0 :     if (nc >= MAXWORDUTF8LEN) return 0;
     745                 :   } else {
     746               0 :     if (nc >= MAXWORDLEN) return 0;
     747                 :   }
     748               0 :   int captype = 0;
     749               0 :   int abbv = 0;
     750               0 :   int wl = 0;
     751                 : 
     752                 :   // input conversion
     753               0 :   RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
     754               0 :   if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
     755               0 :   else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
     756                 : 
     757               0 :   if (wl == 0) return 0;
     758               0 :   int ns = 0;
     759               0 :   int capwords = 0;
     760                 : 
     761                 :   // check capitalized form for FORCEUCASE
     762               0 :   if (pAMgr && captype == NOCAP && pAMgr->get_forceucase()) {
     763               0 :     int info = SPELL_ORIGCAP;
     764                 :     char ** wlst;
     765               0 :     if (checkword(cw, &info, NULL)) {
     766               0 :         if (*slst) {
     767               0 :             wlst = *slst;
     768                 :         } else {
     769               0 :             wlst = (char **) malloc(MAXSUGGESTION * sizeof(char *));
     770               0 :             if (wlst == NULL) return -1;
     771               0 :             *slst = wlst;
     772               0 :             for (int i = 0; i < MAXSUGGESTION; i++) {
     773               0 :                 wlst[i] = NULL;
     774                 :             }
     775                 :         }
     776               0 :         wlst[0] = mystrdup(cw);
     777               0 :         mkinitcap(wlst[0]);
     778               0 :         return 1;
     779                 :     }
     780                 :   }
     781                 :  
     782               0 :   switch(captype) {
     783                 :      case NOCAP:   {
     784               0 :                      ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
     785               0 :                      break;
     786                 :                    }
     787                 : 
     788                 :      case INITCAP: {
     789               0 :                      capwords = 1;
     790               0 :                      ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
     791               0 :                      if (ns == -1) break;
     792               0 :                      memcpy(wspace,cw,(wl+1));
     793               0 :                      mkallsmall2(wspace, unicw, nc);
     794               0 :                      ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
     795               0 :                      break;
     796                 :                    }
     797                 :      case HUHINITCAP:
     798               0 :                     capwords = 1;
     799                 :      case HUHCAP: {
     800               0 :                      ns = pSMgr->suggest(slst, cw, ns, &onlycmpdsug);
     801               0 :                      if (ns != -1) {
     802                 :                         int prevns;
     803                 :                         // something.The -> something. The
     804               0 :                         char * dot = strchr(cw, '.');
     805               0 :                         if (dot && (dot > cw)) {
     806                 :                             int captype_;
     807               0 :                             if (utf8) {
     808                 :                                w_char w_[MAXWORDLEN];
     809               0 :                                int wl_ = u8_u16(w_, MAXWORDLEN, dot + 1);
     810               0 :                                captype_ = get_captype_utf8(w_, wl_, langnum);
     811               0 :                             } else captype_ = get_captype(dot+1, strlen(dot+1), csconv);
     812               0 :                             if (captype_ == INITCAP) {
     813               0 :                                 char * st = mystrdup(cw);
     814               0 :                                 if (st) st = (char *) realloc(st, wl + 2);
     815               0 :                                 if (st) {
     816               0 :                                         st[(dot - cw) + 1] = ' ';
     817               0 :                                         strcpy(st + (dot - cw) + 2, dot + 1);
     818               0 :                                         ns = insert_sug(slst, st, ns);
     819               0 :                                         free(st);
     820                 :                                 }
     821                 :                             }
     822                 :                         }
     823               0 :                         if (captype == HUHINITCAP) {
     824                 :                             // TheOpenOffice.org -> The OpenOffice.org
     825               0 :                             memcpy(wspace,cw,(wl+1));
     826               0 :                             mkinitsmall2(wspace, unicw, nc);
     827               0 :                             ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
     828                 :                         }
     829               0 :                         memcpy(wspace,cw,(wl+1));
     830               0 :                         mkallsmall2(wspace, unicw, nc);
     831               0 :                         if (spell(wspace)) ns = insert_sug(slst, wspace, ns);
     832               0 :                         prevns = ns;
     833               0 :                         ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
     834               0 :                         if (captype == HUHINITCAP) {
     835               0 :                             mkinitcap2(wspace, unicw, nc);
     836               0 :                             if (spell(wspace)) ns = insert_sug(slst, wspace, ns);
     837               0 :                             ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
     838                 :                         }
     839                 :                         // aNew -> "a New" (instead of "a new")
     840               0 :                         for (int j = prevns; j < ns; j++) {
     841               0 :                            char * space = strchr((*slst)[j],' ');
     842               0 :                            if (space) {
     843               0 :                                 int slen = strlen(space + 1);
     844                 :                                 // different case after space (need capitalisation)
     845               0 :                                 if ((slen < wl) && strcmp(cw + wl - slen, space + 1)) {
     846                 :                                     w_char w[MAXWORDLEN];
     847               0 :                                     int wc = 0;
     848               0 :                                     char * r = (*slst)[j];
     849               0 :                                     if (utf8) wc = u8_u16(w, MAXWORDLEN, space + 1);
     850               0 :                                     mkinitcap2(space + 1, w, wc);
     851                 :                                     // set as first suggestion
     852               0 :                                     for (int k = j; k > 0; k--) (*slst)[k] = (*slst)[k - 1];
     853               0 :                                     (*slst)[0] = r;
     854                 :                                 }
     855                 :                            }
     856                 :                         }
     857                 :                      }
     858               0 :                      break;
     859                 :                    }
     860                 : 
     861                 :      case ALLCAP: {
     862               0 :                      memcpy(wspace, cw, (wl+1));
     863               0 :                      mkallsmall2(wspace, unicw, nc);
     864               0 :                      ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
     865               0 :                      if (ns == -1) break;
     866               0 :                      if (pAMgr && pAMgr->get_keepcase() && spell(wspace))
     867               0 :                         ns = insert_sug(slst, wspace, ns);
     868               0 :                      mkinitcap2(wspace, unicw, nc);
     869               0 :                      ns = pSMgr->suggest(slst, wspace, ns, &onlycmpdsug);
     870               0 :                      for (int j=0; j < ns; j++) {
     871               0 :                         mkallcap((*slst)[j]);
     872               0 :                         if (pAMgr && pAMgr->get_checksharps()) {
     873                 :                             char * pos;
     874               0 :                             if (utf8) {
     875               0 :                                 pos = strstr((*slst)[j], "\xC3\x9F");
     876               0 :                                 while (pos) {
     877               0 :                                     *pos = 'S';
     878               0 :                                     *(pos+1) = 'S';
     879               0 :                                     pos = strstr(pos+2, "\xC3\x9F");
     880                 :                                 }
     881                 :                             } else {
     882               0 :                                 pos = strchr((*slst)[j], '\xDF');
     883               0 :                                 while (pos) {
     884               0 :                                     (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 2);
     885               0 :                                     mystrrep((*slst)[j], "\xDF", "SS");
     886               0 :                                     pos = strchr((*slst)[j], '\xDF');
     887                 :                                 }
     888                 :                             }
     889                 :                         }
     890                 :                      }
     891               0 :                      break;
     892                 :                    }
     893                 :   }
     894                 : 
     895                 :  // LANG_hu section: replace '-' with ' ' in Hungarian
     896               0 :   if (langnum == LANG_hu) {
     897               0 :       for (int j=0; j < ns; j++) {
     898               0 :           char * pos = strchr((*slst)[j],'-');
     899               0 :           if (pos) {
     900                 :               int info;
     901                 :               char w[MAXWORDUTF8LEN];
     902               0 :               *pos = '\0';
     903               0 :               strcpy(w, (*slst)[j]);
     904               0 :               strcat(w, pos + 1);
     905               0 :               spell(w, &info, NULL);
     906               0 :               if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
     907               0 :                   *pos = ' ';
     908               0 :               } else *pos = '-';
     909                 :           }
     910                 :       }
     911                 :   }
     912                 :   // END OF LANG_hu section
     913                 : 
     914                 :   // try ngram approach since found nothing or only compound words
     915               0 :   if (pAMgr && (ns == 0 || onlycmpdsug) && (pAMgr->get_maxngramsugs() != 0) && (*slst)) {
     916               0 :       switch(captype) {
     917                 :           case NOCAP: {
     918               0 :               ns = pSMgr->ngsuggest(*slst, cw, ns, pHMgr, maxdic);
     919               0 :               break;
     920                 :           }
     921                 :           case HUHINITCAP:
     922               0 :               capwords = 1;
     923                 :           case HUHCAP: {
     924               0 :               memcpy(wspace,cw,(wl+1));
     925               0 :               mkallsmall2(wspace, unicw, nc);
     926               0 :               ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
     927               0 :               break;
     928                 :           }
     929                 :          case INITCAP: {
     930               0 :               capwords = 1;
     931               0 :               memcpy(wspace,cw,(wl+1));
     932               0 :               mkallsmall2(wspace, unicw, nc);
     933               0 :               ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
     934               0 :               break;
     935                 :           }
     936                 :           case ALLCAP: {
     937               0 :               memcpy(wspace,cw,(wl+1));
     938               0 :               mkallsmall2(wspace, unicw, nc);
     939               0 :               int oldns = ns;
     940               0 :               ns = pSMgr->ngsuggest(*slst, wspace, ns, pHMgr, maxdic);
     941               0 :               for (int j = oldns; j < ns; j++)
     942               0 :                   mkallcap((*slst)[j]);
     943               0 :               break;
     944                 :          }
     945                 :       }
     946                 :   }
     947                 : 
     948                 :   // try dash suggestion (Afo-American -> Afro-American)
     949               0 :   if (char * pos = strchr(cw, '-')) {
     950               0 :      char * ppos = cw;
     951               0 :      int nodashsug = 1;
     952               0 :      char ** nlst = NULL;
     953               0 :      int nn = 0;
     954               0 :      int last = 0;
     955               0 :      if (*slst) {
     956               0 :         for (int j = 0; j < ns && nodashsug == 1; j++) {
     957               0 :            if (strchr((*slst)[j], '-')) nodashsug = 0;
     958                 :         }
     959                 :      }
     960               0 :      while (nodashsug && !last) {
     961               0 :         if (*pos == '\0') last = 1; else *pos = '\0';
     962               0 :         if (!spell(ppos)) {
     963               0 :           nn = suggest(&nlst, ppos);
     964               0 :           for (int j = nn - 1; j >= 0; j--) {
     965               0 :             strncpy(wspace, cw, ppos - cw);
     966               0 :             strcpy(wspace + (ppos - cw), nlst[j]);
     967               0 :             if (!last) {
     968               0 :                 strcat(wspace, "-");
     969               0 :                 strcat(wspace, pos + 1);
     970                 :             }
     971               0 :             ns = insert_sug(slst, wspace, ns);
     972               0 :             free(nlst[j]);
     973                 :           }
     974               0 :           if (nlst != NULL) free(nlst);
     975               0 :           nodashsug = 0;
     976                 :         }
     977               0 :         if (!last) {
     978               0 :           *pos = '-';
     979               0 :           ppos = pos + 1;
     980               0 :           pos = strchr(ppos, '-');
     981                 :         }
     982               0 :         if (!pos) pos = cw + strlen(cw);
     983                 :      }
     984                 :   }
     985                 : 
     986                 :   // word reversing wrapper for complex prefixes
     987               0 :   if (complexprefixes) {
     988               0 :     for (int j = 0; j < ns; j++) {
     989               0 :       if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
     990                 :     }
     991                 :   }
     992                 : 
     993                 :   // capitalize
     994               0 :   if (capwords) for (int j=0; j < ns; j++) {
     995               0 :       mkinitcap((*slst)[j]);
     996                 :   }
     997                 : 
     998                 :   // expand suggestions with dot(s)
     999               0 :   if (abbv && pAMgr && pAMgr->get_sugswithdots()) {
    1000               0 :     for (int j = 0; j < ns; j++) {
    1001               0 :       (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);
    1002               0 :       strcat((*slst)[j], word + strlen(word) - abbv);
    1003                 :     }
    1004                 :   }
    1005                 : 
    1006                 :   // remove bad capitalized and forbidden forms
    1007               0 :   if (pAMgr && (pAMgr->get_keepcase() || pAMgr->get_forbiddenword())) {
    1008               0 :   switch (captype) {
    1009                 :     case INITCAP:
    1010                 :     case ALLCAP: {
    1011               0 :       int l = 0;
    1012               0 :       for (int j=0; j < ns; j++) {
    1013               0 :         if (!strchr((*slst)[j],' ') && !spell((*slst)[j])) {
    1014                 :           char s[MAXSWUTF8L];
    1015                 :           w_char w[MAXSWL];
    1016                 :           int len;
    1017               0 :           if (utf8) {
    1018               0 :             len = u8_u16(w, MAXSWL, (*slst)[j]);
    1019                 :           } else {
    1020               0 :             strcpy(s, (*slst)[j]);
    1021               0 :             len = strlen(s);
    1022                 :           }
    1023               0 :           mkallsmall2(s, w, len);
    1024               0 :           free((*slst)[j]);
    1025               0 :           if (spell(s)) {
    1026               0 :             (*slst)[l] = mystrdup(s);
    1027               0 :             if ((*slst)[l]) l++;
    1028                 :           } else {
    1029               0 :             mkinitcap2(s, w, len);
    1030               0 :             if (spell(s)) {
    1031               0 :               (*slst)[l] = mystrdup(s);
    1032               0 :               if ((*slst)[l]) l++;
    1033                 :             }
    1034                 :           }
    1035                 :         } else {
    1036               0 :           (*slst)[l] = (*slst)[j];
    1037               0 :           l++;
    1038                 :         }
    1039                 :       }
    1040               0 :       ns = l;
    1041                 :     }
    1042                 :   }
    1043                 :   }
    1044                 : 
    1045                 :   // remove duplications
    1046               0 :   int l = 0;
    1047               0 :   for (int j = 0; j < ns; j++) {
    1048               0 :     (*slst)[l] = (*slst)[j];
    1049               0 :     for (int k = 0; k < l; k++) {
    1050               0 :       if (strcmp((*slst)[k], (*slst)[j]) == 0) {
    1051               0 :         free((*slst)[j]);
    1052               0 :         l--;
    1053               0 :         break;
    1054                 :       }
    1055                 :     }
    1056               0 :     l++;
    1057                 :   }
    1058               0 :   ns = l;
    1059                 : 
    1060                 :   // output conversion
    1061               0 :   rl = (pAMgr) ? pAMgr->get_oconvtable() : NULL;
    1062               0 :   for (int j = 0; rl && j < ns; j++) {
    1063               0 :     if (rl->conv((*slst)[j], wspace)) {
    1064               0 :       free((*slst)[j]);
    1065               0 :       (*slst)[j] = mystrdup(wspace);
    1066                 :     }
    1067                 :   }
    1068                 : 
    1069                 :   // if suggestions removed by nosuggest, onlyincompound parameters
    1070               0 :   if (l == 0 && *slst) {
    1071               0 :     free(*slst);
    1072               0 :     *slst = NULL;
    1073                 :   }
    1074               0 :   return l;
    1075                 : }
    1076                 : 
    1077               0 : void Hunspell::free_list(char *** slst, int n) {
    1078               0 :         freelist(slst, n);
    1079               0 : }
    1080                 : 
    1081             220 : char * Hunspell::get_dic_encoding()
    1082                 : {
    1083             220 :   return encoding;
    1084                 : }
    1085                 : 
    1086                 : #ifdef HUNSPELL_EXPERIMENTAL
    1087                 : // XXX need UTF-8 support
    1088                 : int Hunspell::suggest_auto(char*** slst, const char * word)
    1089                 : {
    1090                 :   char cw[MAXWORDUTF8LEN];
    1091                 :   char wspace[MAXWORDUTF8LEN];
    1092                 :   if (!pSMgr || maxdic == 0) return 0;
    1093                 :   int wl = strlen(word);
    1094                 :   if (utf8) {
    1095                 :     if (wl >= MAXWORDUTF8LEN) return 0;
    1096                 :   } else {
    1097                 :     if (wl >= MAXWORDLEN) return 0;
    1098                 :   }
    1099                 :   int captype = 0;
    1100                 :   int abbv = 0;
    1101                 :   wl = cleanword(cw, word, &captype, &abbv);
    1102                 :   if (wl == 0) return 0;
    1103                 :   int ns = 0;
    1104                 :   *slst = NULL; // HU, nsug in pSMgr->suggest
    1105                 : 
    1106                 :   switch(captype) {
    1107                 :      case NOCAP:   {
    1108                 :                      ns = pSMgr->suggest_auto(slst, cw, ns);
    1109                 :                      if (ns>0) break;
    1110                 :                      break;
    1111                 :                    }
    1112                 : 
    1113                 :      case INITCAP: {
    1114                 :                      memcpy(wspace,cw,(wl+1));
    1115                 :                      mkallsmall(wspace);
    1116                 :                      ns = pSMgr->suggest_auto(slst, wspace, ns);
    1117                 :                      for (int j=0; j < ns; j++)
    1118                 :                        mkinitcap((*slst)[j]);
    1119                 :                      ns = pSMgr->suggest_auto(slst, cw, ns);
    1120                 :                      break;
    1121                 : 
    1122                 :                    }
    1123                 : 
    1124                 :      case HUHINITCAP:
    1125                 :      case HUHCAP: {
    1126                 :                      ns = pSMgr->suggest_auto(slst, cw, ns);
    1127                 :                      if (ns == 0) {
    1128                 :                         memcpy(wspace,cw,(wl+1));
    1129                 :                         mkallsmall(wspace);
    1130                 :                         ns = pSMgr->suggest_auto(slst, wspace, ns);
    1131                 :                      }
    1132                 :                      break;
    1133                 :                    }
    1134                 : 
    1135                 :      case ALLCAP: {
    1136                 :                      memcpy(wspace,cw,(wl+1));
    1137                 :                      mkallsmall(wspace);
    1138                 :                      ns = pSMgr->suggest_auto(slst, wspace, ns);
    1139                 : 
    1140                 :                      mkinitcap(wspace);
    1141                 :                      ns = pSMgr->suggest_auto(slst, wspace, ns);
    1142                 : 
    1143                 :                      for (int j=0; j < ns; j++)
    1144                 :                        mkallcap((*slst)[j]);
    1145                 :                      break;
    1146                 :                    }
    1147                 :   }
    1148                 : 
    1149                 :   // word reversing wrapper for complex prefixes
    1150                 :   if (complexprefixes) {
    1151                 :     for (int j = 0; j < ns; j++) {
    1152                 :       if (utf8) reverseword_utf((*slst)[j]); else reverseword((*slst)[j]);
    1153                 :     }
    1154                 :   }
    1155                 : 
    1156                 :   // expand suggestions with dot(s)
    1157                 :   if (abbv && pAMgr && pAMgr->get_sugswithdots()) {
    1158                 :     for (int j = 0; j < ns; j++) {
    1159                 :       (*slst)[j] = (char *) realloc((*slst)[j], strlen((*slst)[j]) + 1 + abbv);
    1160                 :       strcat((*slst)[j], word + strlen(word) - abbv);
    1161                 :     }
    1162                 :   }
    1163                 : 
    1164                 :   // LANG_hu section: replace '-' with ' ' in Hungarian
    1165                 :   if (langnum == LANG_hu) {
    1166                 :       for (int j=0; j < ns; j++) {
    1167                 :           char * pos = strchr((*slst)[j],'-');
    1168                 :           if (pos) {
    1169                 :               int info;
    1170                 :               char w[MAXWORDUTF8LEN];
    1171                 :               *pos = '\0';
    1172                 :               strcpy(w, (*slst)[j]);
    1173                 :               strcat(w, pos + 1);
    1174                 :               spell(w, &info, NULL);
    1175                 :               if ((info & SPELL_COMPOUND) && (info & SPELL_FORBIDDEN)) {
    1176                 :                   *pos = ' ';
    1177                 :               } else *pos = '-';
    1178                 :           }
    1179                 :       }
    1180                 :   }
    1181                 :   // END OF LANG_hu section
    1182                 :   return ns;
    1183                 : }
    1184                 : #endif
    1185                 : 
    1186               0 : int Hunspell::stem(char*** slst, char ** desc, int n)
    1187                 : {
    1188                 :   char result[MAXLNLEN];
    1189                 :   char result2[MAXLNLEN];
    1190               0 :   *slst = NULL;
    1191               0 :   if (n == 0) return 0;
    1192               0 :   *result2 = '\0';
    1193               0 :   for (int i = 0; i < n; i++) {
    1194               0 :     *result = '\0';
    1195                 :     // add compound word parts (except the last one)
    1196               0 :     char * s = (char *) desc[i];
    1197               0 :     char * part = strstr(s, MORPH_PART);
    1198               0 :     if (part) {
    1199               0 :         char * nextpart = strstr(part + 1, MORPH_PART);
    1200               0 :         while (nextpart) {
    1201               0 :             copy_field(result + strlen(result), part, MORPH_PART);
    1202               0 :             part = nextpart;
    1203               0 :             nextpart = strstr(part + 1, MORPH_PART);
    1204                 :         }
    1205               0 :         s = part;
    1206                 :     }
    1207                 : 
    1208                 :     char **pl;
    1209                 :     char tok[MAXLNLEN];
    1210               0 :     strcpy(tok, s);
    1211               0 :     char * alt = strstr(tok, " | ");
    1212               0 :     while (alt) {
    1213               0 :         alt[1] = MSEP_ALT;
    1214               0 :         alt = strstr(alt, " | ");
    1215                 :     }
    1216               0 :     int pln = line_tok(tok, &pl, MSEP_ALT);
    1217               0 :     for (int k = 0; k < pln; k++) {
    1218                 :         // add derivational suffixes
    1219               0 :         if (strstr(pl[k], MORPH_DERI_SFX)) {
    1220                 :             // remove inflectional suffixes
    1221               0 :             char * is = strstr(pl[k], MORPH_INFL_SFX);
    1222               0 :             if (is) *is = '\0';
    1223               0 :             char * sg = pSMgr->suggest_gen(&(pl[k]), 1, pl[k]);
    1224               0 :             if (sg) {
    1225                 :                 char ** gen;
    1226               0 :                 int genl = line_tok(sg, &gen, MSEP_REC);
    1227               0 :                 free(sg);
    1228               0 :                 for (int j = 0; j < genl; j++) {
    1229               0 :                     sprintf(result2 + strlen(result2), "%c%s%s",
    1230               0 :                             MSEP_REC, result, gen[j]);
    1231                 :                 }
    1232               0 :                 freelist(&gen, genl);
    1233                 :             }
    1234                 :         } else {
    1235               0 :             sprintf(result2 + strlen(result2), "%c%s", MSEP_REC, result);
    1236               0 :             if (strstr(pl[k], MORPH_SURF_PFX)) {
    1237               0 :                 copy_field(result2 + strlen(result2), pl[k], MORPH_SURF_PFX);
    1238                 :             }
    1239               0 :             copy_field(result2 + strlen(result2), pl[k], MORPH_STEM);
    1240                 :         }
    1241                 :     }
    1242               0 :     freelist(&pl, pln);
    1243                 :   }
    1244               0 :   int sln = line_tok(result2, slst, MSEP_REC);
    1245               0 :   return uniqlist(*slst, sln);
    1246                 : 
    1247                 : }
    1248                 : 
    1249               0 : int Hunspell::stem(char*** slst, const char * word)
    1250                 : {
    1251                 :   char ** pl;
    1252               0 :   int pln = analyze(&pl, word);
    1253               0 :   int pln2 = stem(slst, pl, pln);
    1254               0 :   freelist(&pl, pln);
    1255               0 :   return pln2;
    1256                 : }
    1257                 : 
    1258                 : #ifdef HUNSPELL_EXPERIMENTAL
    1259                 : int Hunspell::suggest_pos_stems(char*** slst, const char * word)
    1260                 : {
    1261                 :   char cw[MAXWORDUTF8LEN];
    1262                 :   char wspace[MAXWORDUTF8LEN];
    1263                 :   if (! pSMgr || maxdic == 0) return 0;
    1264                 :   int wl = strlen(word);
    1265                 :   if (utf8) {
    1266                 :     if (wl >= MAXWORDUTF8LEN) return 0;
    1267                 :   } else {
    1268                 :     if (wl >= MAXWORDLEN) return 0;
    1269                 :   }
    1270                 :   int captype = 0;
    1271                 :   int abbv = 0;
    1272                 :   wl = cleanword(cw, word, &captype, &abbv);
    1273                 :   if (wl == 0) return 0;
    1274                 : 
    1275                 :   int ns = 0; // ns=0 = normalized input
    1276                 : 
    1277                 :   *slst = NULL; // HU, nsug in pSMgr->suggest
    1278                 : 
    1279                 :   switch(captype) {
    1280                 :      case HUHCAP:
    1281                 :      case NOCAP:   {
    1282                 :                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);
    1283                 : 
    1284                 :                      if ((abbv) && (ns == 0)) {
    1285                 :                          memcpy(wspace,cw,wl);
    1286                 :                          *(wspace+wl) = '.';
    1287                 :                          *(wspace+wl+1) = '\0';
    1288                 :                          ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
    1289                 :                      }
    1290                 : 
    1291                 :                      break;
    1292                 :                    }
    1293                 : 
    1294                 :      case INITCAP: {
    1295                 : 
    1296                 :                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);
    1297                 : 
    1298                 :                      if (ns == 0 || ((*slst)[0][0] == '#')) {
    1299                 :                         memcpy(wspace,cw,(wl+1));
    1300                 :                         mkallsmall(wspace);
    1301                 :                         ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
    1302                 :                      }
    1303                 : 
    1304                 :                      break;
    1305                 : 
    1306                 :                    }
    1307                 : 
    1308                 :      case ALLCAP: {
    1309                 :                      ns = pSMgr->suggest_pos_stems(slst, cw, ns);
    1310                 :                      if (ns != 0) break;
    1311                 : 
    1312                 :                      memcpy(wspace,cw,(wl+1));
    1313                 :                      mkallsmall(wspace);
    1314                 :                      ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
    1315                 : 
    1316                 :                      if (ns == 0) {
    1317                 :                          mkinitcap(wspace);
    1318                 :                          ns = pSMgr->suggest_pos_stems(slst, wspace, ns);
    1319                 :                      }
    1320                 :                      break;
    1321                 :                    }
    1322                 :   }
    1323                 : 
    1324                 :   return ns;
    1325                 : }
    1326                 : #endif // END OF HUNSPELL_EXPERIMENTAL CODE
    1327                 : 
    1328               0 : const char * Hunspell::get_wordchars()
    1329                 : {
    1330               0 :   return pAMgr->get_wordchars();
    1331                 : }
    1332                 : 
    1333               0 : unsigned short * Hunspell::get_wordchars_utf16(int * len)
    1334                 : {
    1335               0 :   return pAMgr->get_wordchars_utf16(len);
    1336                 : }
    1337                 : 
    1338               0 : void Hunspell::mkinitcap(char * p)
    1339                 : {
    1340               0 :   if (!utf8) {
    1341               0 :     if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
    1342                 :   } else {
    1343                 :       int len;
    1344                 :       w_char u[MAXWORDLEN];
    1345               0 :       len = u8_u16(u, MAXWORDLEN, p);
    1346               0 :       unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
    1347               0 :       u[0].h = (unsigned char) (i >> 8);
    1348               0 :       u[0].l = (unsigned char) (i & 0x00FF);
    1349               0 :       u16_u8(p, MAXWORDUTF8LEN, u, len);
    1350                 :   }
    1351               0 : }
    1352                 : 
    1353             145 : int Hunspell::mkinitcap2(char * p, w_char * u, int nc)
    1354                 : {
    1355             145 :   if (!utf8) {
    1356             114 :     if (*p != '\0') *p = csconv[((unsigned char)*p)].cupper;
    1357              31 :   } else if (nc > 0) {
    1358              31 :       unsigned short i = unicodetoupper((u[0].h << 8) + u[0].l, langnum);
    1359              31 :       u[0].h = (unsigned char) (i >> 8);
    1360              31 :       u[0].l = (unsigned char) (i & 0x00FF);
    1361              31 :       u16_u8(p, MAXWORDUTF8LEN, u, nc);
    1362              31 :       return strlen(p);
    1363                 :   }
    1364             114 :   return nc;
    1365                 : }
    1366                 : 
    1367               0 : int Hunspell::mkinitsmall2(char * p, w_char * u, int nc)
    1368                 : {
    1369               0 :   if (!utf8) {
    1370               0 :     if (*p != '\0') *p = csconv[((unsigned char)*p)].clower;
    1371               0 :   } else if (nc > 0) {
    1372               0 :       unsigned short i = unicodetolower((u[0].h << 8) + u[0].l, langnum);
    1373               0 :       u[0].h = (unsigned char) (i >> 8);
    1374               0 :       u[0].l = (unsigned char) (i & 0x00FF);
    1375               0 :       u16_u8(p, MAXWORDUTF8LEN, u, nc);
    1376               0 :       return strlen(p);
    1377                 :   }
    1378               0 :   return nc;
    1379                 : }
    1380                 : 
    1381               0 : int Hunspell::add(const char * word)
    1382                 : {
    1383               0 :     if (pHMgr[0]) return (pHMgr[0])->add(word);
    1384               0 :     return 0;
    1385                 : }
    1386                 : 
    1387               0 : int Hunspell::add_with_affix(const char * word, const char * example)
    1388                 : {
    1389               0 :     if (pHMgr[0]) return (pHMgr[0])->add_with_affix(word, example);
    1390               0 :     return 0;
    1391                 : }
    1392                 : 
    1393               0 : int Hunspell::remove(const char * word)
    1394                 : {
    1395               0 :     if (pHMgr[0]) return (pHMgr[0])->remove(word);
    1396               0 :     return 0;
    1397                 : }
    1398                 : 
    1399               0 : const char * Hunspell::get_version()
    1400                 : {
    1401               0 :   return pAMgr->get_version();
    1402                 : }
    1403                 : 
    1404               0 : struct cs_info * Hunspell::get_csconv()
    1405                 : {
    1406               0 :   return csconv;
    1407                 : }
    1408                 : 
    1409               0 : void Hunspell::cat_result(char * result, char * st)
    1410                 : {
    1411               0 :     if (st) {
    1412               0 :         if (*result) mystrcat(result, "\n", MAXLNLEN);
    1413               0 :         mystrcat(result, st, MAXLNLEN);
    1414               0 :         free(st);
    1415                 :     }
    1416               0 : }
    1417                 : 
    1418               0 : int Hunspell::analyze(char*** slst, const char * word)
    1419                 : {
    1420                 :   char cw[MAXWORDUTF8LEN];
    1421                 :   char wspace[MAXWORDUTF8LEN];
    1422                 :   w_char unicw[MAXWORDLEN];
    1423               0 :   int wl2 = 0;
    1424               0 :   *slst = NULL;
    1425               0 :   if (! pSMgr || maxdic == 0) return 0;
    1426               0 :   int nc = strlen(word);
    1427               0 :   if (utf8) {
    1428               0 :     if (nc >= MAXWORDUTF8LEN) return 0;
    1429                 :   } else {
    1430               0 :     if (nc >= MAXWORDLEN) return 0;
    1431                 :   }
    1432               0 :   int captype = 0;
    1433               0 :   int abbv = 0;
    1434               0 :   int wl = 0;
    1435                 : 
    1436                 :   // input conversion
    1437               0 :   RepList * rl = (pAMgr) ? pAMgr->get_iconvtable() : NULL;
    1438               0 :   if (rl && rl->conv(word, wspace)) wl = cleanword2(cw, wspace, unicw, &nc, &captype, &abbv);
    1439               0 :   else wl = cleanword2(cw, word, unicw, &nc, &captype, &abbv);
    1440                 : 
    1441               0 :   if (wl == 0) {
    1442               0 :       if (abbv) {
    1443               0 :           for (wl = 0; wl < abbv; wl++) cw[wl] = '.';
    1444               0 :           cw[wl] = '\0';
    1445               0 :           abbv = 0;
    1446               0 :       } else return 0;
    1447                 :   }
    1448                 : 
    1449                 :   char result[MAXLNLEN];
    1450               0 :   char * st = NULL;
    1451                 : 
    1452               0 :   *result = '\0';
    1453                 : 
    1454               0 :   int n = 0;
    1455               0 :   int n2 = 0;
    1456               0 :   int n3 = 0;
    1457                 : 
    1458                 :   // test numbers
    1459                 :   // LANG_hu section: set dash information for suggestions
    1460               0 :   if (langnum == LANG_hu) {
    1461               0 :   while ((n < wl) &&
    1462               0 :         (((cw[n] <= '9') && (cw[n] >= '0')) || (((cw[n] == '.') || (cw[n] == ',')) && (n > 0)))) {
    1463               0 :         n++;
    1464               0 :         if ((cw[n] == '.') || (cw[n] == ',')) {
    1465               0 :                 if (((n2 == 0) && (n > 3)) ||
    1466               0 :                         ((n2 > 0) && ((cw[n-1] == '.') || (cw[n-1] == ',')))) break;
    1467               0 :                 n2++;
    1468               0 :                 n3 = n;
    1469                 :         }
    1470                 :   }
    1471                 : 
    1472               0 :   if ((n == wl) && (n3 > 0) && (n - n3 > 3)) return 0;
    1473               0 :   if ((n == wl) || ((n>0) && ((cw[n]=='%') || (cw[n]=='\xB0')) && checkword(cw+n, NULL, NULL))) {
    1474               0 :         mystrcat(result, cw, MAXLNLEN);
    1475               0 :         result[n - 1] = '\0';
    1476               0 :         if (n == wl) cat_result(result, pSMgr->suggest_morph(cw + n - 1));
    1477                 :         else {
    1478               0 :                 char sign = cw[n];
    1479               0 :                 cw[n] = '\0';
    1480               0 :                 cat_result(result, pSMgr->suggest_morph(cw + n - 1));
    1481               0 :                 mystrcat(result, "+", MAXLNLEN); // XXX SPEC. MORPHCODE
    1482               0 :                 cw[n] = sign;
    1483               0 :                 cat_result(result, pSMgr->suggest_morph(cw + n));
    1484                 :         }
    1485               0 :         return line_tok(result, slst, MSEP_REC);
    1486                 :   }
    1487                 :   }
    1488                 :   // END OF LANG_hu section
    1489                 : 
    1490               0 :   switch(captype) {
    1491                 :      case HUHCAP:
    1492                 :      case HUHINITCAP:
    1493                 :      case NOCAP:  {
    1494               0 :                     cat_result(result, pSMgr->suggest_morph(cw));
    1495               0 :                     if (abbv) {
    1496               0 :                         memcpy(wspace,cw,wl);
    1497               0 :                         *(wspace+wl) = '.';
    1498               0 :                         *(wspace+wl+1) = '\0';
    1499               0 :                         cat_result(result, pSMgr->suggest_morph(wspace));
    1500                 :                     }
    1501               0 :                     break;
    1502                 :                 }
    1503                 :      case INITCAP: {
    1504               0 :                      wl = mkallsmall2(cw, unicw, nc);
    1505               0 :                      memcpy(wspace,cw,(wl+1));
    1506               0 :                      wl2 = mkinitcap2(cw, unicw, nc);
    1507               0 :                      cat_result(result, pSMgr->suggest_morph(wspace));
    1508               0 :                      cat_result(result, pSMgr->suggest_morph(cw));
    1509               0 :                      if (abbv) {
    1510               0 :                          *(wspace+wl) = '.';
    1511               0 :                          *(wspace+wl+1) = '\0';
    1512               0 :                          cat_result(result, pSMgr->suggest_morph(wspace));
    1513                 : 
    1514               0 :                          memcpy(wspace, cw, wl2);
    1515               0 :                          *(wspace+wl2) = '.';
    1516               0 :                          *(wspace+wl2+1) = '\0';
    1517                 : 
    1518               0 :                          cat_result(result, pSMgr->suggest_morph(wspace));
    1519                 :                      }
    1520               0 :                      break;
    1521                 :                    }
    1522                 :      case ALLCAP: {
    1523               0 :                      cat_result(result, pSMgr->suggest_morph(cw));
    1524               0 :                      if (abbv) {
    1525               0 :                          memcpy(wspace,cw,wl);
    1526               0 :                          *(wspace+wl) = '.';
    1527               0 :                          *(wspace+wl+1) = '\0';
    1528               0 :                          cat_result(result, pSMgr->suggest_morph(cw));
    1529                 :                      }
    1530               0 :                      wl = mkallsmall2(cw, unicw, nc);
    1531               0 :                      memcpy(wspace,cw,(wl+1));
    1532               0 :                      wl2 = mkinitcap2(cw, unicw, nc);
    1533                 : 
    1534               0 :                      cat_result(result, pSMgr->suggest_morph(wspace));
    1535               0 :                      cat_result(result, pSMgr->suggest_morph(cw));
    1536               0 :                      if (abbv) {
    1537               0 :                          *(wspace+wl) = '.';
    1538               0 :                          *(wspace+wl+1) = '\0';
    1539               0 :                          cat_result(result, pSMgr->suggest_morph(wspace));
    1540                 : 
    1541               0 :                          memcpy(wspace, cw, wl2);
    1542               0 :                          *(wspace+wl2) = '.';
    1543               0 :                          *(wspace+wl2+1) = '\0';
    1544                 : 
    1545               0 :                          cat_result(result, pSMgr->suggest_morph(wspace));
    1546                 :                      }
    1547               0 :                      break;
    1548                 :                    }
    1549                 :   }
    1550                 : 
    1551               0 :   if (*result) {
    1552                 :     // word reversing wrapper for complex prefixes
    1553               0 :     if (complexprefixes) {
    1554               0 :       if (utf8) reverseword_utf(result); else reverseword(result);
    1555                 :     }
    1556               0 :     return line_tok(result, slst, MSEP_REC);
    1557                 :   }
    1558                 : 
    1559                 :   // compound word with dash (HU) I18n
    1560               0 :   char * dash = NULL;
    1561               0 :   int nresult = 0;
    1562                 :   // LANG_hu section: set dash information for suggestions
    1563               0 :   if (langnum == LANG_hu) dash = (char *) strchr(cw,'-');
    1564               0 :   if ((langnum == LANG_hu) && dash) {
    1565               0 :       *dash='\0';
    1566                 :       // examine 2 sides of the dash
    1567               0 :       if (dash[1] == '\0') { // base word ending with dash
    1568               0 :         if (spell(cw)) {
    1569               0 :                 char * p = pSMgr->suggest_morph(cw);
    1570               0 :                 if (p) {
    1571               0 :                     int ret = line_tok(p, slst, MSEP_REC);
    1572               0 :                     free(p);
    1573               0 :                     return ret;
    1574                 :                 }
    1575                 :                 
    1576                 :         }
    1577               0 :       } else if ((dash[1] == 'e') && (dash[2] == '\0')) { // XXX (HU) -e hat.
    1578               0 :         if (spell(cw) && (spell("-e"))) {
    1579               0 :                         st = pSMgr->suggest_morph(cw);
    1580               0 :                         if (st) {
    1581               0 :                                 mystrcat(result, st, MAXLNLEN);
    1582               0 :                                 free(st);
    1583                 :                         }
    1584               0 :                         mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE
    1585               0 :                         st = pSMgr->suggest_morph("-e");
    1586               0 :                         if (st) {
    1587               0 :                                 mystrcat(result, st, MAXLNLEN);
    1588               0 :                                 free(st);
    1589                 :                         }
    1590               0 :                         return line_tok(result, slst, MSEP_REC);
    1591                 :                 }
    1592                 :       } else {
    1593                 :       // first word ending with dash: word- XXX ???
    1594               0 :         char r2 = *(dash + 1);
    1595               0 :         dash[0]='-';
    1596               0 :         dash[1]='\0';
    1597               0 :         nresult = spell(cw);
    1598               0 :         dash[1] = r2;
    1599               0 :         dash[0]='\0';
    1600               0 :         if (nresult && spell(dash+1) && ((strlen(dash+1) > 1) ||
    1601               0 :                 ((dash[1] > '0') && (dash[1] < '9')))) {
    1602               0 :                             st = pSMgr->suggest_morph(cw);
    1603               0 :                             if (st) {
    1604               0 :                                 mystrcat(result, st, MAXLNLEN);
    1605               0 :                                     free(st);
    1606               0 :                                 mystrcat(result,"+", MAXLNLEN); // XXX spec. separator in MORPHCODE
    1607                 :                             }
    1608               0 :                             st = pSMgr->suggest_morph(dash+1);
    1609               0 :                             if (st) {
    1610               0 :                                     mystrcat(result, st, MAXLNLEN);
    1611               0 :                                     free(st);
    1612                 :                             }
    1613               0 :                             return line_tok(result, slst, MSEP_REC);
    1614                 :                         }
    1615                 :       }
    1616                 :       // affixed number in correct word
    1617               0 :      if (nresult && (dash > cw) && (((*(dash-1)<='9') &&
    1618               0 :                         (*(dash-1)>='0')) || (*(dash-1)=='.'))) {
    1619               0 :          *dash='-';
    1620               0 :          n = 1;
    1621               0 :          if (*(dash - n) == '.') n++;
    1622                 :          // search first not a number character to left from dash
    1623               0 :          while (((dash - n)>=cw) && ((*(dash - n)=='0') || (n < 3)) && (n < 6)) {
    1624               0 :             n++;
    1625                 :          }
    1626               0 :          if ((dash - n) < cw) n--;
    1627                 :          // numbers: valami1000000-hoz
    1628                 :          // examine 100000-hoz, 10000-hoz 1000-hoz, 10-hoz,
    1629                 :          // 56-hoz, 6-hoz
    1630               0 :          for(; n >= 1; n--) {
    1631               0 :             if ((*(dash - n) >= '0') && (*(dash - n) <= '9') && checkword(dash - n, NULL, NULL)) {
    1632               0 :                     mystrcat(result, cw, MAXLNLEN);
    1633               0 :                     result[dash - cw - n] = '\0';
    1634               0 :                         st = pSMgr->suggest_morph(dash - n);
    1635               0 :                         if (st) {
    1636               0 :                         mystrcat(result, st, MAXLNLEN);
    1637               0 :                                 free(st);
    1638                 :                         }
    1639               0 :                         return line_tok(result, slst, MSEP_REC);
    1640                 :             }
    1641                 :          }
    1642                 :      }
    1643                 :   }
    1644               0 :   return 0;
    1645                 : }
    1646                 : 
    1647               0 : int Hunspell::generate(char*** slst, const char * word, char ** pl, int pln)
    1648                 : {
    1649               0 :   *slst = NULL;
    1650               0 :   if (!pSMgr || !pln) return 0;
    1651                 :   char **pl2;
    1652               0 :   int pl2n = analyze(&pl2, word);
    1653               0 :   int captype = 0;
    1654               0 :   int abbv = 0;
    1655                 :   char cw[MAXWORDUTF8LEN];
    1656               0 :   cleanword(cw, word, &captype, &abbv);
    1657                 :   char result[MAXLNLEN];
    1658               0 :   *result = '\0';
    1659                 : 
    1660               0 :   for (int i = 0; i < pln; i++) {
    1661               0 :     cat_result(result, pSMgr->suggest_gen(pl2, pl2n, pl[i]));
    1662                 :   }
    1663               0 :   freelist(&pl2, pl2n);
    1664                 : 
    1665               0 :   if (*result) {
    1666                 :     // allcap
    1667               0 :     if (captype == ALLCAP) mkallcap(result);
    1668                 : 
    1669                 :     // line split
    1670               0 :     int linenum = line_tok(result, slst, MSEP_REC);
    1671                 : 
    1672                 :     // capitalize
    1673               0 :     if (captype == INITCAP || captype == HUHINITCAP) {
    1674               0 :         for (int j=0; j < linenum; j++) mkinitcap((*slst)[j]);
    1675                 :     }
    1676                 : 
    1677                 :     // temporary filtering of prefix related errors (eg.
    1678                 :     // generate("undrinkable", "eats") --> "undrinkables" and "*undrinks")
    1679                 : 
    1680               0 :     int r = 0;
    1681               0 :     for (int j=0; j < linenum; j++) {
    1682               0 :         if (!spell((*slst)[j])) {
    1683               0 :             free((*slst)[j]);
    1684               0 :             (*slst)[j] = NULL;
    1685                 :         } else {
    1686               0 :             if (r < j) (*slst)[r] = (*slst)[j];
    1687               0 :             r++;
    1688                 :         }
    1689                 :     }
    1690               0 :     if (r > 0) return r;
    1691               0 :     free(*slst);
    1692               0 :     *slst = NULL;
    1693                 :   }
    1694               0 :   return 0;
    1695                 : }
    1696                 : 
    1697               0 : int Hunspell::generate(char*** slst, const char * word, const char * pattern)
    1698                 : {
    1699                 :   char **pl;
    1700               0 :   int pln = analyze(&pl, pattern);
    1701               0 :   int n = generate(slst, word, pl, pln);
    1702               0 :   freelist(&pl, pln);
    1703               0 :   return uniqlist(*slst, n);
    1704                 : }
    1705                 : 
    1706                 : // minimal XML parser functions
    1707               0 : int Hunspell::get_xml_par(char * dest, const char * par, int max)
    1708                 : {
    1709               0 :    char * d = dest;
    1710               0 :    if (!par) return 0;
    1711               0 :    char end = *par;
    1712               0 :    char * dmax = dest + max;
    1713               0 :    if (end == '>') end = '<';
    1714               0 :    else if (end != '\'' && end != '"') return 0; // bad XML
    1715               0 :    for (par++; d < dmax && *par != '\0' && *par != end; par++, d++) *d = *par;
    1716               0 :    *d = '\0';
    1717               0 :    mystrrep(dest, "&lt;", "<");
    1718               0 :    mystrrep(dest, "&amp;", "&");
    1719               0 :    return (int)(d - dest);
    1720                 : }
    1721                 : 
    1722               0 : int Hunspell::get_langnum() const
    1723                 : {
    1724               0 :    return langnum;
    1725                 : }
    1726                 : 
    1727                 : // return the beginning of the element (attr == NULL) or the attribute
    1728               0 : const char * Hunspell::get_xml_pos(const char * s, const char * attr)
    1729                 : {
    1730               0 :   const char * end = strchr(s, '>');
    1731               0 :   const char * p = s;
    1732               0 :   if (attr == NULL) return end;
    1733               0 :   do {
    1734               0 :     p = strstr(p, attr);
    1735               0 :     if (!p || p >= end) return 0;
    1736               0 :   } while (*(p-1) != ' ' &&  *(p-1) != '\n');
    1737               0 :   return p + strlen(attr);
    1738                 : }
    1739                 : 
    1740               0 : int Hunspell::check_xml_par(const char * q, const char * attr, const char * value) {
    1741                 :   char cw[MAXWORDUTF8LEN];
    1742               0 :   if (get_xml_par(cw, get_xml_pos(q, attr), MAXWORDUTF8LEN - 1) &&
    1743               0 :     strcmp(cw, value) == 0) return 1;
    1744               0 :   return 0;
    1745                 : }
    1746                 : 
    1747               0 : int Hunspell::get_xml_list(char ***slst, char * list, const char * tag) {
    1748               0 :     int n = 0;
    1749                 :     char * p;
    1750               0 :     if (!list) return 0;
    1751               0 :     for (p = list; (p = strstr(p, tag)); p++) n++;
    1752               0 :     if (n == 0) return 0;
    1753               0 :     *slst = (char **) malloc(sizeof(char *) * n);
    1754               0 :     if (!*slst) return 0;
    1755               0 :     for (p = list, n = 0; (p = strstr(p, tag)); p++, n++) {
    1756               0 :         int l = strlen(p);
    1757               0 :         (*slst)[n] = (char *) malloc(l + 1);
    1758               0 :         if (!(*slst)[n]) return n;
    1759               0 :         if (!get_xml_par((*slst)[n], p + strlen(tag) - 1, l)) {
    1760               0 :             free((*slst)[n]);
    1761               0 :             break;
    1762                 :         }
    1763                 :     }
    1764               0 :     return n;
    1765                 : }
    1766                 : 
    1767               0 : int Hunspell::spellml(char*** slst, const char * word)
    1768                 : {
    1769                 :   char *q, *q2;
    1770                 :   char cw[MAXWORDUTF8LEN], cw2[MAXWORDUTF8LEN];
    1771               0 :   q = (char *) strstr(word, "<query");
    1772               0 :   if (!q) return 0; // bad XML input
    1773               0 :   q2 = strchr(q, '>');
    1774               0 :   if (!q2) return 0; // bad XML input
    1775               0 :   q2 = strstr(q2, "<word");
    1776               0 :   if (!q2) return 0; // bad XML input
    1777               0 :   if (check_xml_par(q, "type=", "analyze")) {
    1778               0 :       int n = 0, s = 0;
    1779               0 :       if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 10)) n = analyze(slst, cw);
    1780               0 :       if (n == 0) return 0;
    1781                 :       // convert the result to <code><a>ana1</a><a>ana2</a></code> format
    1782               0 :       for (int i = 0; i < n; i++) s+= strlen((*slst)[i]);
    1783               0 :       char * r = (char *) malloc(6 + 5 * s + 7 * n + 7 + 1); // XXX 5*s->&->&amp;
    1784               0 :       if (!r) return 0;
    1785               0 :       strcpy(r, "<code>");
    1786               0 :       for (int i = 0; i < n; i++) {
    1787               0 :         int l = strlen(r);
    1788               0 :         strcpy(r + l, "<a>");
    1789               0 :         strcpy(r + l + 3, (*slst)[i]);
    1790               0 :         mystrrep(r + l + 3, "\t", " ");
    1791               0 :         mystrrep(r + l + 3, "<", "&lt;");
    1792               0 :         mystrrep(r + l + 3, "&", "&amp;");
    1793               0 :         strcat(r, "</a>");
    1794               0 :         free((*slst)[i]);
    1795                 :       }
    1796               0 :       strcat(r, "</code>");
    1797               0 :       (*slst)[0] = r;
    1798               0 :       return 1;
    1799               0 :   } else if (check_xml_par(q, "type=", "stem")) {
    1800               0 :       if (get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1)) return stem(slst, cw);
    1801               0 :   } else if (check_xml_par(q, "type=", "generate")) {
    1802               0 :       int n = get_xml_par(cw, strchr(q2, '>'), MAXWORDUTF8LEN - 1);
    1803               0 :       if (n == 0) return 0;
    1804               0 :       char * q3 = strstr(q2 + 1, "<word");
    1805               0 :       if (q3) {
    1806               0 :         if (get_xml_par(cw2, strchr(q3, '>'), MAXWORDUTF8LEN - 1)) {
    1807               0 :             return generate(slst, cw, cw2);
    1808                 :         }
    1809                 :       } else {
    1810               0 :         if ((q2 = strstr(q2 + 1, "<code"))) {
    1811                 :           char ** slst2;
    1812               0 :           if ((n = get_xml_list(&slst2, strchr(q2, '>'), "<a>"))) {
    1813               0 :             int n2 = generate(slst, cw, slst2, n);
    1814               0 :             freelist(&slst2, n);
    1815               0 :             return uniqlist(*slst, n2);
    1816                 :           }
    1817               0 :           freelist(&slst2, n);
    1818                 :         }
    1819                 :       }
    1820                 :   }
    1821               0 :   return 0;
    1822                 : }
    1823                 : 
    1824                 : 
    1825                 : #ifdef HUNSPELL_EXPERIMENTAL
    1826                 : // XXX need UTF-8 support
    1827                 : char * Hunspell::morph_with_correction(const char * word)
    1828                 : {
    1829                 :   char cw[MAXWORDUTF8LEN];
    1830                 :   char wspace[MAXWORDUTF8LEN];
    1831                 :   if (! pSMgr || maxdic == 0) return NULL;
    1832                 :   int wl = strlen(word);
    1833                 :   if (utf8) {
    1834                 :     if (wl >= MAXWORDUTF8LEN) return NULL;
    1835                 :   } else {
    1836                 :     if (wl >= MAXWORDLEN) return NULL;
    1837                 :   }
    1838                 :   int captype = 0;
    1839                 :   int abbv = 0;
    1840                 :   wl = cleanword(cw, word, &captype, &abbv);
    1841                 :   if (wl == 0) return NULL;
    1842                 : 
    1843                 :   char result[MAXLNLEN];
    1844                 :   char * st = NULL;
    1845                 : 
    1846                 :   *result = '\0';
    1847                 : 
    1848                 : 
    1849                 :   switch(captype) {
    1850                 :      case NOCAP:   {
    1851                 :                      st = pSMgr->suggest_morph_for_spelling_error(cw);
    1852                 :                      if (st) {
    1853                 :                         mystrcat(result, st, MAXLNLEN);
    1854                 :                         free(st);
    1855                 :                      }
    1856                 :                      if (abbv) {
    1857                 :                          memcpy(wspace,cw,wl);
    1858                 :                          *(wspace+wl) = '.';
    1859                 :                          *(wspace+wl+1) = '\0';
    1860                 :                          st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1861                 :                          if (st) {
    1862                 :                             if (*result) mystrcat(result, "\n", MAXLNLEN);
    1863                 :                             mystrcat(result, st, MAXLNLEN);
    1864                 :                             free(st);
    1865                 :                                                  }
    1866                 :                      }
    1867                 :                                          break;
    1868                 :                    }
    1869                 :      case INITCAP: {
    1870                 :                      memcpy(wspace,cw,(wl+1));
    1871                 :                      mkallsmall(wspace);
    1872                 :                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1873                 :                      if (st) {
    1874                 :                         mystrcat(result, st, MAXLNLEN);
    1875                 :                         free(st);
    1876                 :                      }
    1877                 :                      st = pSMgr->suggest_morph_for_spelling_error(cw);
    1878                 :                      if (st) {
    1879                 :                         if (*result) mystrcat(result, "\n", MAXLNLEN);
    1880                 :                         mystrcat(result, st, MAXLNLEN);
    1881                 :                         free(st);
    1882                 :                      }
    1883                 :                      if (abbv) {
    1884                 :                          memcpy(wspace,cw,wl);
    1885                 :                          *(wspace+wl) = '.';
    1886                 :                          *(wspace+wl+1) = '\0';
    1887                 :                          mkallsmall(wspace);
    1888                 :                          st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1889                 :                          if (st) {
    1890                 :                             if (*result) mystrcat(result, "\n", MAXLNLEN);
    1891                 :                             mystrcat(result, st, MAXLNLEN);
    1892                 :                             free(st);
    1893                 :                          }
    1894                 :                          mkinitcap(wspace);
    1895                 :                          st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1896                 :                          if (st) {
    1897                 :                             if (*result) mystrcat(result, "\n", MAXLNLEN);
    1898                 :                             mystrcat(result, st, MAXLNLEN);
    1899                 :                             free(st);
    1900                 :                          }
    1901                 :                      }
    1902                 :                      break;
    1903                 :                    }
    1904                 :      case HUHCAP: {
    1905                 :                      st = pSMgr->suggest_morph_for_spelling_error(cw);
    1906                 :                      if (st) {
    1907                 :                         mystrcat(result, st, MAXLNLEN);
    1908                 :                         free(st);
    1909                 :                      }
    1910                 :                      memcpy(wspace,cw,(wl+1));
    1911                 :                      mkallsmall(wspace);
    1912                 :                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1913                 :                      if (st) {
    1914                 :                         if (*result) mystrcat(result, "\n", MAXLNLEN);
    1915                 :                         mystrcat(result, st, MAXLNLEN);
    1916                 :                         free(st);
    1917                 :                      }
    1918                 :                      break;
    1919                 :                  }
    1920                 :      case ALLCAP: {
    1921                 :                      memcpy(wspace,cw,(wl+1));
    1922                 :                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1923                 :                      if (st) {
    1924                 :                         mystrcat(result, st, MAXLNLEN);
    1925                 :                         free(st);
    1926                 :                      }
    1927                 :                      mkallsmall(wspace);
    1928                 :                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1929                 :                      if (st) {
    1930                 :                         if (*result) mystrcat(result, "\n", MAXLNLEN);
    1931                 :                         mystrcat(result, st, MAXLNLEN);
    1932                 :                         free(st);
    1933                 :                      }
    1934                 :                      mkinitcap(wspace);
    1935                 :                      st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1936                 :                      if (st) {
    1937                 :                         if (*result) mystrcat(result, "\n", MAXLNLEN);
    1938                 :                         mystrcat(result, st, MAXLNLEN);
    1939                 :                         free(st);
    1940                 :                      }
    1941                 :                      if (abbv) {
    1942                 :                         memcpy(wspace,cw,(wl+1));
    1943                 :                         *(wspace+wl) = '.';
    1944                 :                         *(wspace+wl+1) = '\0';
    1945                 :                         if (*result) mystrcat(result, "\n", MAXLNLEN);
    1946                 :                         st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1947                 :                         if (st) {
    1948                 :                             mystrcat(result, st, MAXLNLEN);
    1949                 :                             free(st);
    1950                 :                         }
    1951                 :                         mkallsmall(wspace);
    1952                 :                         st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1953                 :                         if (st) {
    1954                 :                           if (*result) mystrcat(result, "\n", MAXLNLEN);
    1955                 :                           mystrcat(result, st, MAXLNLEN);
    1956                 :                           free(st);
    1957                 :                         }
    1958                 :                         mkinitcap(wspace);
    1959                 :                         st = pSMgr->suggest_morph_for_spelling_error(wspace);
    1960                 :                         if (st) {
    1961                 :                           if (*result) mystrcat(result, "\n", MAXLNLEN);
    1962                 :                           mystrcat(result, st, MAXLNLEN);
    1963                 :                           free(st);
    1964                 :                         }
    1965                 :                      }
    1966                 :                      break;
    1967                 :                    }
    1968                 :   }
    1969                 : 
    1970                 :   if (*result) return mystrdup(result);
    1971                 :   return NULL;
    1972                 : }
    1973                 : 
    1974                 : #endif // END OF HUNSPELL_EXPERIMENTAL CODE
    1975                 : 
    1976               0 : Hunhandle *Hunspell_create(const char * affpath, const char * dpath)
    1977                 : {
    1978               0 :         return (Hunhandle*)(new Hunspell(affpath, dpath));
    1979                 : }
    1980                 : 
    1981               0 : Hunhandle *Hunspell_create_key(const char * affpath, const char * dpath,
    1982                 :     const char * key)
    1983                 : {
    1984               0 :         return (Hunhandle*)(new Hunspell(affpath, dpath, key));
    1985                 : }
    1986                 : 
    1987               0 : void Hunspell_destroy(Hunhandle *pHunspell)
    1988                 : {
    1989               0 :         delete (Hunspell*)(pHunspell);
    1990               0 : }
    1991                 : 
    1992               0 : int Hunspell_spell(Hunhandle *pHunspell, const char *word)
    1993                 : {
    1994               0 :         return ((Hunspell*)pHunspell)->spell(word);
    1995                 : }
    1996                 : 
    1997               0 : char *Hunspell_get_dic_encoding(Hunhandle *pHunspell)
    1998                 : {
    1999               0 :         return ((Hunspell*)pHunspell)->get_dic_encoding();
    2000                 : }
    2001                 : 
    2002               0 : int Hunspell_suggest(Hunhandle *pHunspell, char*** slst, const char * word)
    2003                 : {
    2004               0 :         return ((Hunspell*)pHunspell)->suggest(slst, word);
    2005                 : }
    2006                 : 
    2007               0 : int Hunspell_analyze(Hunhandle *pHunspell, char*** slst, const char * word)
    2008                 : {
    2009               0 :         return ((Hunspell*)pHunspell)->analyze(slst, word);
    2010                 : }
    2011                 : 
    2012               0 : int Hunspell_stem(Hunhandle *pHunspell, char*** slst, const char * word)
    2013                 : {
    2014               0 :         return ((Hunspell*)pHunspell)->stem(slst, word);
    2015                 : }
    2016                 : 
    2017               0 : int Hunspell_stem2(Hunhandle *pHunspell, char*** slst, char** desc, int n)
    2018                 : {
    2019               0 :         return ((Hunspell*)pHunspell)->stem(slst, desc, n);
    2020                 : }
    2021                 : 
    2022               0 : int Hunspell_generate(Hunhandle *pHunspell, char*** slst, const char * word,
    2023                 :     const char * word2)
    2024                 : {
    2025               0 :         return ((Hunspell*)pHunspell)->generate(slst, word, word2);
    2026                 : }
    2027                 : 
    2028               0 : int Hunspell_generate2(Hunhandle *pHunspell, char*** slst, const char * word,
    2029                 :     char** desc, int n)
    2030                 : {
    2031               0 :         return ((Hunspell*)pHunspell)->generate(slst, word, desc, n);
    2032                 : }
    2033                 : 
    2034                 :   /* functions for run-time modification of the dictionary */
    2035                 : 
    2036                 :   /* add word to the run-time dictionary */
    2037                 : 
    2038               0 : int Hunspell_add(Hunhandle *pHunspell, const char * word) {
    2039               0 :         return ((Hunspell*)pHunspell)->add(word);
    2040                 : }
    2041                 : 
    2042                 :   /* add word to the run-time dictionary with affix flags of
    2043                 :    * the example (a dictionary word): Hunspell will recognize
    2044                 :    * affixed forms of the new word, too.
    2045                 :    */
    2046                 : 
    2047               0 : int Hunspell_add_with_affix(Hunhandle *pHunspell, const char * word,
    2048                 :         const char * example) {
    2049               0 :         return ((Hunspell*)pHunspell)->add_with_affix(word, example);
    2050                 : }
    2051                 : 
    2052                 :   /* remove word from the run-time dictionary */
    2053                 : 
    2054               0 : int Hunspell_remove(Hunhandle *pHunspell, const char * word) {
    2055               0 :         return ((Hunspell*)pHunspell)->remove(word);
    2056                 : }
    2057                 : 
    2058               0 : void Hunspell_free_list(Hunhandle *, char *** slst, int n) {
    2059               0 :         freelist(slst, n);
    2060               0 : }

Generated by: LCOV version 1.7