LCOV - code coverage report
Current view: top level - extensions/spellcheck/hunspell/src - hunspell.cxx (source / functions) Hit Total Coverage
Test: output.info Lines: 0 1171 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 90 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13