LCOV - code coverage report
Current view: top level - extensions/spellcheck/hunspell/src - affixmgr.cxx (source / functions) Hit Total Coverage
Test: output.info Lines: 0 2899 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 100 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             : #include <ctype.h>
      75             : 
      76             : #include <algorithm>
      77             : #include <limits>
      78             : #include <string>
      79             : #include <vector>
      80             : 
      81             : #include "affixmgr.hxx"
      82             : #include "affentry.hxx"
      83             : #include "langnum.hxx"
      84             : 
      85             : #include "csutil.hxx"
      86             : 
      87           0 : AffixMgr::AffixMgr(const char* affpath,
      88             :                    const std::vector<HashMgr*>& ptr,
      89           0 :                    const char* key)
      90             :   : alldic(ptr)
      91           0 :   , pHMgr(ptr[0]) {
      92             : 
      93             :   // register hash manager and load affix data from aff file
      94           0 :   csconv = NULL;
      95           0 :   utf8 = 0;
      96           0 :   complexprefixes = 0;
      97           0 :   parsedmaptable = false;
      98           0 :   parsedbreaktable = false;
      99           0 :   parsedrep = false;
     100           0 :   iconvtable = NULL;
     101           0 :   oconvtable = NULL;
     102             :   // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN)
     103           0 :   simplifiedcpd = 0;
     104           0 :   parsedcheckcpd = false;
     105           0 :   parseddefcpd = false;
     106           0 :   phone = NULL;
     107           0 :   compoundflag = FLAG_NULL;        // permits word in compound forms
     108           0 :   compoundbegin = FLAG_NULL;       // may be first word in compound forms
     109           0 :   compoundmiddle = FLAG_NULL;      // may be middle word in compound forms
     110           0 :   compoundend = FLAG_NULL;         // may be last word in compound forms
     111           0 :   compoundroot = FLAG_NULL;        // compound word signing flag
     112           0 :   compoundpermitflag = FLAG_NULL;  // compound permitting flag for suffixed word
     113           0 :   compoundforbidflag = FLAG_NULL;  // compound fordidden flag for suffixed word
     114           0 :   compoundmoresuffixes = 0;        // allow more suffixes within compound words
     115           0 :   checkcompounddup = 0;            // forbid double words in compounds
     116           0 :   checkcompoundrep = 0;  // forbid bad compounds (may be non compound word with
     117             :                          // a REP substitution)
     118           0 :   checkcompoundcase =
     119             :       0;  // forbid upper and lowercase combinations at word bounds
     120           0 :   checkcompoundtriple = 0;  // forbid compounds with triple letters
     121           0 :   simplifiedtriple = 0;     // allow simplified triple letters in compounds
     122             :                             // (Schiff+fahrt -> Schiffahrt)
     123           0 :   forbiddenword = FORBIDDENWORD;  // forbidden word signing flag
     124           0 :   nosuggest = FLAG_NULL;  // don't suggest words signed with NOSUGGEST flag
     125           0 :   nongramsuggest = FLAG_NULL;
     126           0 :   langnum = 0;  // language code (see http://l10n.openoffice.org/languages.html)
     127           0 :   needaffix = FLAG_NULL;  // forbidden root, allowed only with suffixes
     128           0 :   cpdwordmax = -1;        // default: unlimited wordcount in compound words
     129           0 :   cpdmin = -1;            // undefined
     130           0 :   cpdmaxsyllable = 0;     // default: unlimited syllablecount in compound words
     131           0 :   pfxappnd = NULL;  // previous prefix for counting syllables of the prefix BUG
     132           0 :   sfxappnd = NULL;  // previous suffix for counting syllables of the suffix BUG
     133           0 :   sfxextra = 0;     // modifier for syllable count of sfxappnd BUG
     134           0 :   checknum = 0;               // checking numbers, and word with numbers
     135           0 :   havecontclass = 0;  // flags of possible continuing classes (double affix)
     136             :   // LEMMA_PRESENT: not put root into the morphological output. Lemma presents
     137             :   // in morhological description in dictionary file. It's often combined with
     138             :   // PSEUDOROOT.
     139           0 :   lemma_present = FLAG_NULL;
     140           0 :   circumfix = FLAG_NULL;
     141           0 :   onlyincompound = FLAG_NULL;
     142           0 :   maxngramsugs = -1;  // undefined
     143           0 :   maxdiff = -1;       // undefined
     144           0 :   onlymaxdiff = 0;
     145           0 :   maxcpdsugs = -1;  // undefined
     146           0 :   nosplitsugs = 0;
     147           0 :   sugswithdots = 0;
     148           0 :   keepcase = 0;
     149           0 :   forceucase = 0;
     150           0 :   warn = 0;
     151           0 :   forbidwarn = 0;
     152           0 :   checksharps = 0;
     153           0 :   substandard = FLAG_NULL;
     154           0 :   fullstrip = 0;
     155             : 
     156           0 :   sfx = NULL;
     157           0 :   pfx = NULL;
     158             : 
     159           0 :   for (int i = 0; i < SETSIZE; i++) {
     160           0 :     pStart[i] = NULL;
     161           0 :     sStart[i] = NULL;
     162           0 :     pFlag[i] = NULL;
     163           0 :     sFlag[i] = NULL;
     164             :   }
     165             : 
     166           0 :   for (int j = 0; j < CONTSIZE; j++) {
     167           0 :     contclasses[j] = 0;
     168             :   }
     169             : 
     170           0 :   if (parse_file(affpath, key)) {
     171           0 :     HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n", affpath);
     172             :   }
     173             : 
     174           0 :   if (cpdmin == -1)
     175           0 :     cpdmin = MINCPDLEN;
     176           0 : }
     177             : 
     178           0 : AffixMgr::~AffixMgr() {
     179             :   // pass through linked prefix entries and clean up
     180           0 :   for (int i = 0; i < SETSIZE; i++) {
     181           0 :     pFlag[i] = NULL;
     182           0 :     PfxEntry* ptr = pStart[i];
     183           0 :     PfxEntry* nptr = NULL;
     184           0 :     while (ptr) {
     185           0 :       nptr = ptr->getNext();
     186           0 :       delete (ptr);
     187           0 :       ptr = nptr;
     188           0 :       nptr = NULL;
     189             :     }
     190             :   }
     191             : 
     192             :   // pass through linked suffix entries and clean up
     193           0 :   for (int j = 0; j < SETSIZE; j++) {
     194           0 :     sFlag[j] = NULL;
     195           0 :     SfxEntry* ptr = sStart[j];
     196           0 :     SfxEntry* nptr = NULL;
     197           0 :     while (ptr) {
     198           0 :       nptr = ptr->getNext();
     199           0 :       delete (ptr);
     200           0 :       ptr = nptr;
     201           0 :       nptr = NULL;
     202             :     }
     203           0 :     sStart[j] = NULL;
     204             :   }
     205             : 
     206           0 :   delete iconvtable;
     207           0 :   delete oconvtable;
     208           0 :   delete phone;
     209             : 
     210           0 :   FREE_FLAG(compoundflag);
     211           0 :   FREE_FLAG(compoundbegin);
     212           0 :   FREE_FLAG(compoundmiddle);
     213           0 :   FREE_FLAG(compoundend);
     214           0 :   FREE_FLAG(compoundpermitflag);
     215           0 :   FREE_FLAG(compoundforbidflag);
     216           0 :   FREE_FLAG(compoundroot);
     217           0 :   FREE_FLAG(forbiddenword);
     218           0 :   FREE_FLAG(nosuggest);
     219           0 :   FREE_FLAG(nongramsuggest);
     220           0 :   FREE_FLAG(needaffix);
     221           0 :   FREE_FLAG(lemma_present);
     222           0 :   FREE_FLAG(circumfix);
     223           0 :   FREE_FLAG(onlyincompound);
     224             : 
     225           0 :   cpdwordmax = 0;
     226           0 :   pHMgr = NULL;
     227           0 :   cpdmin = 0;
     228           0 :   cpdmaxsyllable = 0;
     229           0 :   free_utf_tbl();
     230           0 :   checknum = 0;
     231             : #ifdef MOZILLA_CLIENT
     232           0 :   delete[] csconv;
     233             : #endif
     234           0 : }
     235             : 
     236           0 : void AffixMgr::finishFileMgr(FileMgr* afflst) {
     237           0 :   delete afflst;
     238             : 
     239             :   // convert affix trees to sorted list
     240           0 :   process_pfx_tree_to_list();
     241           0 :   process_sfx_tree_to_list();
     242           0 : }
     243             : 
     244             : // read in aff file and build up prefix and suffix entry objects
     245           0 : int AffixMgr::parse_file(const char* affpath, const char* key) {
     246             : 
     247             :   // checking flag duplication
     248             :   char dupflags[CONTSIZE];
     249           0 :   char dupflags_ini = 1;
     250             : 
     251             :   // first line indicator for removing byte order mark
     252           0 :   int firstline = 1;
     253             : 
     254             :   // open the affix file
     255           0 :   FileMgr* afflst = new FileMgr(affpath, key);
     256           0 :   if (!afflst) {
     257             :     HUNSPELL_WARNING(
     258           0 :         stderr, "error: could not open affix description file %s\n", affpath);
     259           0 :     return 1;
     260             :   }
     261             : 
     262             :   // step one is to parse the affix file building up the internal
     263             :   // affix data structures
     264             : 
     265             :   // read in each line ignoring any that do not
     266             :   // start with a known line type indicator
     267           0 :   std::string line;
     268           0 :   while (afflst->getline(line)) {
     269           0 :     mychomp(line);
     270             : 
     271             :     /* remove byte order mark */
     272           0 :     if (firstline) {
     273           0 :       firstline = 0;
     274             :       // Affix file begins with byte order mark: possible incompatibility with
     275             :       // old Hunspell versions
     276           0 :       if (line.compare(0, 3, "\xEF\xBB\xBF", 3) == 0) {
     277           0 :         line.erase(0, 3);
     278             :       }
     279             :     }
     280             : 
     281             :     /* parse in the keyboard string */
     282           0 :     if (line.compare(0, 3, "KEY", 3) == 0) {
     283           0 :       if (!parse_string(line, keystring, afflst->getlinenum())) {
     284           0 :         finishFileMgr(afflst);
     285           0 :         return 1;
     286             :       }
     287             :     }
     288             : 
     289             :     /* parse in the try string */
     290           0 :     if (line.compare(0, 3, "TRY", 3) == 0) {
     291           0 :       if (!parse_string(line, trystring, afflst->getlinenum())) {
     292           0 :         finishFileMgr(afflst);
     293           0 :         return 1;
     294             :       }
     295             :     }
     296             : 
     297             :     /* parse in the name of the character set used by the .dict and .aff */
     298           0 :     if (line.compare(0, 3, "SET", 3) == 0) {
     299           0 :       if (!parse_string(line, encoding, afflst->getlinenum())) {
     300           0 :         finishFileMgr(afflst);
     301           0 :         return 1;
     302             :       }
     303           0 :       if (encoding == "UTF-8") {
     304           0 :         utf8 = 1;
     305             : #ifndef OPENOFFICEORG
     306             : #ifndef MOZILLA_CLIENT
     307             :         initialize_utf_tbl();
     308             : #endif
     309             : #endif
     310             :       }
     311             :     }
     312             : 
     313             :     /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left
     314             :      * writing system */
     315           0 :     if (line.compare(0, 15, "COMPLEXPREFIXES", 15) == 0)
     316           0 :       complexprefixes = 1;
     317             : 
     318             :     /* parse in the flag used by the controlled compound words */
     319           0 :     if (line.compare(0, 12, "COMPOUNDFLAG", 12) == 0) {
     320           0 :       if (!parse_flag(line, &compoundflag, afflst)) {
     321           0 :         finishFileMgr(afflst);
     322           0 :         return 1;
     323             :       }
     324             :     }
     325             : 
     326             :     /* parse in the flag used by compound words */
     327           0 :     if (line.compare(0, 13, "COMPOUNDBEGIN", 13) == 0) {
     328           0 :       if (complexprefixes) {
     329           0 :         if (!parse_flag(line, &compoundend, afflst)) {
     330           0 :           finishFileMgr(afflst);
     331           0 :           return 1;
     332             :         }
     333             :       } else {
     334           0 :         if (!parse_flag(line, &compoundbegin, afflst)) {
     335           0 :           finishFileMgr(afflst);
     336           0 :           return 1;
     337             :         }
     338             :       }
     339             :     }
     340             : 
     341             :     /* parse in the flag used by compound words */
     342           0 :     if (line.compare(0, 14, "COMPOUNDMIDDLE", 14) == 0) {
     343           0 :       if (!parse_flag(line, &compoundmiddle, afflst)) {
     344           0 :         finishFileMgr(afflst);
     345           0 :         return 1;
     346             :       }
     347             :     }
     348             : 
     349             :     /* parse in the flag used by compound words */
     350           0 :     if (line.compare(0, 11, "COMPOUNDEND", 11) == 0) {
     351           0 :       if (complexprefixes) {
     352           0 :         if (!parse_flag(line, &compoundbegin, afflst)) {
     353           0 :           finishFileMgr(afflst);
     354           0 :           return 1;
     355             :         }
     356             :       } else {
     357           0 :         if (!parse_flag(line, &compoundend, afflst)) {
     358           0 :           finishFileMgr(afflst);
     359           0 :           return 1;
     360             :         }
     361             :       }
     362             :     }
     363             : 
     364             :     /* parse in the data used by compound_check() method */
     365           0 :     if (line.compare(0, 15, "COMPOUNDWORDMAX", 15) == 0) {
     366           0 :       if (!parse_num(line, &cpdwordmax, afflst)) {
     367           0 :         finishFileMgr(afflst);
     368           0 :         return 1;
     369             :       }
     370             :     }
     371             : 
     372             :     /* parse in the flag sign compounds in dictionary */
     373           0 :     if (line.compare(0, 12, "COMPOUNDROOT", 12) == 0) {
     374           0 :       if (!parse_flag(line, &compoundroot, afflst)) {
     375           0 :         finishFileMgr(afflst);
     376           0 :         return 1;
     377             :       }
     378             :     }
     379             : 
     380             :     /* parse in the flag used by compound_check() method */
     381           0 :     if (line.compare(0, 18, "COMPOUNDPERMITFLAG", 18) == 0) {
     382           0 :       if (!parse_flag(line, &compoundpermitflag, afflst)) {
     383           0 :         finishFileMgr(afflst);
     384           0 :         return 1;
     385             :       }
     386             :     }
     387             : 
     388             :     /* parse in the flag used by compound_check() method */
     389           0 :     if (line.compare(0, 18, "COMPOUNDFORBIDFLAG", 18) == 0) {
     390           0 :       if (!parse_flag(line, &compoundforbidflag, afflst)) {
     391           0 :         finishFileMgr(afflst);
     392           0 :         return 1;
     393             :       }
     394             :     }
     395             : 
     396           0 :     if (line.compare(0, 20, "COMPOUNDMORESUFFIXES", 20) == 0) {
     397           0 :       compoundmoresuffixes = 1;
     398             :     }
     399             : 
     400           0 :     if (line.compare(0, 16, "CHECKCOMPOUNDDUP", 16) == 0) {
     401           0 :       checkcompounddup = 1;
     402             :     }
     403             : 
     404           0 :     if (line.compare(0, 16, "CHECKCOMPOUNDREP", 16) == 0) {
     405           0 :       checkcompoundrep = 1;
     406             :     }
     407             : 
     408           0 :     if (line.compare(0, 19, "CHECKCOMPOUNDTRIPLE", 19) == 0) {
     409           0 :       checkcompoundtriple = 1;
     410             :     }
     411             : 
     412           0 :     if (line.compare(0, 16, "SIMPLIFIEDTRIPLE", 16) == 0) {
     413           0 :       simplifiedtriple = 1;
     414             :     }
     415             : 
     416           0 :     if (line.compare(0, 17, "CHECKCOMPOUNDCASE", 17) == 0) {
     417           0 :       checkcompoundcase = 1;
     418             :     }
     419             : 
     420           0 :     if (line.compare(0, 9, "NOSUGGEST", 9) == 0) {
     421           0 :       if (!parse_flag(line, &nosuggest, afflst)) {
     422           0 :         finishFileMgr(afflst);
     423           0 :         return 1;
     424             :       }
     425             :     }
     426             : 
     427           0 :     if (line.compare(0, 14, "NONGRAMSUGGEST", 14) == 0) {
     428           0 :       if (!parse_flag(line, &nongramsuggest, afflst)) {
     429           0 :         finishFileMgr(afflst);
     430           0 :         return 1;
     431             :       }
     432             :     }
     433             : 
     434             :     /* parse in the flag used by forbidden words */
     435           0 :     if (line.compare(0, 13, "FORBIDDENWORD", 13) == 0) {
     436           0 :       if (!parse_flag(line, &forbiddenword, afflst)) {
     437           0 :         finishFileMgr(afflst);
     438           0 :         return 1;
     439             :       }
     440             :     }
     441             : 
     442             :     /* parse in the flag used by forbidden words */
     443           0 :     if (line.compare(0, 13, "LEMMA_PRESENT", 13) == 0) {
     444           0 :       if (!parse_flag(line, &lemma_present, afflst)) {
     445           0 :         finishFileMgr(afflst);
     446           0 :         return 1;
     447             :       }
     448             :     }
     449             : 
     450             :     /* parse in the flag used by circumfixes */
     451           0 :     if (line.compare(0, 9, "CIRCUMFIX", 9) == 0) {
     452           0 :       if (!parse_flag(line, &circumfix, afflst)) {
     453           0 :         finishFileMgr(afflst);
     454           0 :         return 1;
     455             :       }
     456             :     }
     457             : 
     458             :     /* parse in the flag used by fogemorphemes */
     459           0 :     if (line.compare(0, 14, "ONLYINCOMPOUND", 14) == 0) {
     460           0 :       if (!parse_flag(line, &onlyincompound, afflst)) {
     461           0 :         finishFileMgr(afflst);
     462           0 :         return 1;
     463             :       }
     464             :     }
     465             : 
     466             :     /* parse in the flag used by `needaffixs' */
     467           0 :     if (line.compare(0, 10, "PSEUDOROOT", 10) == 0) {
     468           0 :       if (!parse_flag(line, &needaffix, afflst)) {
     469           0 :         finishFileMgr(afflst);
     470           0 :         return 1;
     471             :       }
     472             :     }
     473             : 
     474             :     /* parse in the flag used by `needaffixs' */
     475           0 :     if (line.compare(0, 9, "NEEDAFFIX", 9) == 0) {
     476           0 :       if (!parse_flag(line, &needaffix, afflst)) {
     477           0 :         finishFileMgr(afflst);
     478           0 :         return 1;
     479             :       }
     480             :     }
     481             : 
     482             :     /* parse in the minimal length for words in compounds */
     483           0 :     if (line.compare(0, 11, "COMPOUNDMIN", 11) == 0) {
     484           0 :       if (!parse_num(line, &cpdmin, afflst)) {
     485           0 :         finishFileMgr(afflst);
     486           0 :         return 1;
     487             :       }
     488           0 :       if (cpdmin < 1)
     489           0 :         cpdmin = 1;
     490             :     }
     491             : 
     492             :     /* parse in the max. words and syllables in compounds */
     493           0 :     if (line.compare(0, 16, "COMPOUNDSYLLABLE", 16) == 0) {
     494           0 :       if (!parse_cpdsyllable(line, afflst)) {
     495           0 :         finishFileMgr(afflst);
     496           0 :         return 1;
     497             :       }
     498             :     }
     499             : 
     500             :     /* parse in the flag used by compound_check() method */
     501           0 :     if (line.compare(0, 11, "SYLLABLENUM", 11) == 0) {
     502           0 :       if (!parse_string(line, cpdsyllablenum, afflst->getlinenum())) {
     503           0 :         finishFileMgr(afflst);
     504           0 :         return 1;
     505             :       }
     506             :     }
     507             : 
     508             :     /* parse in the flag used by the controlled compound words */
     509           0 :     if (line.compare(0, 8, "CHECKNUM", 8) == 0) {
     510           0 :       checknum = 1;
     511             :     }
     512             : 
     513             :     /* parse in the extra word characters */
     514           0 :     if (line.compare(0, 9, "WORDCHARS", 9) == 0) {
     515           0 :       if (!parse_array(line, wordchars, wordchars_utf16,
     516             :                        utf8, afflst->getlinenum())) {
     517           0 :         finishFileMgr(afflst);
     518           0 :         return 1;
     519             :       }
     520             :     }
     521             : 
     522             :     /* parse in the ignored characters (for example, Arabic optional diacretics
     523             :      * charachters */
     524           0 :     if (line.compare(0, 6, "IGNORE", 6) == 0) {
     525           0 :       if (!parse_array(line, ignorechars, ignorechars_utf16,
     526             :                        utf8, afflst->getlinenum())) {
     527           0 :         finishFileMgr(afflst);
     528           0 :         return 1;
     529             :       }
     530             :     }
     531             : 
     532             :     /* parse in the typical fault correcting table */
     533           0 :     if (line.compare(0, 3, "REP", 3) == 0) {
     534           0 :       if (!parse_reptable(line, afflst)) {
     535           0 :         finishFileMgr(afflst);
     536           0 :         return 1;
     537             :       }
     538             :     }
     539             : 
     540             :     /* parse in the input conversion table */
     541           0 :     if (line.compare(0, 5, "ICONV", 5) == 0) {
     542           0 :       if (!parse_convtable(line, afflst, &iconvtable, "ICONV")) {
     543           0 :         finishFileMgr(afflst);
     544           0 :         return 1;
     545             :       }
     546             :     }
     547             : 
     548             :     /* parse in the input conversion table */
     549           0 :     if (line.compare(0, 5, "OCONV", 5) == 0) {
     550           0 :       if (!parse_convtable(line, afflst, &oconvtable, "OCONV")) {
     551           0 :         finishFileMgr(afflst);
     552           0 :         return 1;
     553             :       }
     554             :     }
     555             : 
     556             :     /* parse in the phonetic translation table */
     557           0 :     if (line.compare(0, 5, "PHONE", 5) == 0) {
     558           0 :       if (!parse_phonetable(line, afflst)) {
     559           0 :         finishFileMgr(afflst);
     560           0 :         return 1;
     561             :       }
     562             :     }
     563             : 
     564             :     /* parse in the checkcompoundpattern table */
     565           0 :     if (line.compare(0, 20, "CHECKCOMPOUNDPATTERN", 20) == 0) {
     566           0 :       if (!parse_checkcpdtable(line, afflst)) {
     567           0 :         finishFileMgr(afflst);
     568           0 :         return 1;
     569             :       }
     570             :     }
     571             : 
     572             :     /* parse in the defcompound table */
     573           0 :     if (line.compare(0, 12, "COMPOUNDRULE", 12) == 0) {
     574           0 :       if (!parse_defcpdtable(line, afflst)) {
     575           0 :         finishFileMgr(afflst);
     576           0 :         return 1;
     577             :       }
     578             :     }
     579             : 
     580             :     /* parse in the related character map table */
     581           0 :     if (line.compare(0, 3, "MAP", 3) == 0) {
     582           0 :       if (!parse_maptable(line, afflst)) {
     583           0 :         finishFileMgr(afflst);
     584           0 :         return 1;
     585             :       }
     586             :     }
     587             : 
     588             :     /* parse in the word breakpoints table */
     589           0 :     if (line.compare(0, 5, "BREAK", 5) == 0) {
     590           0 :       if (!parse_breaktable(line, afflst)) {
     591           0 :         finishFileMgr(afflst);
     592           0 :         return 1;
     593             :       }
     594             :     }
     595             : 
     596             :     /* parse in the language for language specific codes */
     597           0 :     if (line.compare(0, 4, "LANG", 4) == 0) {
     598           0 :       if (!parse_string(line, lang, afflst->getlinenum())) {
     599           0 :         finishFileMgr(afflst);
     600           0 :         return 1;
     601             :       }
     602           0 :       langnum = get_lang_num(lang);
     603             :     }
     604             : 
     605           0 :     if (line.compare(0, 7, "VERSION", 7) == 0) {
     606           0 :       size_t startpos = line.find_first_not_of(" \t", 7);
     607           0 :       if (startpos != std::string::npos) {
     608           0 :           version = line.substr(startpos);
     609             :       }
     610             :     }
     611             : 
     612           0 :     if (line.compare(0, 12, "MAXNGRAMSUGS", 12) == 0) {
     613           0 :       if (!parse_num(line, &maxngramsugs, afflst)) {
     614           0 :         finishFileMgr(afflst);
     615           0 :         return 1;
     616             :       }
     617             :     }
     618             : 
     619           0 :     if (line.compare(0, 11, "ONLYMAXDIFF", 11) == 0)
     620           0 :       onlymaxdiff = 1;
     621             : 
     622           0 :     if (line.compare(0, 7, "MAXDIFF", 7) == 0) {
     623           0 :       if (!parse_num(line, &maxdiff, afflst)) {
     624           0 :         finishFileMgr(afflst);
     625           0 :         return 1;
     626             :       }
     627             :     }
     628             : 
     629           0 :     if (line.compare(0, 10, "MAXCPDSUGS", 10) == 0) {
     630           0 :       if (!parse_num(line, &maxcpdsugs, afflst)) {
     631           0 :         finishFileMgr(afflst);
     632           0 :         return 1;
     633             :       }
     634             :     }
     635             : 
     636           0 :     if (line.compare(0, 11, "NOSPLITSUGS", 11) == 0) {
     637           0 :       nosplitsugs = 1;
     638             :     }
     639             : 
     640           0 :     if (line.compare(0, 9, "FULLSTRIP", 9) == 0) {
     641           0 :       fullstrip = 1;
     642             :     }
     643             : 
     644           0 :     if (line.compare(0, 12, "SUGSWITHDOTS", 12) == 0) {
     645           0 :       sugswithdots = 1;
     646             :     }
     647             : 
     648             :     /* parse in the flag used by forbidden words */
     649           0 :     if (line.compare(0, 8, "KEEPCASE", 8) == 0) {
     650           0 :       if (!parse_flag(line, &keepcase, afflst)) {
     651           0 :         finishFileMgr(afflst);
     652           0 :         return 1;
     653             :       }
     654             :     }
     655             : 
     656             :     /* parse in the flag used by `forceucase' */
     657           0 :     if (line.compare(0, 10, "FORCEUCASE", 10) == 0) {
     658           0 :       if (!parse_flag(line, &forceucase, afflst)) {
     659           0 :         finishFileMgr(afflst);
     660           0 :         return 1;
     661             :       }
     662             :     }
     663             : 
     664             :     /* parse in the flag used by `warn' */
     665           0 :     if (line.compare(0, 4, "WARN", 4) == 0) {
     666           0 :       if (!parse_flag(line, &warn, afflst)) {
     667           0 :         finishFileMgr(afflst);
     668           0 :         return 1;
     669             :       }
     670             :     }
     671             : 
     672           0 :     if (line.compare(0, 10, "FORBIDWARN", 10) == 0) {
     673           0 :       forbidwarn = 1;
     674             :     }
     675             : 
     676             :     /* parse in the flag used by the affix generator */
     677           0 :     if (line.compare(0, 11, "SUBSTANDARD", 11) == 0) {
     678           0 :       if (!parse_flag(line, &substandard, afflst)) {
     679           0 :         finishFileMgr(afflst);
     680           0 :         return 1;
     681             :       }
     682             :     }
     683             : 
     684           0 :     if (line.compare(0, 11, "CHECKSHARPS", 11) == 0) {
     685           0 :       checksharps = 1;
     686             :     }
     687             : 
     688             :     /* parse this affix: P - prefix, S - suffix */
     689             :     // affix type
     690           0 :     char ft = ' ';
     691           0 :     if (line.compare(0, 3, "PFX", 3) == 0)
     692           0 :       ft = complexprefixes ? 'S' : 'P';
     693           0 :     if (line.compare(0, 3, "SFX", 3) == 0)
     694           0 :       ft = complexprefixes ? 'P' : 'S';
     695           0 :     if (ft != ' ') {
     696           0 :       if (dupflags_ini) {
     697           0 :         memset(dupflags, 0, sizeof(dupflags));
     698           0 :         dupflags_ini = 0;
     699             :       }
     700           0 :       if (!parse_affix(line, ft, afflst, dupflags)) {
     701           0 :         finishFileMgr(afflst);
     702           0 :         return 1;
     703             :       }
     704             :     }
     705             :   }
     706             : 
     707           0 :   finishFileMgr(afflst);
     708             :   // affix trees are sorted now
     709             : 
     710             :   // now we can speed up performance greatly taking advantage of the
     711             :   // relationship between the affixes and the idea of "subsets".
     712             : 
     713             :   // View each prefix as a potential leading subset of another and view
     714             :   // each suffix (reversed) as a potential trailing subset of another.
     715             : 
     716             :   // To illustrate this relationship if we know the prefix "ab" is found in the
     717             :   // word to examine, only prefixes that "ab" is a leading subset of need be
     718             :   // examined.
     719             :   // Furthermore is "ab" is not present then none of the prefixes that "ab" is
     720             :   // is a subset need be examined.
     721             :   // The same argument goes for suffix string that are reversed.
     722             : 
     723             :   // Then to top this off why not examine the first char of the word to quickly
     724             :   // limit the set of prefixes to examine (i.e. the prefixes to examine must
     725             :   // be leading supersets of the first character of the word (if they exist)
     726             : 
     727             :   // To take advantage of this "subset" relationship, we need to add two links
     728             :   // from entry.  One to take next if the current prefix is found (call it
     729             :   // nexteq)
     730             :   // and one to take next if the current prefix is not found (call it nextne).
     731             : 
     732             :   // Since we have built ordered lists, all that remains is to properly
     733             :   // initialize
     734             :   // the nextne and nexteq pointers that relate them
     735             : 
     736           0 :   process_pfx_order();
     737           0 :   process_sfx_order();
     738             : 
     739             :   /* get encoding for CHECKCOMPOUNDCASE */
     740           0 :   if (!utf8) {
     741           0 :     csconv = get_current_cs(get_encoding());
     742           0 :     for (int i = 0; i <= 255; i++) {
     743           0 :       if ((csconv[i].cupper != csconv[i].clower) &&
     744           0 :           (wordchars.find((char)i) == std::string::npos)) {
     745           0 :         wordchars.push_back((char)i);
     746             :       }
     747             :     }
     748             : 
     749             :   }
     750             : 
     751             :   // default BREAK definition
     752           0 :   if (!parsedbreaktable) {
     753           0 :     breaktable.push_back("-");
     754           0 :     breaktable.push_back("^-");
     755           0 :     breaktable.push_back("-$");
     756           0 :     parsedbreaktable = true;
     757             :   }
     758           0 :   return 0;
     759             : }
     760             : 
     761             : // we want to be able to quickly access prefix information
     762             : // both by prefix flag, and sorted by prefix string itself
     763             : // so we need to set up two indexes
     764             : 
     765           0 : int AffixMgr::build_pfxtree(PfxEntry* pfxptr) {
     766             :   PfxEntry* ptr;
     767             :   PfxEntry* pptr;
     768           0 :   PfxEntry* ep = pfxptr;
     769             : 
     770             :   // get the right starting points
     771           0 :   const char* key = ep->getKey();
     772           0 :   const unsigned char flg = (unsigned char)(ep->getFlag() & 0x00FF);
     773             : 
     774             :   // first index by flag which must exist
     775           0 :   ptr = pFlag[flg];
     776           0 :   ep->setFlgNxt(ptr);
     777           0 :   pFlag[flg] = ep;
     778             : 
     779             :   // handle the special case of null affix string
     780           0 :   if (strlen(key) == 0) {
     781             :     // always inset them at head of list at element 0
     782           0 :     ptr = pStart[0];
     783           0 :     ep->setNext(ptr);
     784           0 :     pStart[0] = ep;
     785           0 :     return 0;
     786             :   }
     787             : 
     788             :   // now handle the normal case
     789           0 :   ep->setNextEQ(NULL);
     790           0 :   ep->setNextNE(NULL);
     791             : 
     792           0 :   unsigned char sp = *((const unsigned char*)key);
     793           0 :   ptr = pStart[sp];
     794             : 
     795             :   // handle the first insert
     796           0 :   if (!ptr) {
     797           0 :     pStart[sp] = ep;
     798           0 :     return 0;
     799             :   }
     800             : 
     801             :   // otherwise use binary tree insertion so that a sorted
     802             :   // list can easily be generated later
     803           0 :   pptr = NULL;
     804             :   for (;;) {
     805           0 :     pptr = ptr;
     806           0 :     if (strcmp(ep->getKey(), ptr->getKey()) <= 0) {
     807           0 :       ptr = ptr->getNextEQ();
     808           0 :       if (!ptr) {
     809           0 :         pptr->setNextEQ(ep);
     810           0 :         break;
     811             :       }
     812             :     } else {
     813           0 :       ptr = ptr->getNextNE();
     814           0 :       if (!ptr) {
     815           0 :         pptr->setNextNE(ep);
     816           0 :         break;
     817             :       }
     818             :     }
     819             :   }
     820           0 :   return 0;
     821             : }
     822             : 
     823             : // we want to be able to quickly access suffix information
     824             : // both by suffix flag, and sorted by the reverse of the
     825             : // suffix string itself; so we need to set up two indexes
     826           0 : int AffixMgr::build_sfxtree(SfxEntry* sfxptr) {
     827             : 
     828           0 :   sfxptr->initReverseWord();
     829             : 
     830             :   SfxEntry* ptr;
     831             :   SfxEntry* pptr;
     832           0 :   SfxEntry* ep = sfxptr;
     833             : 
     834             :   /* get the right starting point */
     835           0 :   const char* key = ep->getKey();
     836           0 :   const unsigned char flg = (unsigned char)(ep->getFlag() & 0x00FF);
     837             : 
     838             :   // first index by flag which must exist
     839           0 :   ptr = sFlag[flg];
     840           0 :   ep->setFlgNxt(ptr);
     841           0 :   sFlag[flg] = ep;
     842             : 
     843             :   // next index by affix string
     844             : 
     845             :   // handle the special case of null affix string
     846           0 :   if (strlen(key) == 0) {
     847             :     // always inset them at head of list at element 0
     848           0 :     ptr = sStart[0];
     849           0 :     ep->setNext(ptr);
     850           0 :     sStart[0] = ep;
     851           0 :     return 0;
     852             :   }
     853             : 
     854             :   // now handle the normal case
     855           0 :   ep->setNextEQ(NULL);
     856           0 :   ep->setNextNE(NULL);
     857             : 
     858           0 :   unsigned char sp = *((const unsigned char*)key);
     859           0 :   ptr = sStart[sp];
     860             : 
     861             :   // handle the first insert
     862           0 :   if (!ptr) {
     863           0 :     sStart[sp] = ep;
     864           0 :     return 0;
     865             :   }
     866             : 
     867             :   // otherwise use binary tree insertion so that a sorted
     868             :   // list can easily be generated later
     869           0 :   pptr = NULL;
     870             :   for (;;) {
     871           0 :     pptr = ptr;
     872           0 :     if (strcmp(ep->getKey(), ptr->getKey()) <= 0) {
     873           0 :       ptr = ptr->getNextEQ();
     874           0 :       if (!ptr) {
     875           0 :         pptr->setNextEQ(ep);
     876           0 :         break;
     877             :       }
     878             :     } else {
     879           0 :       ptr = ptr->getNextNE();
     880           0 :       if (!ptr) {
     881           0 :         pptr->setNextNE(ep);
     882           0 :         break;
     883             :       }
     884             :     }
     885             :   }
     886           0 :   return 0;
     887             : }
     888             : 
     889             : // convert from binary tree to sorted list
     890           0 : int AffixMgr::process_pfx_tree_to_list() {
     891           0 :   for (int i = 1; i < SETSIZE; i++) {
     892           0 :     pStart[i] = process_pfx_in_order(pStart[i], NULL);
     893             :   }
     894           0 :   return 0;
     895             : }
     896             : 
     897           0 : PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr) {
     898           0 :   if (ptr) {
     899           0 :     nptr = process_pfx_in_order(ptr->getNextNE(), nptr);
     900           0 :     ptr->setNext(nptr);
     901           0 :     nptr = process_pfx_in_order(ptr->getNextEQ(), ptr);
     902             :   }
     903           0 :   return nptr;
     904             : }
     905             : 
     906             : // convert from binary tree to sorted list
     907           0 : int AffixMgr::process_sfx_tree_to_list() {
     908           0 :   for (int i = 1; i < SETSIZE; i++) {
     909           0 :     sStart[i] = process_sfx_in_order(sStart[i], NULL);
     910             :   }
     911           0 :   return 0;
     912             : }
     913             : 
     914           0 : SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr) {
     915           0 :   if (ptr) {
     916           0 :     nptr = process_sfx_in_order(ptr->getNextNE(), nptr);
     917           0 :     ptr->setNext(nptr);
     918           0 :     nptr = process_sfx_in_order(ptr->getNextEQ(), ptr);
     919             :   }
     920           0 :   return nptr;
     921             : }
     922             : 
     923             : // reinitialize the PfxEntry links NextEQ and NextNE to speed searching
     924             : // using the idea of leading subsets this time
     925           0 : int AffixMgr::process_pfx_order() {
     926             :   PfxEntry* ptr;
     927             : 
     928             :   // loop through each prefix list starting point
     929           0 :   for (int i = 1; i < SETSIZE; i++) {
     930           0 :     ptr = pStart[i];
     931             : 
     932             :     // look through the remainder of the list
     933             :     //  and find next entry with affix that
     934             :     // the current one is not a subset of
     935             :     // mark that as destination for NextNE
     936             :     // use next in list that you are a subset
     937             :     // of as NextEQ
     938             : 
     939           0 :     for (; ptr != NULL; ptr = ptr->getNext()) {
     940           0 :       PfxEntry* nptr = ptr->getNext();
     941           0 :       for (; nptr != NULL; nptr = nptr->getNext()) {
     942           0 :         if (!isSubset(ptr->getKey(), nptr->getKey()))
     943           0 :           break;
     944             :       }
     945           0 :       ptr->setNextNE(nptr);
     946           0 :       ptr->setNextEQ(NULL);
     947           0 :       if ((ptr->getNext()) &&
     948           0 :           isSubset(ptr->getKey(), (ptr->getNext())->getKey()))
     949           0 :         ptr->setNextEQ(ptr->getNext());
     950             :     }
     951             : 
     952             :     // now clean up by adding smart search termination strings:
     953             :     // if you are already a superset of the previous prefix
     954             :     // but not a subset of the next, search can end here
     955             :     // so set NextNE properly
     956             : 
     957           0 :     ptr = pStart[i];
     958           0 :     for (; ptr != NULL; ptr = ptr->getNext()) {
     959           0 :       PfxEntry* nptr = ptr->getNext();
     960           0 :       PfxEntry* mptr = NULL;
     961           0 :       for (; nptr != NULL; nptr = nptr->getNext()) {
     962           0 :         if (!isSubset(ptr->getKey(), nptr->getKey()))
     963           0 :           break;
     964           0 :         mptr = nptr;
     965             :       }
     966           0 :       if (mptr)
     967           0 :         mptr->setNextNE(NULL);
     968             :     }
     969             :   }
     970           0 :   return 0;
     971             : }
     972             : 
     973             : // initialize the SfxEntry links NextEQ and NextNE to speed searching
     974             : // using the idea of leading subsets this time
     975           0 : int AffixMgr::process_sfx_order() {
     976             :   SfxEntry* ptr;
     977             : 
     978             :   // loop through each prefix list starting point
     979           0 :   for (int i = 1; i < SETSIZE; i++) {
     980           0 :     ptr = sStart[i];
     981             : 
     982             :     // look through the remainder of the list
     983             :     //  and find next entry with affix that
     984             :     // the current one is not a subset of
     985             :     // mark that as destination for NextNE
     986             :     // use next in list that you are a subset
     987             :     // of as NextEQ
     988             : 
     989           0 :     for (; ptr != NULL; ptr = ptr->getNext()) {
     990           0 :       SfxEntry* nptr = ptr->getNext();
     991           0 :       for (; nptr != NULL; nptr = nptr->getNext()) {
     992           0 :         if (!isSubset(ptr->getKey(), nptr->getKey()))
     993           0 :           break;
     994             :       }
     995           0 :       ptr->setNextNE(nptr);
     996           0 :       ptr->setNextEQ(NULL);
     997           0 :       if ((ptr->getNext()) &&
     998           0 :           isSubset(ptr->getKey(), (ptr->getNext())->getKey()))
     999           0 :         ptr->setNextEQ(ptr->getNext());
    1000             :     }
    1001             : 
    1002             :     // now clean up by adding smart search termination strings:
    1003             :     // if you are already a superset of the previous suffix
    1004             :     // but not a subset of the next, search can end here
    1005             :     // so set NextNE properly
    1006             : 
    1007           0 :     ptr = sStart[i];
    1008           0 :     for (; ptr != NULL; ptr = ptr->getNext()) {
    1009           0 :       SfxEntry* nptr = ptr->getNext();
    1010           0 :       SfxEntry* mptr = NULL;
    1011           0 :       for (; nptr != NULL; nptr = nptr->getNext()) {
    1012           0 :         if (!isSubset(ptr->getKey(), nptr->getKey()))
    1013           0 :           break;
    1014           0 :         mptr = nptr;
    1015             :       }
    1016           0 :       if (mptr)
    1017           0 :         mptr->setNextNE(NULL);
    1018             :     }
    1019             :   }
    1020           0 :   return 0;
    1021             : }
    1022             : 
    1023             : // add flags to the result for dictionary debugging
    1024           0 : std::string& AffixMgr::debugflag(std::string& result, unsigned short flag) {
    1025           0 :   char* st = encode_flag(flag);
    1026           0 :   result.append(" ");
    1027           0 :   result.append(MORPH_FLAG);
    1028           0 :   if (st) {
    1029           0 :     result.append(st);
    1030           0 :     free(st);
    1031             :   }
    1032           0 :   return result;
    1033             : }
    1034             : 
    1035             : // calculate the character length of the condition
    1036           0 : int AffixMgr::condlen(const char* st) {
    1037           0 :   int l = 0;
    1038           0 :   bool group = false;
    1039           0 :   for (; *st; st++) {
    1040           0 :     if (*st == '[') {
    1041           0 :       group = true;
    1042           0 :       l++;
    1043           0 :     } else if (*st == ']')
    1044           0 :       group = false;
    1045           0 :     else if (!group && (!utf8 || (!(*st & 0x80) || ((*st & 0xc0) == 0x80))))
    1046           0 :       l++;
    1047             :   }
    1048           0 :   return l;
    1049             : }
    1050             : 
    1051           0 : int AffixMgr::encodeit(AffEntry& entry, const char* cs) {
    1052           0 :   if (strcmp(cs, ".") != 0) {
    1053           0 :     entry.numconds = (char)condlen(cs);
    1054           0 :     const size_t cslen = strlen(cs);
    1055           0 :     const size_t short_part = std::min<size_t>(MAXCONDLEN, cslen);
    1056           0 :     memcpy(entry.c.conds, cs, short_part);
    1057           0 :     if (short_part < MAXCONDLEN) {
    1058             :       //blank out the remaining space
    1059           0 :       memset(entry.c.conds + short_part, 0, MAXCONDLEN - short_part);
    1060           0 :     } else if (cs[MAXCONDLEN]) {
    1061             :       //there is more conditions than fit in fixed space, so its
    1062             :       //a long condition
    1063           0 :       entry.opts += aeLONGCOND;
    1064           0 :       entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1);
    1065           0 :       if (!entry.c.l.conds2)
    1066           0 :         return 1;
    1067             :     }
    1068             :   } else {
    1069           0 :     entry.numconds = 0;
    1070           0 :     entry.c.conds[0] = '\0';
    1071             :   }
    1072           0 :   return 0;
    1073             : }
    1074             : 
    1075             : // return 1 if s1 is a leading subset of s2 (dots are for infixes)
    1076           0 : inline int AffixMgr::isSubset(const char* s1, const char* s2) {
    1077           0 :   while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) {
    1078           0 :     s1++;
    1079           0 :     s2++;
    1080             :   }
    1081           0 :   return (*s1 == '\0');
    1082             : }
    1083             : 
    1084             : // check word for prefixes
    1085           0 : struct hentry* AffixMgr::prefix_check(const char* word,
    1086             :                                       int len,
    1087             :                                       char in_compound,
    1088             :                                       const FLAG needflag) {
    1089           0 :   struct hentry* rv = NULL;
    1090             : 
    1091           0 :   pfx = NULL;
    1092           0 :   pfxappnd = NULL;
    1093           0 :   sfxappnd = NULL;
    1094           0 :   sfxextra = 0;
    1095             : 
    1096             :   // first handle the special case of 0 length prefixes
    1097           0 :   PfxEntry* pe = pStart[0];
    1098           0 :   while (pe) {
    1099           0 :     if (
    1100             :         // fogemorpheme
    1101           0 :         ((in_compound != IN_CPD_NOT) ||
    1102           0 :          !(pe->getCont() &&
    1103           0 :            (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) &&
    1104             :         // permit prefixes in compounds
    1105           0 :         ((in_compound != IN_CPD_END) ||
    1106           0 :          (pe->getCont() &&
    1107           0 :           (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen()))))) {
    1108             :       // check prefix
    1109           0 :       rv = pe->checkword(word, len, in_compound, needflag);
    1110           0 :       if (rv) {
    1111           0 :         pfx = pe;  // BUG: pfx not stateless
    1112           0 :         return rv;
    1113             :       }
    1114             :     }
    1115           0 :     pe = pe->getNext();
    1116             :   }
    1117             : 
    1118             :   // now handle the general case
    1119           0 :   unsigned char sp = *((const unsigned char*)word);
    1120           0 :   PfxEntry* pptr = pStart[sp];
    1121             : 
    1122           0 :   while (pptr) {
    1123           0 :     if (isSubset(pptr->getKey(), word)) {
    1124           0 :       if (
    1125             :           // fogemorpheme
    1126           0 :           ((in_compound != IN_CPD_NOT) ||
    1127           0 :            !(pptr->getCont() &&
    1128           0 :              (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) &&
    1129             :           // permit prefixes in compounds
    1130           0 :           ((in_compound != IN_CPD_END) ||
    1131           0 :            (pptr->getCont() && (TESTAFF(pptr->getCont(), compoundpermitflag,
    1132             :                                         pptr->getContLen()))))) {
    1133             :         // check prefix
    1134           0 :         rv = pptr->checkword(word, len, in_compound, needflag);
    1135           0 :         if (rv) {
    1136           0 :           pfx = pptr;  // BUG: pfx not stateless
    1137           0 :           return rv;
    1138             :         }
    1139             :       }
    1140           0 :       pptr = pptr->getNextEQ();
    1141             :     } else {
    1142           0 :       pptr = pptr->getNextNE();
    1143             :     }
    1144             :   }
    1145             : 
    1146           0 :   return NULL;
    1147             : }
    1148             : 
    1149             : // check word for prefixes
    1150           0 : struct hentry* AffixMgr::prefix_check_twosfx(const char* word,
    1151             :                                              int len,
    1152             :                                              char in_compound,
    1153             :                                              const FLAG needflag) {
    1154           0 :   struct hentry* rv = NULL;
    1155             : 
    1156           0 :   pfx = NULL;
    1157           0 :   sfxappnd = NULL;
    1158           0 :   sfxextra = 0;
    1159             : 
    1160             :   // first handle the special case of 0 length prefixes
    1161           0 :   PfxEntry* pe = pStart[0];
    1162             : 
    1163           0 :   while (pe) {
    1164           0 :     rv = pe->check_twosfx(word, len, in_compound, needflag);
    1165           0 :     if (rv)
    1166           0 :       return rv;
    1167           0 :     pe = pe->getNext();
    1168             :   }
    1169             : 
    1170             :   // now handle the general case
    1171           0 :   unsigned char sp = *((const unsigned char*)word);
    1172           0 :   PfxEntry* pptr = pStart[sp];
    1173             : 
    1174           0 :   while (pptr) {
    1175           0 :     if (isSubset(pptr->getKey(), word)) {
    1176           0 :       rv = pptr->check_twosfx(word, len, in_compound, needflag);
    1177           0 :       if (rv) {
    1178           0 :         pfx = pptr;
    1179           0 :         return rv;
    1180             :       }
    1181           0 :       pptr = pptr->getNextEQ();
    1182             :     } else {
    1183           0 :       pptr = pptr->getNextNE();
    1184             :     }
    1185             :   }
    1186             : 
    1187           0 :   return NULL;
    1188             : }
    1189             : 
    1190             : // check word for prefixes
    1191           0 : std::string AffixMgr::prefix_check_morph(const char* word,
    1192             :                                          int len,
    1193             :                                          char in_compound,
    1194             :                                          const FLAG needflag) {
    1195             : 
    1196           0 :   std::string result;
    1197             : 
    1198           0 :   pfx = NULL;
    1199           0 :   sfxappnd = NULL;
    1200           0 :   sfxextra = 0;
    1201             : 
    1202             :   // first handle the special case of 0 length prefixes
    1203           0 :   PfxEntry* pe = pStart[0];
    1204           0 :   while (pe) {
    1205           0 :     std::string st = pe->check_morph(word, len, in_compound, needflag);
    1206           0 :     if (!st.empty()) {
    1207           0 :       result.append(st);
    1208             :     }
    1209           0 :     pe = pe->getNext();
    1210             :   }
    1211             : 
    1212             :   // now handle the general case
    1213           0 :   unsigned char sp = *((const unsigned char*)word);
    1214           0 :   PfxEntry* pptr = pStart[sp];
    1215             : 
    1216           0 :   while (pptr) {
    1217           0 :     if (isSubset(pptr->getKey(), word)) {
    1218           0 :       std::string st = pptr->check_morph(word, len, in_compound, needflag);
    1219           0 :       if (!st.empty()) {
    1220             :         // fogemorpheme
    1221           0 :         if ((in_compound != IN_CPD_NOT) ||
    1222           0 :             !((pptr->getCont() && (TESTAFF(pptr->getCont(), onlyincompound,
    1223             :                                            pptr->getContLen()))))) {
    1224           0 :           result.append(st);
    1225           0 :           pfx = pptr;
    1226             :         }
    1227             :       }
    1228           0 :       pptr = pptr->getNextEQ();
    1229             :     } else {
    1230           0 :       pptr = pptr->getNextNE();
    1231             :     }
    1232             :   }
    1233             : 
    1234           0 :   return result;
    1235             : }
    1236             : 
    1237             : // check word for prefixes
    1238           0 : std::string AffixMgr::prefix_check_twosfx_morph(const char* word,
    1239             :                                                 int len,
    1240             :                                                 char in_compound,
    1241             :                                                 const FLAG needflag) {
    1242           0 :   std::string result;
    1243             : 
    1244           0 :   pfx = NULL;
    1245           0 :   sfxappnd = NULL;
    1246           0 :   sfxextra = 0;
    1247             : 
    1248             :   // first handle the special case of 0 length prefixes
    1249           0 :   PfxEntry* pe = pStart[0];
    1250           0 :   while (pe) {
    1251           0 :     std::string st = pe->check_twosfx_morph(word, len, in_compound, needflag);
    1252           0 :     if (!st.empty()) {
    1253           0 :       result.append(st);
    1254             :     }
    1255           0 :     pe = pe->getNext();
    1256             :   }
    1257             : 
    1258             :   // now handle the general case
    1259           0 :   unsigned char sp = *((const unsigned char*)word);
    1260           0 :   PfxEntry* pptr = pStart[sp];
    1261             : 
    1262           0 :   while (pptr) {
    1263           0 :     if (isSubset(pptr->getKey(), word)) {
    1264           0 :       std::string st = pptr->check_twosfx_morph(word, len, in_compound, needflag);
    1265           0 :       if (!st.empty()) {
    1266           0 :         result.append(st);
    1267           0 :         pfx = pptr;
    1268             :       }
    1269           0 :       pptr = pptr->getNextEQ();
    1270             :     } else {
    1271           0 :       pptr = pptr->getNextNE();
    1272             :     }
    1273             :   }
    1274             : 
    1275           0 :   return result;
    1276             : }
    1277             : 
    1278             : // Is word a non compound with a REP substitution (see checkcompoundrep)?
    1279           0 : int AffixMgr::cpdrep_check(const char* word, int wl) {
    1280             : 
    1281           0 :   if ((wl < 2) || reptable.empty())
    1282           0 :     return 0;
    1283             : 
    1284           0 :   for (size_t i = 0; i < reptable.size(); ++i) {
    1285           0 :     const char* r = word;
    1286           0 :     const size_t lenp = reptable[i].pattern.size();
    1287             :     // search every occurence of the pattern in the word
    1288           0 :     while ((r = strstr(r, reptable[i].pattern.c_str())) != NULL) {
    1289           0 :       std::string candidate(word);
    1290           0 :       size_t type = r == word && langnum != LANG_hu ? 1 : 0;
    1291           0 :       if (r - word + reptable[i].pattern.size() == lenp && langnum != LANG_hu)
    1292           0 :         type += 2;
    1293           0 :       candidate.replace(r - word, lenp, reptable[i].outstrings[type]);
    1294           0 :       if (candidate_check(candidate.c_str(), candidate.size()))
    1295           0 :         return 1;
    1296           0 :       ++r;  // search for the next letter
    1297             :     }
    1298             :   }
    1299             : 
    1300           0 :   return 0;
    1301             : }
    1302             : 
    1303             : // forbid compoundings when there are special patterns at word bound
    1304           0 : int AffixMgr::cpdpat_check(const char* word,
    1305             :                            int pos,
    1306             :                            hentry* r1,
    1307             :                            hentry* r2,
    1308             :                            const char /*affixed*/) {
    1309           0 :   for (size_t i = 0; i < checkcpdtable.size(); ++i) {
    1310             :     size_t len;
    1311           0 :     if (isSubset(checkcpdtable[i].pattern2.c_str(), word + pos) &&
    1312           0 :         (!r1 || !checkcpdtable[i].cond ||
    1313           0 :          (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) &&
    1314           0 :         (!r2 || !checkcpdtable[i].cond2 ||
    1315           0 :          (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) &&
    1316             :         // zero length pattern => only TESTAFF
    1317             :         // zero pattern (0/flag) => unmodified stem (zero affixes allowed)
    1318           0 :         (checkcpdtable[i].pattern.empty() ||
    1319           0 :          ((checkcpdtable[i].pattern[0] == '0' && r1->blen <= pos &&
    1320           0 :            strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) ||
    1321           0 :           (checkcpdtable[i].pattern[0] != '0' &&
    1322           0 :            ((len = checkcpdtable[i].pattern.size()) != 0) &&
    1323           0 :            strncmp(word + pos - len, checkcpdtable[i].pattern.c_str(), len) == 0)))) {
    1324           0 :       return 1;
    1325             :     }
    1326             :   }
    1327           0 :   return 0;
    1328             : }
    1329             : 
    1330             : // forbid compounding with neighbouring upper and lower case characters at word
    1331             : // bounds
    1332           0 : int AffixMgr::cpdcase_check(const char* word, int pos) {
    1333           0 :   if (utf8) {
    1334             :     const char* p;
    1335           0 :     for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--)
    1336             :       ;
    1337           0 :     std::string pair(p);
    1338           0 :     std::vector<w_char> pair_u;
    1339           0 :     u8_u16(pair_u, pair);
    1340           0 :     unsigned short a = pair_u.size() > 1 ? ((pair_u[1].h << 8) + pair_u[1].l) : 0;
    1341           0 :     unsigned short b = !pair_u.empty() ? ((pair_u[0].h << 8) + pair_u[0].l) : 0;
    1342           0 :     if (((unicodetoupper(a, langnum) == a) ||
    1343           0 :          (unicodetoupper(b, langnum) == b)) &&
    1344           0 :         (a != '-') && (b != '-'))
    1345           0 :       return 1;
    1346             :   } else {
    1347           0 :     unsigned char a = *(word + pos - 1);
    1348           0 :     unsigned char b = *(word + pos);
    1349           0 :     if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-'))
    1350           0 :       return 1;
    1351             :   }
    1352           0 :   return 0;
    1353             : }
    1354             : 
    1355             : struct metachar_data {
    1356             :   signed short btpp;  // metacharacter (*, ?) position for backtracking
    1357             :   signed short btwp;  // word position for metacharacters
    1358             :   int btnum;          // number of matched characters in metacharacter
    1359             : };
    1360             : 
    1361             : // check compound patterns
    1362           0 : int AffixMgr::defcpd_check(hentry*** words,
    1363             :                            short wnum,
    1364             :                            hentry* rv,
    1365             :                            hentry** def,
    1366             :                            char all) {
    1367           0 :   int w = 0;
    1368             : 
    1369           0 :   if (!*words) {
    1370           0 :     w = 1;
    1371           0 :     *words = def;
    1372             :   }
    1373             : 
    1374           0 :   if (!*words) {
    1375           0 :     return 0;
    1376             :   }
    1377             : 
    1378           0 :   std::vector<metachar_data> btinfo(1);
    1379             : 
    1380           0 :   short bt = 0;
    1381             : 
    1382           0 :   (*words)[wnum] = rv;
    1383             : 
    1384             :   // has the last word COMPOUNDRULE flag?
    1385           0 :   if (rv->alen == 0) {
    1386           0 :     (*words)[wnum] = NULL;
    1387           0 :     if (w)
    1388           0 :       *words = NULL;
    1389           0 :     return 0;
    1390             :   }
    1391           0 :   int ok = 0;
    1392           0 :   for (size_t i = 0; i < defcpdtable.size(); ++i) {
    1393           0 :     for (size_t j = 0; j < defcpdtable[i].size(); ++j) {
    1394           0 :       if (defcpdtable[i][j] != '*' && defcpdtable[i][j] != '?' &&
    1395           0 :           TESTAFF(rv->astr, defcpdtable[i][j], rv->alen)) {
    1396           0 :         ok = 1;
    1397           0 :         break;
    1398             :       }
    1399             :     }
    1400             :   }
    1401           0 :   if (ok == 0) {
    1402           0 :     (*words)[wnum] = NULL;
    1403           0 :     if (w)
    1404           0 :       *words = NULL;
    1405           0 :     return 0;
    1406             :   }
    1407             : 
    1408           0 :   for (size_t i = 0; i < defcpdtable.size(); ++i) {
    1409           0 :     size_t pp = 0;  // pattern position
    1410           0 :     signed short wp = 0;  // "words" position
    1411             :     int ok2;
    1412           0 :     ok = 1;
    1413           0 :     ok2 = 1;
    1414           0 :     do {
    1415           0 :       while ((pp < defcpdtable[i].size()) && (wp <= wnum)) {
    1416           0 :         if (((pp + 1) < defcpdtable[i].size()) &&
    1417           0 :             ((defcpdtable[i][pp + 1] == '*') ||
    1418           0 :              (defcpdtable[i][pp + 1] == '?'))) {
    1419           0 :           int wend = (defcpdtable[i][pp + 1] == '?') ? wp : wnum;
    1420           0 :           ok2 = 1;
    1421           0 :           pp += 2;
    1422           0 :           btinfo[bt].btpp = pp;
    1423           0 :           btinfo[bt].btwp = wp;
    1424           0 :           while (wp <= wend) {
    1425           0 :             if (!(*words)[wp]->alen ||
    1426           0 :                 !TESTAFF((*words)[wp]->astr, defcpdtable[i][pp - 2],
    1427             :                          (*words)[wp]->alen)) {
    1428           0 :               ok2 = 0;
    1429           0 :               break;
    1430             :             }
    1431           0 :             wp++;
    1432             :           }
    1433           0 :           if (wp <= wnum)
    1434           0 :             ok2 = 0;
    1435           0 :           btinfo[bt].btnum = wp - btinfo[bt].btwp;
    1436           0 :           if (btinfo[bt].btnum > 0) {
    1437           0 :             ++bt;
    1438           0 :             btinfo.resize(bt+1);
    1439             :           }
    1440           0 :           if (ok2)
    1441           0 :             break;
    1442             :         } else {
    1443           0 :           ok2 = 1;
    1444           0 :           if (!(*words)[wp] || !(*words)[wp]->alen ||
    1445           0 :               !TESTAFF((*words)[wp]->astr, defcpdtable[i][pp],
    1446             :                        (*words)[wp]->alen)) {
    1447           0 :             ok = 0;
    1448           0 :             break;
    1449             :           }
    1450           0 :           pp++;
    1451           0 :           wp++;
    1452           0 :           if ((defcpdtable[i].size() == pp) && !(wp > wnum))
    1453           0 :             ok = 0;
    1454             :         }
    1455             :       }
    1456           0 :       if (ok && ok2) {
    1457           0 :         size_t r = pp;
    1458           0 :         while ((defcpdtable[i].size() > r) && ((r + 1) < defcpdtable[i].size()) &&
    1459           0 :                ((defcpdtable[i][r + 1] == '*') ||
    1460           0 :                 (defcpdtable[i][r + 1] == '?')))
    1461           0 :           r += 2;
    1462           0 :         if (defcpdtable[i].size() <= r)
    1463           0 :           return 1;
    1464             :       }
    1465             :       // backtrack
    1466           0 :       if (bt)
    1467           0 :         do {
    1468           0 :           ok = 1;
    1469           0 :           btinfo[bt - 1].btnum--;
    1470           0 :           pp = btinfo[bt - 1].btpp;
    1471           0 :           wp = btinfo[bt - 1].btwp + (signed short)btinfo[bt - 1].btnum;
    1472           0 :         } while ((btinfo[bt - 1].btnum < 0) && --bt);
    1473           0 :     } while (bt);
    1474             : 
    1475           0 :     if (ok && ok2 && (!all || (defcpdtable[i].size() <= pp)))
    1476           0 :       return 1;
    1477             : 
    1478             :     // check zero ending
    1479           0 :     while (ok && ok2 && (defcpdtable[i].size() > pp) &&
    1480           0 :            ((pp + 1) < defcpdtable[i].size()) &&
    1481           0 :            ((defcpdtable[i][pp + 1] == '*') ||
    1482           0 :             (defcpdtable[i][pp + 1] == '?')))
    1483           0 :       pp += 2;
    1484           0 :     if (ok && ok2 && (defcpdtable[i].size() <= pp))
    1485           0 :       return 1;
    1486             :   }
    1487           0 :   (*words)[wnum] = NULL;
    1488           0 :   if (w)
    1489           0 :     *words = NULL;
    1490           0 :   return 0;
    1491             : }
    1492             : 
    1493           0 : inline int AffixMgr::candidate_check(const char* word, int len) {
    1494             : 
    1495           0 :   struct hentry* rv = lookup(word);
    1496           0 :   if (rv)
    1497           0 :     return 1;
    1498             : 
    1499             :   //  rv = prefix_check(word,len,1);
    1500             :   //  if (rv) return 1;
    1501             : 
    1502           0 :   rv = affix_check(word, len);
    1503           0 :   if (rv)
    1504           0 :     return 1;
    1505           0 :   return 0;
    1506             : }
    1507             : 
    1508             : // calculate number of syllable for compound-checking
    1509           0 : short AffixMgr::get_syllable(const std::string& word) {
    1510           0 :   if (cpdmaxsyllable == 0)
    1511           0 :     return 0;
    1512             : 
    1513           0 :   short num = 0;
    1514             : 
    1515           0 :   if (!utf8) {
    1516           0 :     for (size_t i = 0; i < word.size(); ++i) {
    1517           0 :       if (std::binary_search(cpdvowels.begin(), cpdvowels.end(),
    1518           0 :                              word[i])) {
    1519           0 :         ++num;
    1520             :       }
    1521             :     }
    1522           0 :   } else if (!cpdvowels_utf16.empty()) {
    1523           0 :     std::vector<w_char> w;
    1524           0 :     u8_u16(w, word);
    1525           0 :     for (size_t i = 0; i < w.size(); ++i) {
    1526           0 :       if (std::binary_search(cpdvowels_utf16.begin(),
    1527             :                              cpdvowels_utf16.end(),
    1528           0 :                              w[i])) {
    1529           0 :         ++num;
    1530             :       }
    1531             :     }
    1532             :   }
    1533             : 
    1534           0 :   return num;
    1535             : }
    1536             : 
    1537           0 : void AffixMgr::setcminmax(int* cmin, int* cmax, const char* word, int len) {
    1538           0 :   if (utf8) {
    1539             :     int i;
    1540           0 :     for (*cmin = 0, i = 0; (i < cpdmin) && *cmin < len; i++) {
    1541           0 :       for ((*cmin)++; *cmin < len && (word[*cmin] & 0xc0) == 0x80; (*cmin)++)
    1542             :         ;
    1543             :     }
    1544           0 :     for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax >= 0; i++) {
    1545           0 :       for ((*cmax)--; *cmax >= 0 && (word[*cmax] & 0xc0) == 0x80; (*cmax)--)
    1546             :         ;
    1547             :     }
    1548             :   } else {
    1549           0 :     *cmin = cpdmin;
    1550           0 :     *cmax = len - cpdmin + 1;
    1551             :   }
    1552           0 : }
    1553             : 
    1554             : // check if compound word is correctly spelled
    1555             : // hu_mov_rule = spec. Hungarian rule (XXX)
    1556           0 : struct hentry* AffixMgr::compound_check(const std::string& word,
    1557             :                                         short wordnum,
    1558             :                                         short numsyllable,
    1559             :                                         short maxwordnum,
    1560             :                                         short wnum,
    1561             :                                         hentry** words = NULL,
    1562             :                                         hentry** rwords = NULL,
    1563             :                                         char hu_mov_rule = 0,
    1564             :                                         char is_sug = 0,
    1565             :                                         int* info = NULL) {
    1566             :   int i;
    1567             :   short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
    1568           0 :   struct hentry* rv = NULL;
    1569             :   struct hentry* rv_first;
    1570           0 :   std::string st;
    1571           0 :   char ch = '\0';
    1572             :   int cmin;
    1573             :   int cmax;
    1574           0 :   int striple = 0;
    1575           0 :   size_t scpd = 0;
    1576           0 :   int soldi = 0;
    1577           0 :   int oldcmin = 0;
    1578           0 :   int oldcmax = 0;
    1579           0 :   int oldlen = 0;
    1580           0 :   int checkedstriple = 0;
    1581           0 :   char affixed = 0;
    1582           0 :   hentry** oldwords = words;
    1583           0 :   size_t len = word.size();
    1584             : 
    1585             :   int checked_prefix;
    1586             : 
    1587           0 :   setcminmax(&cmin, &cmax, word.c_str(), len);
    1588             : 
    1589           0 :   st.assign(word);
    1590             : 
    1591           0 :   for (i = cmin; i < cmax; i++) {
    1592             :     // go to end of the UTF-8 character
    1593           0 :     if (utf8) {
    1594           0 :       for (; (st[i] & 0xc0) == 0x80; i++)
    1595             :         ;
    1596           0 :       if (i >= cmax)
    1597           0 :         return NULL;
    1598             :     }
    1599             : 
    1600           0 :     words = oldwords;
    1601           0 :     int onlycpdrule = (words) ? 1 : 0;
    1602             : 
    1603           0 :     do {  // onlycpdrule loop
    1604             : 
    1605           0 :       oldnumsyllable = numsyllable;
    1606           0 :       oldwordnum = wordnum;
    1607           0 :       checked_prefix = 0;
    1608             : 
    1609           0 :       do {  // simplified checkcompoundpattern loop
    1610             : 
    1611           0 :         if (scpd > 0) {
    1612           0 :           for (; scpd <= checkcpdtable.size() &&
    1613           0 :                  (checkcpdtable[scpd - 1].pattern3.empty() ||
    1614           0 :                   strncmp(word.c_str() + i, checkcpdtable[scpd - 1].pattern3.c_str(),
    1615           0 :                           checkcpdtable[scpd - 1].pattern3.size()) != 0);
    1616             :                scpd++)
    1617             :             ;
    1618             : 
    1619           0 :           if (scpd > checkcpdtable.size())
    1620           0 :             break;  // break simplified checkcompoundpattern loop
    1621           0 :           st.replace(i, std::string::npos, checkcpdtable[scpd - 1].pattern);
    1622           0 :           soldi = i;
    1623           0 :           i += checkcpdtable[scpd - 1].pattern.size();
    1624           0 :           st.replace(i, std::string::npos, checkcpdtable[scpd - 1].pattern2);
    1625           0 :           st.replace(i + checkcpdtable[scpd - 1].pattern2.size(), std::string::npos,
    1626           0 :                  word.substr(soldi + checkcpdtable[scpd - 1].pattern3.size()));
    1627             : 
    1628           0 :           oldlen = len;
    1629           0 :           len += checkcpdtable[scpd - 1].pattern.size() +
    1630           0 :                  checkcpdtable[scpd - 1].pattern2.size() -
    1631           0 :                  checkcpdtable[scpd - 1].pattern3.size();
    1632           0 :           oldcmin = cmin;
    1633           0 :           oldcmax = cmax;
    1634           0 :           setcminmax(&cmin, &cmax, st.c_str(), len);
    1635             : 
    1636           0 :           cmax = len - cpdmin + 1;
    1637             :         }
    1638             : 
    1639           0 :         ch = st[i];
    1640           0 :         st[i] = '\0';
    1641             : 
    1642           0 :         sfx = NULL;
    1643           0 :         pfx = NULL;
    1644             : 
    1645             :         // FIRST WORD
    1646             : 
    1647           0 :         affixed = 1;
    1648           0 :         rv = lookup(st.c_str());  // perhaps without prefix
    1649             : 
    1650             :         // search homonym with compound flag
    1651           0 :         while ((rv) && !hu_mov_rule &&
    1652           0 :                ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
    1653           0 :                 !((compoundflag && !words && !onlycpdrule &&
    1654           0 :                    TESTAFF(rv->astr, compoundflag, rv->alen)) ||
    1655           0 :                   (compoundbegin && !wordnum && !onlycpdrule &&
    1656           0 :                    TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
    1657           0 :                   (compoundmiddle && wordnum && !words && !onlycpdrule &&
    1658           0 :                    TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
    1659           0 :                   (!defcpdtable.empty() && onlycpdrule &&
    1660           0 :                    ((!words && !wordnum &&
    1661           0 :                      defcpd_check(&words, wnum, rv, rwords, 0)) ||
    1662           0 :                     (words &&
    1663           0 :                      defcpd_check(&words, wnum, rv, rwords, 0))))) ||
    1664           0 :                 (scpd != 0 && checkcpdtable[scpd - 1].cond != FLAG_NULL &&
    1665           0 :                  !TESTAFF(rv->astr, checkcpdtable[scpd - 1].cond, rv->alen)))) {
    1666           0 :           rv = rv->next_homonym;
    1667             :         }
    1668             : 
    1669           0 :         if (rv)
    1670           0 :           affixed = 0;
    1671             : 
    1672           0 :         if (!rv) {
    1673           0 :           if (onlycpdrule)
    1674           0 :             break;
    1675           0 :           if (compoundflag &&
    1676           0 :               !(rv = prefix_check(st.c_str(), i,
    1677             :                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
    1678           0 :                                   compoundflag))) {
    1679           0 :             if (((rv = suffix_check(
    1680           0 :                       st.c_str(), i, 0, NULL, FLAG_NULL, compoundflag,
    1681           0 :                       hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
    1682           0 :                  (compoundmoresuffixes &&
    1683           0 :                   (rv = suffix_check_twosfx(st.c_str(), i, 0, NULL, compoundflag)))) &&
    1684           0 :                 !hu_mov_rule && sfx->getCont() &&
    1685           0 :                 ((compoundforbidflag &&
    1686           0 :                   TESTAFF(sfx->getCont(), compoundforbidflag,
    1687           0 :                           sfx->getContLen())) ||
    1688           0 :                  (compoundend &&
    1689           0 :                   TESTAFF(sfx->getCont(), compoundend, sfx->getContLen())))) {
    1690           0 :               rv = NULL;
    1691             :             }
    1692             :           }
    1693             : 
    1694           0 :           if (rv ||
    1695           0 :               (((wordnum == 0) && compoundbegin &&
    1696           0 :                 ((rv = suffix_check(
    1697           0 :                       st.c_str(), i, 0, NULL, FLAG_NULL, compoundbegin,
    1698           0 :                       hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
    1699           0 :                  (compoundmoresuffixes &&
    1700           0 :                   (rv = suffix_check_twosfx(
    1701             :                        st.c_str(), i, 0, NULL,
    1702           0 :                        compoundbegin))) ||  // twofold suffixes + compound
    1703           0 :                  (rv = prefix_check(st.c_str(), i,
    1704             :                                     hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
    1705           0 :                                     compoundbegin)))) ||
    1706           0 :                ((wordnum > 0) && compoundmiddle &&
    1707           0 :                 ((rv = suffix_check(
    1708           0 :                       st.c_str(), i, 0, NULL, FLAG_NULL, compoundmiddle,
    1709           0 :                       hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
    1710           0 :                  (compoundmoresuffixes &&
    1711           0 :                   (rv = suffix_check_twosfx(
    1712             :                        st.c_str(), i, 0, NULL,
    1713           0 :                        compoundmiddle))) ||  // twofold suffixes + compound
    1714           0 :                  (rv = prefix_check(st.c_str(), i,
    1715             :                                     hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
    1716           0 :                                     compoundmiddle))))))
    1717           0 :             checked_prefix = 1;
    1718             :           // else check forbiddenwords and needaffix
    1719           0 :         } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
    1720           0 :                                 TESTAFF(rv->astr, needaffix, rv->alen) ||
    1721           0 :                                 TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
    1722           0 :                                 (is_sug && nosuggest &&
    1723           0 :                                  TESTAFF(rv->astr, nosuggest, rv->alen)))) {
    1724           0 :           st[i] = ch;
    1725             :           // continue;
    1726           0 :           break;
    1727             :         }
    1728             : 
    1729             :         // check non_compound flag in suffix and prefix
    1730           0 :         if ((rv) && !hu_mov_rule &&
    1731           0 :             ((pfx && pfx->getCont() &&
    1732           0 :               TESTAFF(pfx->getCont(), compoundforbidflag, pfx->getContLen())) ||
    1733           0 :              (sfx && sfx->getCont() &&
    1734           0 :               TESTAFF(sfx->getCont(), compoundforbidflag,
    1735             :                       sfx->getContLen())))) {
    1736           0 :           rv = NULL;
    1737             :         }
    1738             : 
    1739             :         // check compoundend flag in suffix and prefix
    1740           0 :         if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
    1741           0 :             ((pfx && pfx->getCont() &&
    1742           0 :               TESTAFF(pfx->getCont(), compoundend, pfx->getContLen())) ||
    1743           0 :              (sfx && sfx->getCont() &&
    1744           0 :               TESTAFF(sfx->getCont(), compoundend, sfx->getContLen())))) {
    1745           0 :           rv = NULL;
    1746             :         }
    1747             : 
    1748             :         // check compoundmiddle flag in suffix and prefix
    1749           0 :         if ((rv) && !checked_prefix && (wordnum == 0) && compoundmiddle &&
    1750           0 :             !hu_mov_rule &&
    1751           0 :             ((pfx && pfx->getCont() &&
    1752           0 :               TESTAFF(pfx->getCont(), compoundmiddle, pfx->getContLen())) ||
    1753           0 :              (sfx && sfx->getCont() &&
    1754           0 :               TESTAFF(sfx->getCont(), compoundmiddle, sfx->getContLen())))) {
    1755           0 :           rv = NULL;
    1756             :         }
    1757             : 
    1758             :         // check forbiddenwords
    1759           0 :         if ((rv) && (rv->astr) &&
    1760           0 :             (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
    1761           0 :              TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
    1762           0 :              (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) {
    1763           0 :           return NULL;
    1764             :         }
    1765             : 
    1766             :         // increment word number, if the second root has a compoundroot flag
    1767           0 :         if ((rv) && compoundroot &&
    1768           0 :             (TESTAFF(rv->astr, compoundroot, rv->alen))) {
    1769           0 :           wordnum++;
    1770             :         }
    1771             : 
    1772             :         // first word is acceptable in compound words?
    1773           0 :         if (((rv) &&
    1774           0 :              (checked_prefix || (words && words[wnum]) ||
    1775           0 :               (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
    1776           0 :               ((oldwordnum == 0) && compoundbegin &&
    1777           0 :                TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
    1778           0 :               ((oldwordnum > 0) && compoundmiddle &&
    1779           0 :                TESTAFF(rv->astr, compoundmiddle, rv->alen))
    1780             : 
    1781             :               // LANG_hu section: spec. Hungarian rule
    1782           0 :               || ((langnum == LANG_hu) && hu_mov_rule &&
    1783           0 :                   (TESTAFF(
    1784             :                        rv->astr, 'F',
    1785           0 :                        rv->alen) ||  // XXX hardwired Hungarian dictionary codes
    1786           0 :                    TESTAFF(rv->astr, 'G', rv->alen) ||
    1787           0 :                    TESTAFF(rv->astr, 'H', rv->alen)))
    1788             :               // END of LANG_hu section
    1789           0 :               ) &&
    1790             :              (
    1791             :                  // test CHECKCOMPOUNDPATTERN conditions
    1792           0 :                  scpd == 0 || checkcpdtable[scpd - 1].cond == FLAG_NULL ||
    1793           0 :                  TESTAFF(rv->astr, checkcpdtable[scpd - 1].cond, rv->alen)) &&
    1794           0 :              !((checkcompoundtriple && scpd == 0 &&
    1795           0 :                 !words &&  // test triple letters
    1796           0 :                 (word[i - 1] == word[i]) &&
    1797           0 :                 (((i > 1) && (word[i - 1] == word[i - 2])) ||
    1798           0 :                  ((word[i - 1] == word[i + 1]))  // may be word[i+1] == '\0'
    1799             :                  )) ||
    1800           0 :                (checkcompoundcase && scpd == 0 && !words &&
    1801           0 :                 cpdcase_check(word.c_str(), i))))
    1802             :             // LANG_hu section: spec. Hungarian rule
    1803           0 :             || ((!rv) && (langnum == LANG_hu) && hu_mov_rule &&
    1804           0 :                 (rv = affix_check(st.c_str(), i)) &&
    1805           0 :                 (sfx && sfx->getCont() &&
    1806             :                  (  // XXX hardwired Hungarian dic. codes
    1807           0 :                      TESTAFF(sfx->getCont(), (unsigned short)'x',
    1808           0 :                              sfx->getContLen()) ||
    1809           0 :                      TESTAFF(
    1810             :                          sfx->getCont(), (unsigned short)'%',
    1811             :                          sfx->getContLen()))))) {  // first word is ok condition
    1812             : 
    1813             :           // LANG_hu section: spec. Hungarian rule
    1814           0 :           if (langnum == LANG_hu) {
    1815             :             // calculate syllable number of the word
    1816           0 :             numsyllable += get_syllable(st.substr(0, i));
    1817             :             // + 1 word, if syllable number of the prefix > 1 (hungarian
    1818             :             // convention)
    1819           0 :             if (pfx && (get_syllable(pfx->getKey()) > 1))
    1820           0 :               wordnum++;
    1821             :           }
    1822             :           // END of LANG_hu section
    1823             : 
    1824             :           // NEXT WORD(S)
    1825           0 :           rv_first = rv;
    1826           0 :           st[i] = ch;
    1827             : 
    1828           0 :           do {  // striple loop
    1829             : 
    1830             :             // check simplifiedtriple
    1831           0 :             if (simplifiedtriple) {
    1832           0 :               if (striple) {
    1833           0 :                 checkedstriple = 1;
    1834           0 :                 i--;  // check "fahrt" instead of "ahrt" in "Schiffahrt"
    1835           0 :               } else if (i > 2 && word[i - 1] == word[i - 2])
    1836           0 :                 striple = 1;
    1837             :             }
    1838             : 
    1839           0 :             rv = lookup(st.c_str() + i);  // perhaps without prefix
    1840             : 
    1841             :             // search homonym with compound flag
    1842           0 :             while ((rv) &&
    1843           0 :                    ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
    1844           0 :                     !((compoundflag && !words &&
    1845           0 :                        TESTAFF(rv->astr, compoundflag, rv->alen)) ||
    1846           0 :                       (compoundend && !words &&
    1847           0 :                        TESTAFF(rv->astr, compoundend, rv->alen)) ||
    1848           0 :                       (!defcpdtable.empty() && words &&
    1849           0 :                        defcpd_check(&words, wnum + 1, rv, NULL, 1))) ||
    1850           0 :                     (scpd != 0 && checkcpdtable[scpd - 1].cond2 != FLAG_NULL &&
    1851           0 :                      !TESTAFF(rv->astr, checkcpdtable[scpd - 1].cond2,
    1852             :                               rv->alen)))) {
    1853           0 :               rv = rv->next_homonym;
    1854             :             }
    1855             : 
    1856             :             // check FORCEUCASE
    1857           0 :             if (rv && forceucase && (rv) &&
    1858           0 :                 (TESTAFF(rv->astr, forceucase, rv->alen)) &&
    1859           0 :                 !(info && *info & SPELL_ORIGCAP))
    1860           0 :               rv = NULL;
    1861             : 
    1862           0 :             if (rv && words && words[wnum + 1])
    1863           0 :               return rv_first;
    1864             : 
    1865           0 :             oldnumsyllable2 = numsyllable;
    1866           0 :             oldwordnum2 = wordnum;
    1867             : 
    1868             :             // LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary
    1869             :             // code
    1870           0 :             if ((rv) && (langnum == LANG_hu) &&
    1871           0 :                 (TESTAFF(rv->astr, 'I', rv->alen)) &&
    1872           0 :                 !(TESTAFF(rv->astr, 'J', rv->alen))) {
    1873           0 :               numsyllable--;
    1874             :             }
    1875             :             // END of LANG_hu section
    1876             : 
    1877             :             // increment word number, if the second root has a compoundroot flag
    1878           0 :             if ((rv) && (compoundroot) &&
    1879           0 :                 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
    1880           0 :               wordnum++;
    1881             :             }
    1882             : 
    1883             :             // check forbiddenwords
    1884           0 :             if ((rv) && (rv->astr) &&
    1885           0 :                 (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
    1886           0 :                  TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
    1887           0 :                  (is_sug && nosuggest &&
    1888           0 :                   TESTAFF(rv->astr, nosuggest, rv->alen))))
    1889           0 :               return NULL;
    1890             : 
    1891             :             // second word is acceptable, as a root?
    1892             :             // hungarian conventions: compounding is acceptable,
    1893             :             // when compound forms consist of 2 words, or if more,
    1894             :             // then the syllable number of root words must be 6, or lesser.
    1895             : 
    1896           0 :             if ((rv) &&
    1897           0 :                 ((compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
    1898           0 :                  (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))) &&
    1899           0 :                 (((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) ||
    1900           0 :                  ((cpdmaxsyllable != 0) &&
    1901           0 :                   (numsyllable + get_syllable(std::string(HENTRY_WORD(rv), rv->blen)) <=
    1902           0 :                    cpdmaxsyllable))) &&
    1903             :                 (
    1904             :                     // test CHECKCOMPOUNDPATTERN
    1905           0 :                     checkcpdtable.empty() || scpd != 0 ||
    1906           0 :                     !cpdpat_check(word.c_str(), i, rv_first, rv, 0)) &&
    1907           0 :                 ((!checkcompounddup || (rv != rv_first)))
    1908             :                 // test CHECKCOMPOUNDPATTERN conditions
    1909           0 :                 &&
    1910           0 :                 (scpd == 0 || checkcpdtable[scpd - 1].cond2 == FLAG_NULL ||
    1911           0 :                  TESTAFF(rv->astr, checkcpdtable[scpd - 1].cond2, rv->alen))) {
    1912             :               // forbid compound word, if it is a non compound word with typical
    1913             :               // fault
    1914           0 :               if (checkcompoundrep && cpdrep_check(word.c_str(), len))
    1915           0 :                 return NULL;
    1916           0 :               return rv_first;
    1917             :             }
    1918             : 
    1919           0 :             numsyllable = oldnumsyllable2;
    1920           0 :             wordnum = oldwordnum2;
    1921             : 
    1922             :             // perhaps second word has prefix or/and suffix
    1923           0 :             sfx = NULL;
    1924           0 :             sfxflag = FLAG_NULL;
    1925           0 :             rv = (compoundflag && !onlycpdrule)
    1926           0 :                      ? affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundflag,
    1927             :                                    IN_CPD_END)
    1928             :                      : NULL;
    1929           0 :             if (!rv && compoundend && !onlycpdrule) {
    1930           0 :               sfx = NULL;
    1931           0 :               pfx = NULL;
    1932           0 :               rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), compoundend,
    1933           0 :                                IN_CPD_END);
    1934             :             }
    1935             : 
    1936           0 :             if (!rv && !defcpdtable.empty() && words) {
    1937           0 :               rv = affix_check((word.c_str() + i), strlen(word.c_str() + i), 0, IN_CPD_END);
    1938           0 :               if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1))
    1939           0 :                 return rv_first;
    1940           0 :               rv = NULL;
    1941             :             }
    1942             : 
    1943             :             // test CHECKCOMPOUNDPATTERN conditions (allowed forms)
    1944           0 :             if (rv &&
    1945           0 :                 !(scpd == 0 || checkcpdtable[scpd - 1].cond2 == FLAG_NULL ||
    1946           0 :                   TESTAFF(rv->astr, checkcpdtable[scpd - 1].cond2, rv->alen)))
    1947           0 :               rv = NULL;
    1948             : 
    1949             :             // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds)
    1950           0 :             if (rv && !checkcpdtable.empty() && scpd == 0 &&
    1951           0 :                 cpdpat_check(word.c_str(), i, rv_first, rv, affixed))
    1952           0 :               rv = NULL;
    1953             : 
    1954             :             // check non_compound flag in suffix and prefix
    1955           0 :             if ((rv) && ((pfx && pfx->getCont() &&
    1956           0 :                           TESTAFF(pfx->getCont(), compoundforbidflag,
    1957           0 :                                   pfx->getContLen())) ||
    1958           0 :                          (sfx && sfx->getCont() &&
    1959           0 :                           TESTAFF(sfx->getCont(), compoundforbidflag,
    1960             :                                   sfx->getContLen())))) {
    1961           0 :               rv = NULL;
    1962             :             }
    1963             : 
    1964             :             // check FORCEUCASE
    1965           0 :             if (rv && forceucase && (rv) &&
    1966           0 :                 (TESTAFF(rv->astr, forceucase, rv->alen)) &&
    1967           0 :                 !(info && *info & SPELL_ORIGCAP))
    1968           0 :               rv = NULL;
    1969             : 
    1970             :             // check forbiddenwords
    1971           0 :             if ((rv) && (rv->astr) &&
    1972           0 :                 (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
    1973           0 :                  TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
    1974           0 :                  (is_sug && nosuggest &&
    1975           0 :                   TESTAFF(rv->astr, nosuggest, rv->alen))))
    1976           0 :               return NULL;
    1977             : 
    1978             :             // pfxappnd = prefix of word+i, or NULL
    1979             :             // calculate syllable number of prefix.
    1980             :             // hungarian convention: when syllable number of prefix is more,
    1981             :             // than 1, the prefix+word counts as two words.
    1982             : 
    1983           0 :             if (langnum == LANG_hu) {
    1984             :               // calculate syllable number of the word
    1985           0 :               numsyllable += get_syllable(word.c_str() + i);
    1986             : 
    1987             :               // - affix syllable num.
    1988             :               // XXX only second suffix (inflections, not derivations)
    1989           0 :               if (sfxappnd) {
    1990           0 :                 std::string tmp(sfxappnd);
    1991           0 :                 reverseword(tmp);
    1992           0 :                 numsyllable -= get_syllable(tmp) + sfxextra;
    1993             :               }
    1994             : 
    1995             :               // + 1 word, if syllable number of the prefix > 1 (hungarian
    1996             :               // convention)
    1997           0 :               if (pfx && (get_syllable(pfx->getKey()) > 1))
    1998           0 :                 wordnum++;
    1999             : 
    2000             :               // increment syllable num, if last word has a SYLLABLENUM flag
    2001             :               // and the suffix is beginning `s'
    2002             : 
    2003           0 :               if (!cpdsyllablenum.empty()) {
    2004           0 :                 switch (sfxflag) {
    2005             :                   case 'c': {
    2006           0 :                     numsyllable += 2;
    2007           0 :                     break;
    2008             :                   }
    2009             :                   case 'J': {
    2010           0 :                     numsyllable += 1;
    2011           0 :                     break;
    2012             :                   }
    2013             :                   case 'I': {
    2014           0 :                     if (rv && TESTAFF(rv->astr, 'J', rv->alen))
    2015           0 :                       numsyllable += 1;
    2016           0 :                     break;
    2017             :                   }
    2018             :                 }
    2019             :               }
    2020             :             }
    2021             : 
    2022             :             // increment word number, if the second word has a compoundroot flag
    2023           0 :             if ((rv) && (compoundroot) &&
    2024           0 :                 (TESTAFF(rv->astr, compoundroot, rv->alen))) {
    2025           0 :               wordnum++;
    2026             :             }
    2027             : 
    2028             :             // second word is acceptable, as a word with prefix or/and suffix?
    2029             :             // hungarian conventions: compounding is acceptable,
    2030             :             // when compound forms consist 2 word, otherwise
    2031             :             // the syllable number of root words is 6, or lesser.
    2032           0 :             if ((rv) &&
    2033           0 :                 (((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) ||
    2034           0 :                  ((cpdmaxsyllable != 0) && (numsyllable <= cpdmaxsyllable))) &&
    2035           0 :                 ((!checkcompounddup || (rv != rv_first)))) {
    2036             :               // forbid compound word, if it is a non compound word with typical
    2037             :               // fault
    2038           0 :               if (checkcompoundrep && cpdrep_check(word.c_str(), len))
    2039           0 :                 return NULL;
    2040           0 :               return rv_first;
    2041             :             }
    2042             : 
    2043           0 :             numsyllable = oldnumsyllable2;
    2044           0 :             wordnum = oldwordnum2;
    2045             : 
    2046             :             // perhaps second word is a compound word (recursive call)
    2047           0 :             if (wordnum + 2 < maxwordnum) {
    2048           0 :               rv = compound_check(st.substr(i), wordnum + 1,
    2049           0 :                                   numsyllable, maxwordnum, wnum + 1, words, rwords, 0,
    2050           0 :                                   is_sug, info);
    2051             : 
    2052           0 :               if (rv && !checkcpdtable.empty() &&
    2053           0 :                   ((scpd == 0 &&
    2054           0 :                     cpdpat_check(word.c_str(), i, rv_first, rv, affixed)) ||
    2055           0 :                    (scpd != 0 &&
    2056           0 :                     !cpdpat_check(word.c_str(), i, rv_first, rv, affixed))))
    2057           0 :                 rv = NULL;
    2058             :             } else {
    2059           0 :               rv = NULL;
    2060             :             }
    2061           0 :             if (rv) {
    2062             :               // forbid compound word, if it is a non compound word with typical
    2063             :               // fault
    2064           0 :               if (checkcompoundrep || forbiddenword) {
    2065             : 
    2066           0 :                 if (checkcompoundrep && cpdrep_check(word.c_str(), len))
    2067           0 :                   return NULL;
    2068             : 
    2069             :                 // check first part
    2070           0 :                 if (strncmp(rv->word, word.c_str() + i, rv->blen) == 0) {
    2071           0 :                   char r = st[i + rv->blen];
    2072           0 :                   st[i + rv->blen] = '\0';
    2073             : 
    2074           0 :                   if (checkcompoundrep && cpdrep_check(st.c_str(), i + rv->blen)) {
    2075           0 :                     st[ + i + rv->blen] = r;
    2076           0 :                     continue;
    2077             :                   }
    2078             : 
    2079           0 :                   if (forbiddenword) {
    2080           0 :                     struct hentry* rv2 = lookup(word.c_str());
    2081           0 :                     if (!rv2)
    2082           0 :                       rv2 = affix_check(word.c_str(), len);
    2083           0 :                     if (rv2 && rv2->astr &&
    2084           0 :                         TESTAFF(rv2->astr, forbiddenword, rv2->alen) &&
    2085           0 :                         (strncmp(rv2->word, st.c_str(), i + rv->blen) == 0)) {
    2086           0 :                       return NULL;
    2087             :                     }
    2088             :                   }
    2089           0 :                   st[i + rv->blen] = r;
    2090             :                 }
    2091             :               }
    2092           0 :               return rv_first;
    2093             :             }
    2094           0 :           } while (striple && !checkedstriple);  // end of striple loop
    2095             : 
    2096           0 :           if (checkedstriple) {
    2097           0 :             i++;
    2098           0 :             checkedstriple = 0;
    2099           0 :             striple = 0;
    2100             :           }
    2101             : 
    2102             :         }  // first word is ok condition
    2103             : 
    2104           0 :         if (soldi != 0) {
    2105           0 :           i = soldi;
    2106           0 :           soldi = 0;
    2107           0 :           len = oldlen;
    2108           0 :           cmin = oldcmin;
    2109           0 :           cmax = oldcmax;
    2110             :         }
    2111           0 :         scpd++;
    2112             : 
    2113           0 :       } while (!onlycpdrule && simplifiedcpd &&
    2114           0 :                scpd <= checkcpdtable.size());  // end of simplifiedcpd loop
    2115             : 
    2116           0 :       scpd = 0;
    2117           0 :       wordnum = oldwordnum;
    2118           0 :       numsyllable = oldnumsyllable;
    2119             : 
    2120           0 :       if (soldi != 0) {
    2121           0 :         i = soldi;
    2122           0 :         st.assign(word);  // XXX add more optim.
    2123           0 :         soldi = 0;
    2124             :       } else
    2125           0 :         st[i] = ch;
    2126             : 
    2127           0 :     } while (!defcpdtable.empty() && oldwordnum == 0 &&
    2128           0 :              onlycpdrule++ < 1);  // end of onlycpd loop
    2129             :   }
    2130             : 
    2131           0 :   return NULL;
    2132             : }
    2133             : 
    2134             : // check if compound word is correctly spelled
    2135             : // hu_mov_rule = spec. Hungarian rule (XXX)
    2136           0 : int AffixMgr::compound_check_morph(const char* word,
    2137             :                                    int len,
    2138             :                                    short wordnum,
    2139             :                                    short numsyllable,
    2140             :                                    short maxwordnum,
    2141             :                                    short wnum,
    2142             :                                    hentry** words,
    2143             :                                    hentry** rwords,
    2144             :                                    char hu_mov_rule,
    2145             :                                    std::string& result,
    2146             :                                    const std::string* partresult) {
    2147             :   int i;
    2148             :   short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2;
    2149           0 :   int ok = 0;
    2150             : 
    2151           0 :   struct hentry* rv = NULL;
    2152             :   struct hentry* rv_first;
    2153           0 :   std::string st;
    2154             :   char ch;
    2155             : 
    2156             :   int checked_prefix;
    2157           0 :   std::string presult;
    2158             : 
    2159             :   int cmin;
    2160             :   int cmax;
    2161             : 
    2162           0 :   char affixed = 0;
    2163           0 :   hentry** oldwords = words;
    2164             : 
    2165           0 :   setcminmax(&cmin, &cmax, word, len);
    2166             : 
    2167           0 :   st.assign(word);
    2168             : 
    2169           0 :   for (i = cmin; i < cmax; i++) {
    2170             :     // go to end of the UTF-8 character
    2171           0 :     if (utf8) {
    2172           0 :       for (; (st[i] & 0xc0) == 0x80; i++)
    2173             :         ;
    2174           0 :       if (i >= cmax)
    2175           0 :         return 0;
    2176             :     }
    2177             : 
    2178           0 :     words = oldwords;
    2179           0 :     int onlycpdrule = (words) ? 1 : 0;
    2180             : 
    2181           0 :     do {  // onlycpdrule loop
    2182             : 
    2183           0 :       oldnumsyllable = numsyllable;
    2184           0 :       oldwordnum = wordnum;
    2185           0 :       checked_prefix = 0;
    2186             : 
    2187           0 :       ch = st[i];
    2188           0 :       st[i] = '\0';
    2189           0 :       sfx = NULL;
    2190             : 
    2191             :       // FIRST WORD
    2192             : 
    2193           0 :       affixed = 1;
    2194             : 
    2195           0 :       presult.clear();
    2196           0 :       if (partresult)
    2197           0 :         presult.append(*partresult);
    2198             : 
    2199           0 :       rv = lookup(st.c_str());  // perhaps without prefix
    2200             : 
    2201             :       // search homonym with compound flag
    2202           0 :       while ((rv) && !hu_mov_rule &&
    2203           0 :              ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
    2204           0 :               !((compoundflag && !words && !onlycpdrule &&
    2205           0 :                  TESTAFF(rv->astr, compoundflag, rv->alen)) ||
    2206           0 :                 (compoundbegin && !wordnum && !onlycpdrule &&
    2207           0 :                  TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
    2208           0 :                 (compoundmiddle && wordnum && !words && !onlycpdrule &&
    2209           0 :                  TESTAFF(rv->astr, compoundmiddle, rv->alen)) ||
    2210           0 :                 (!defcpdtable.empty() && onlycpdrule &&
    2211           0 :                  ((!words && !wordnum &&
    2212           0 :                    defcpd_check(&words, wnum, rv, rwords, 0)) ||
    2213           0 :                   (words &&
    2214           0 :                    defcpd_check(&words, wnum, rv, rwords, 0))))))) {
    2215           0 :         rv = rv->next_homonym;
    2216             :       }
    2217             : 
    2218           0 :       if (rv)
    2219           0 :         affixed = 0;
    2220             : 
    2221           0 :       if (rv) {
    2222           0 :         presult.push_back(MSEP_FLD);
    2223           0 :         presult.append(MORPH_PART);
    2224           0 :         presult.append(st.c_str());
    2225           0 :         if (!HENTRY_FIND(rv, MORPH_STEM)) {
    2226           0 :           presult.push_back(MSEP_FLD);
    2227           0 :           presult.append(MORPH_STEM);
    2228           0 :           presult.append(st.c_str());
    2229             :         }
    2230           0 :         if (HENTRY_DATA(rv)) {
    2231           0 :           presult.push_back(MSEP_FLD);
    2232           0 :           presult.append(HENTRY_DATA2(rv));
    2233             :         }
    2234             :       }
    2235             : 
    2236           0 :       if (!rv) {
    2237           0 :         if (compoundflag &&
    2238             :             !(rv =
    2239           0 :                   prefix_check(st.c_str(), i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
    2240           0 :                                compoundflag))) {
    2241           0 :           if (((rv = suffix_check(st.c_str(), i, 0, NULL, FLAG_NULL,
    2242           0 :                                   compoundflag,
    2243           0 :                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
    2244           0 :                (compoundmoresuffixes &&
    2245           0 :                 (rv = suffix_check_twosfx(st.c_str(), i, 0, NULL, compoundflag)))) &&
    2246           0 :               !hu_mov_rule && sfx->getCont() &&
    2247           0 :               ((compoundforbidflag &&
    2248           0 :                 TESTAFF(sfx->getCont(), compoundforbidflag,
    2249           0 :                         sfx->getContLen())) ||
    2250           0 :                (compoundend &&
    2251           0 :                 TESTAFF(sfx->getCont(), compoundend, sfx->getContLen())))) {
    2252           0 :             rv = NULL;
    2253             :           }
    2254             :         }
    2255             : 
    2256           0 :         if (rv ||
    2257           0 :             (((wordnum == 0) && compoundbegin &&
    2258           0 :               ((rv = suffix_check(st.c_str(), i, 0, NULL, FLAG_NULL,
    2259           0 :                                   compoundbegin,
    2260           0 :                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
    2261           0 :                (compoundmoresuffixes &&
    2262           0 :                 (rv = suffix_check_twosfx(
    2263             :                      st.c_str(), i, 0, NULL,
    2264           0 :                      compoundbegin))) ||  // twofold suffix+compound
    2265           0 :                (rv = prefix_check(st.c_str(), i,
    2266             :                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
    2267           0 :                                   compoundbegin)))) ||
    2268           0 :              ((wordnum > 0) && compoundmiddle &&
    2269           0 :               ((rv = suffix_check(st.c_str(), i, 0, NULL, FLAG_NULL,
    2270           0 :                                   compoundmiddle,
    2271           0 :                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) ||
    2272           0 :                (compoundmoresuffixes &&
    2273           0 :                 (rv = suffix_check_twosfx(
    2274             :                      st.c_str(), i, 0, NULL,
    2275           0 :                      compoundmiddle))) ||  // twofold suffix+compound
    2276           0 :                (rv = prefix_check(st.c_str(), i,
    2277             :                                   hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN,
    2278           0 :                                   compoundmiddle)))))) {
    2279           0 :           std::string p;
    2280           0 :           if (compoundflag)
    2281           0 :             p = affix_check_morph(st.c_str(), i, compoundflag);
    2282           0 :           if (p.empty()) {
    2283           0 :             if ((wordnum == 0) && compoundbegin) {
    2284           0 :               p = affix_check_morph(st.c_str(), i, compoundbegin);
    2285           0 :             } else if ((wordnum > 0) && compoundmiddle) {
    2286           0 :               p = affix_check_morph(st.c_str(), i, compoundmiddle);
    2287             :             }
    2288             :           }
    2289           0 :           if (!p.empty()) {
    2290           0 :             presult.push_back(MSEP_FLD);
    2291           0 :             presult.append(MORPH_PART);
    2292           0 :             presult.append(st.c_str());
    2293           0 :             line_uniq_app(p, MSEP_REC);
    2294           0 :             presult.append(p);
    2295             :           }
    2296           0 :           checked_prefix = 1;
    2297             :         }
    2298             :         // else check forbiddenwords
    2299           0 :       } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
    2300           0 :                               TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) ||
    2301           0 :                               TESTAFF(rv->astr, needaffix, rv->alen))) {
    2302           0 :         st[i] = ch;
    2303           0 :         continue;
    2304             :       }
    2305             : 
    2306             :       // check non_compound flag in suffix and prefix
    2307           0 :       if ((rv) && !hu_mov_rule &&
    2308           0 :           ((pfx && pfx->getCont() &&
    2309           0 :             TESTAFF(pfx->getCont(), compoundforbidflag, pfx->getContLen())) ||
    2310           0 :            (sfx && sfx->getCont() &&
    2311           0 :             TESTAFF(sfx->getCont(), compoundforbidflag, sfx->getContLen())))) {
    2312           0 :         continue;
    2313             :       }
    2314             : 
    2315             :       // check compoundend flag in suffix and prefix
    2316           0 :       if ((rv) && !checked_prefix && compoundend && !hu_mov_rule &&
    2317           0 :           ((pfx && pfx->getCont() &&
    2318           0 :             TESTAFF(pfx->getCont(), compoundend, pfx->getContLen())) ||
    2319           0 :            (sfx && sfx->getCont() &&
    2320           0 :             TESTAFF(sfx->getCont(), compoundend, sfx->getContLen())))) {
    2321           0 :         continue;
    2322             :       }
    2323             : 
    2324             :       // check compoundmiddle flag in suffix and prefix
    2325           0 :       if ((rv) && !checked_prefix && (wordnum == 0) && compoundmiddle &&
    2326           0 :           !hu_mov_rule &&
    2327           0 :           ((pfx && pfx->getCont() &&
    2328           0 :             TESTAFF(pfx->getCont(), compoundmiddle, pfx->getContLen())) ||
    2329           0 :            (sfx && sfx->getCont() &&
    2330           0 :             TESTAFF(sfx->getCont(), compoundmiddle, sfx->getContLen())))) {
    2331           0 :         rv = NULL;
    2332             :       }
    2333             : 
    2334             :       // check forbiddenwords
    2335           0 :       if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
    2336           0 :                                  TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen)))
    2337           0 :         continue;
    2338             : 
    2339             :       // increment word number, if the second root has a compoundroot flag
    2340           0 :       if ((rv) && (compoundroot) &&
    2341           0 :           (TESTAFF(rv->astr, compoundroot, rv->alen))) {
    2342           0 :         wordnum++;
    2343             :       }
    2344             : 
    2345             :       // first word is acceptable in compound words?
    2346           0 :       if (((rv) &&
    2347           0 :            (checked_prefix || (words && words[wnum]) ||
    2348           0 :             (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
    2349           0 :             ((oldwordnum == 0) && compoundbegin &&
    2350           0 :              TESTAFF(rv->astr, compoundbegin, rv->alen)) ||
    2351           0 :             ((oldwordnum > 0) && compoundmiddle &&
    2352           0 :              TESTAFF(rv->astr, compoundmiddle, rv->alen))
    2353             :             // LANG_hu section: spec. Hungarian rule
    2354           0 :             || ((langnum == LANG_hu) &&  // hu_mov_rule
    2355           0 :                 hu_mov_rule && (TESTAFF(rv->astr, 'F', rv->alen) ||
    2356           0 :                                 TESTAFF(rv->astr, 'G', rv->alen) ||
    2357           0 :                                 TESTAFF(rv->astr, 'H', rv->alen)))
    2358             :             // END of LANG_hu section
    2359           0 :             ) &&
    2360           0 :            !((checkcompoundtriple && !words &&  // test triple letters
    2361           0 :               (word[i - 1] == word[i]) &&
    2362           0 :               (((i > 1) && (word[i - 1] == word[i - 2])) ||
    2363           0 :                ((word[i - 1] == word[i + 1]))  // may be word[i+1] == '\0'
    2364           0 :                )) ||
    2365             :              (
    2366             :                  // test CHECKCOMPOUNDPATTERN
    2367           0 :                  !checkcpdtable.empty() && !words &&
    2368           0 :                  cpdpat_check(word, i, rv, NULL, affixed)) ||
    2369           0 :              (checkcompoundcase && !words && cpdcase_check(word, i))))
    2370             :           // LANG_hu section: spec. Hungarian rule
    2371           0 :           ||
    2372           0 :           ((!rv) && (langnum == LANG_hu) && hu_mov_rule &&
    2373           0 :            (rv = affix_check(st.c_str(), i)) &&
    2374           0 :            (sfx && sfx->getCont() &&
    2375           0 :             (TESTAFF(sfx->getCont(), (unsigned short)'x', sfx->getContLen()) ||
    2376           0 :              TESTAFF(sfx->getCont(), (unsigned short)'%', sfx->getContLen()))))
    2377             :           // END of LANG_hu section
    2378             :           ) {
    2379             :         // LANG_hu section: spec. Hungarian rule
    2380           0 :         if (langnum == LANG_hu) {
    2381             :           // calculate syllable number of the word
    2382           0 :           numsyllable += get_syllable(st.substr(0, i));
    2383             : 
    2384             :           // + 1 word, if syllable number of the prefix > 1 (hungarian
    2385             :           // convention)
    2386           0 :           if (pfx && (get_syllable(pfx->getKey()) > 1))
    2387           0 :             wordnum++;
    2388             :         }
    2389             :         // END of LANG_hu section
    2390             : 
    2391             :         // NEXT WORD(S)
    2392           0 :         rv_first = rv;
    2393           0 :         rv = lookup((word + i));  // perhaps without prefix
    2394             : 
    2395             :         // search homonym with compound flag
    2396           0 :         while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) ||
    2397           0 :                         !((compoundflag && !words &&
    2398           0 :                            TESTAFF(rv->astr, compoundflag, rv->alen)) ||
    2399           0 :                           (compoundend && !words &&
    2400           0 :                            TESTAFF(rv->astr, compoundend, rv->alen)) ||
    2401           0 :                           (!defcpdtable.empty() && words &&
    2402           0 :                            defcpd_check(&words, wnum + 1, rv, NULL, 1))))) {
    2403           0 :           rv = rv->next_homonym;
    2404             :         }
    2405             : 
    2406           0 :         if (rv && words && words[wnum + 1]) {
    2407           0 :           result.append(presult);
    2408           0 :           result.append(" ");
    2409           0 :           result.append(MORPH_PART);
    2410           0 :           result.append(word + i);
    2411           0 :           if (complexprefixes && HENTRY_DATA(rv))
    2412           0 :             result.append(HENTRY_DATA2(rv));
    2413           0 :           if (!HENTRY_FIND(rv, MORPH_STEM)) {
    2414           0 :             result.append(" ");
    2415           0 :             result.append(MORPH_STEM);
    2416           0 :             result.append(HENTRY_WORD(rv));
    2417             :           }
    2418             :           // store the pointer of the hash entry
    2419           0 :           if (!complexprefixes && HENTRY_DATA(rv)) {
    2420           0 :             result.append(" ");
    2421           0 :             result.append(HENTRY_DATA2(rv));
    2422             :           }
    2423           0 :           result.append("\n");
    2424           0 :           return 0;
    2425             :         }
    2426             : 
    2427           0 :         oldnumsyllable2 = numsyllable;
    2428           0 :         oldwordnum2 = wordnum;
    2429             : 
    2430             :         // LANG_hu section: spec. Hungarian rule
    2431           0 :         if ((rv) && (langnum == LANG_hu) &&
    2432           0 :             (TESTAFF(rv->astr, 'I', rv->alen)) &&
    2433           0 :             !(TESTAFF(rv->astr, 'J', rv->alen))) {
    2434           0 :           numsyllable--;
    2435             :         }
    2436             :         // END of LANG_hu section
    2437             :         // increment word number, if the second root has a compoundroot flag
    2438           0 :         if ((rv) && (compoundroot) &&
    2439           0 :             (TESTAFF(rv->astr, compoundroot, rv->alen))) {
    2440           0 :           wordnum++;
    2441             :         }
    2442             : 
    2443             :         // check forbiddenwords
    2444           0 :         if ((rv) && (rv->astr) &&
    2445           0 :             (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
    2446           0 :              TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) {
    2447           0 :           st[i] = ch;
    2448           0 :           continue;
    2449             :         }
    2450             : 
    2451             :         // second word is acceptable, as a root?
    2452             :         // hungarian conventions: compounding is acceptable,
    2453             :         // when compound forms consist of 2 words, or if more,
    2454             :         // then the syllable number of root words must be 6, or lesser.
    2455           0 :         if ((rv) &&
    2456           0 :             ((compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) ||
    2457           0 :              (compoundend && TESTAFF(rv->astr, compoundend, rv->alen))) &&
    2458           0 :             (((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) ||
    2459           0 :              ((cpdmaxsyllable != 0) &&
    2460           0 :               (numsyllable + get_syllable(std::string(HENTRY_WORD(rv), rv->blen)) <=
    2461           0 :                cpdmaxsyllable))) &&
    2462           0 :             ((!checkcompounddup || (rv != rv_first)))) {
    2463             :           // bad compound word
    2464           0 :           result.append(presult);
    2465           0 :           result.append(" ");
    2466           0 :           result.append(MORPH_PART);
    2467           0 :           result.append(word + i);
    2468             : 
    2469           0 :           if (HENTRY_DATA(rv)) {
    2470           0 :             if (complexprefixes)
    2471           0 :               result.append(HENTRY_DATA2(rv));
    2472           0 :             if (!HENTRY_FIND(rv, MORPH_STEM)) {
    2473           0 :               result.append(" ");
    2474           0 :               result.append(MORPH_STEM);
    2475           0 :               result.append(HENTRY_WORD(rv));
    2476             :             }
    2477             :             // store the pointer of the hash entry
    2478           0 :             if (!complexprefixes) {
    2479           0 :               result.append(" ");
    2480           0 :               result.append(HENTRY_DATA2(rv));
    2481             :             }
    2482             :           }
    2483           0 :           result.append("\n");
    2484           0 :           ok = 1;
    2485             :         }
    2486             : 
    2487           0 :         numsyllable = oldnumsyllable2;
    2488           0 :         wordnum = oldwordnum2;
    2489             : 
    2490             :         // perhaps second word has prefix or/and suffix
    2491           0 :         sfx = NULL;
    2492           0 :         sfxflag = FLAG_NULL;
    2493             : 
    2494           0 :         if (compoundflag && !onlycpdrule)
    2495           0 :           rv = affix_check((word + i), strlen(word + i), compoundflag);
    2496             :         else
    2497           0 :           rv = NULL;
    2498             : 
    2499           0 :         if (!rv && compoundend && !onlycpdrule) {
    2500           0 :           sfx = NULL;
    2501           0 :           pfx = NULL;
    2502           0 :           rv = affix_check((word + i), strlen(word + i), compoundend);
    2503             :         }
    2504             : 
    2505           0 :         if (!rv && !defcpdtable.empty() && words) {
    2506           0 :           rv = affix_check((word + i), strlen(word + i), 0, IN_CPD_END);
    2507           0 :           if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) {
    2508           0 :             std::string m;
    2509           0 :             if (compoundflag)
    2510           0 :               m = affix_check_morph((word + i), strlen(word + i), compoundflag);
    2511           0 :             if (m.empty() && compoundend) {
    2512           0 :               m = affix_check_morph((word + i), strlen(word + i), compoundend);
    2513             :             }
    2514           0 :             result.append(presult);
    2515           0 :             if (!m.empty()) {
    2516           0 :               result.push_back(MSEP_FLD);
    2517           0 :               result.append(MORPH_PART);
    2518           0 :               result.append(word + i);
    2519           0 :               line_uniq_app(m, MSEP_REC);
    2520           0 :               result.append(m);
    2521             :             }
    2522           0 :             result.append("\n");
    2523           0 :             ok = 1;
    2524             :           }
    2525             :         }
    2526             : 
    2527             :         // check non_compound flag in suffix and prefix
    2528           0 :         if ((rv) &&
    2529           0 :             ((pfx && pfx->getCont() &&
    2530           0 :               TESTAFF(pfx->getCont(), compoundforbidflag, pfx->getContLen())) ||
    2531           0 :              (sfx && sfx->getCont() &&
    2532           0 :               TESTAFF(sfx->getCont(), compoundforbidflag,
    2533             :                       sfx->getContLen())))) {
    2534           0 :           rv = NULL;
    2535             :         }
    2536             : 
    2537             :         // check forbiddenwords
    2538           0 :         if ((rv) && (rv->astr) &&
    2539           0 :             (TESTAFF(rv->astr, forbiddenword, rv->alen) ||
    2540           0 :              TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen)) &&
    2541           0 :             (!TESTAFF(rv->astr, needaffix, rv->alen))) {
    2542           0 :           st[i] = ch;
    2543           0 :           continue;
    2544             :         }
    2545             : 
    2546           0 :         if (langnum == LANG_hu) {
    2547             :           // calculate syllable number of the word
    2548           0 :           numsyllable += get_syllable(word + i);
    2549             : 
    2550             :           // - affix syllable num.
    2551             :           // XXX only second suffix (inflections, not derivations)
    2552           0 :           if (sfxappnd) {
    2553           0 :             std::string tmp(sfxappnd);
    2554           0 :             reverseword(tmp);
    2555           0 :             numsyllable -= get_syllable(tmp) + sfxextra;
    2556             :           }
    2557             : 
    2558             :           // + 1 word, if syllable number of the prefix > 1 (hungarian
    2559             :           // convention)
    2560           0 :           if (pfx && (get_syllable(pfx->getKey()) > 1))
    2561           0 :             wordnum++;
    2562             : 
    2563             :           // increment syllable num, if last word has a SYLLABLENUM flag
    2564             :           // and the suffix is beginning `s'
    2565             : 
    2566           0 :           if (!cpdsyllablenum.empty()) {
    2567           0 :             switch (sfxflag) {
    2568             :               case 'c': {
    2569           0 :                 numsyllable += 2;
    2570           0 :                 break;
    2571             :               }
    2572             :               case 'J': {
    2573           0 :                 numsyllable += 1;
    2574           0 :                 break;
    2575             :               }
    2576             :               case 'I': {
    2577           0 :                 if (rv && TESTAFF(rv->astr, 'J', rv->alen))
    2578           0 :                   numsyllable += 1;
    2579           0 :                 break;
    2580             :               }
    2581             :             }
    2582             :           }
    2583             :         }
    2584             : 
    2585             :         // increment word number, if the second word has a compoundroot flag
    2586           0 :         if ((rv) && (compoundroot) &&
    2587           0 :             (TESTAFF(rv->astr, compoundroot, rv->alen))) {
    2588           0 :           wordnum++;
    2589             :         }
    2590             :         // second word is acceptable, as a word with prefix or/and suffix?
    2591             :         // hungarian conventions: compounding is acceptable,
    2592             :         // when compound forms consist 2 word, otherwise
    2593             :         // the syllable number of root words is 6, or lesser.
    2594           0 :         if ((rv) &&
    2595           0 :             (((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) ||
    2596           0 :              ((cpdmaxsyllable != 0) && (numsyllable <= cpdmaxsyllable))) &&
    2597           0 :             ((!checkcompounddup || (rv != rv_first)))) {
    2598           0 :           std::string m;
    2599           0 :           if (compoundflag)
    2600           0 :             m = affix_check_morph((word + i), strlen(word + i), compoundflag);
    2601           0 :           if (m.empty() && compoundend) {
    2602           0 :             m = affix_check_morph((word + i), strlen(word + i), compoundend);
    2603             :           }
    2604           0 :           result.append(presult);
    2605           0 :           if (!m.empty()) {
    2606           0 :             result.push_back(MSEP_FLD);
    2607           0 :             result.append(MORPH_PART);
    2608           0 :             result.append(word + 1);
    2609           0 :             line_uniq_app(m, MSEP_REC);
    2610           0 :             result.append(m);
    2611             :           }
    2612           0 :           result.push_back(MSEP_REC);
    2613           0 :           ok = 1;
    2614             :         }
    2615             : 
    2616           0 :         numsyllable = oldnumsyllable2;
    2617           0 :         wordnum = oldwordnum2;
    2618             : 
    2619             :         // perhaps second word is a compound word (recursive call)
    2620           0 :         if ((wordnum + 2 < maxwordnum) && (ok == 0)) {
    2621           0 :           compound_check_morph((word + i), strlen(word + i), wordnum + 1,
    2622           0 :                                numsyllable, maxwordnum, wnum + 1, words, rwords, 0,
    2623           0 :                                result, &presult);
    2624             :         } else {
    2625           0 :           rv = NULL;
    2626             :         }
    2627             :       }
    2628           0 :       st[i] = ch;
    2629           0 :       wordnum = oldwordnum;
    2630           0 :       numsyllable = oldnumsyllable;
    2631             : 
    2632           0 :     } while (!defcpdtable.empty() && oldwordnum == 0 &&
    2633           0 :              onlycpdrule++ < 1);  // end of onlycpd loop
    2634             :   }
    2635           0 :   return 0;
    2636             : }
    2637             : 
    2638             : 
    2639           0 : inline int AffixMgr::isRevSubset(const char* s1,
    2640             :                                  const char* end_of_s2,
    2641             :                                  int len) {
    2642           0 :   while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) {
    2643           0 :     s1++;
    2644           0 :     end_of_s2--;
    2645           0 :     len--;
    2646             :   }
    2647           0 :   return (*s1 == '\0');
    2648             : }
    2649             : 
    2650             : // check word for suffixes
    2651           0 : struct hentry* AffixMgr::suffix_check(const char* word,
    2652             :                                       int len,
    2653             :                                       int sfxopts,
    2654             :                                       PfxEntry* ppfx,
    2655             :                                       const FLAG cclass,
    2656             :                                       const FLAG needflag,
    2657             :                                       char in_compound) {
    2658           0 :   struct hentry* rv = NULL;
    2659           0 :   PfxEntry* ep = ppfx;
    2660             : 
    2661             :   // first handle the special case of 0 length suffixes
    2662           0 :   SfxEntry* se = sStart[0];
    2663             : 
    2664           0 :   while (se) {
    2665           0 :     if (!cclass || se->getCont()) {
    2666             :       // suffixes are not allowed in beginning of compounds
    2667           0 :       if ((((in_compound != IN_CPD_BEGIN)) ||  // && !cclass
    2668             :            // except when signed with compoundpermitflag flag
    2669           0 :            (se->getCont() && compoundpermitflag &&
    2670           0 :             TESTAFF(se->getCont(), compoundpermitflag, se->getContLen()))) &&
    2671           0 :           (!circumfix ||
    2672             :            // no circumfix flag in prefix and suffix
    2673           0 :            ((!ppfx || !(ep->getCont()) ||
    2674           0 :              !TESTAFF(ep->getCont(), circumfix, ep->getContLen())) &&
    2675           0 :             (!se->getCont() ||
    2676           0 :              !(TESTAFF(se->getCont(), circumfix, se->getContLen())))) ||
    2677             :            // circumfix flag in prefix AND suffix
    2678           0 :            ((ppfx && (ep->getCont()) &&
    2679           0 :              TESTAFF(ep->getCont(), circumfix, ep->getContLen())) &&
    2680           0 :             (se->getCont() &&
    2681           0 :              (TESTAFF(se->getCont(), circumfix, se->getContLen()))))) &&
    2682             :           // fogemorpheme
    2683           0 :           (in_compound ||
    2684           0 :            !(se->getCont() &&
    2685           0 :              (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) &&
    2686             :           // needaffix on prefix or first suffix
    2687           0 :           (cclass ||
    2688           0 :            !(se->getCont() &&
    2689           0 :              TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
    2690           0 :            (ppfx &&
    2691           0 :             !((ep->getCont()) &&
    2692           0 :               TESTAFF(ep->getCont(), needaffix, ep->getContLen()))))) {
    2693           0 :         rv = se->checkword(word, len, sfxopts, ppfx,
    2694             :                            (FLAG)cclass, needflag,
    2695           0 :                            (in_compound ? 0 : onlyincompound));
    2696           0 :         if (rv) {
    2697           0 :           sfx = se;  // BUG: sfx not stateless
    2698           0 :           return rv;
    2699             :         }
    2700             :       }
    2701             :     }
    2702           0 :     se = se->getNext();
    2703             :   }
    2704             : 
    2705             :   // now handle the general case
    2706           0 :   if (len == 0)
    2707           0 :     return NULL;  // FULLSTRIP
    2708           0 :   unsigned char sp = *((const unsigned char*)(word + len - 1));
    2709           0 :   SfxEntry* sptr = sStart[sp];
    2710             : 
    2711           0 :   while (sptr) {
    2712           0 :     if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
    2713             :       // suffixes are not allowed in beginning of compounds
    2714           0 :       if ((((in_compound != IN_CPD_BEGIN)) ||  // && !cclass
    2715             :            // except when signed with compoundpermitflag flag
    2716           0 :            (sptr->getCont() && compoundpermitflag &&
    2717           0 :             TESTAFF(sptr->getCont(), compoundpermitflag,
    2718           0 :                     sptr->getContLen()))) &&
    2719           0 :           (!circumfix ||
    2720             :            // no circumfix flag in prefix and suffix
    2721           0 :            ((!ppfx || !(ep->getCont()) ||
    2722           0 :              !TESTAFF(ep->getCont(), circumfix, ep->getContLen())) &&
    2723           0 :             (!sptr->getCont() ||
    2724           0 :              !(TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())))) ||
    2725             :            // circumfix flag in prefix AND suffix
    2726           0 :            ((ppfx && (ep->getCont()) &&
    2727           0 :              TESTAFF(ep->getCont(), circumfix, ep->getContLen())) &&
    2728           0 :             (sptr->getCont() &&
    2729           0 :              (TESTAFF(sptr->getCont(), circumfix, sptr->getContLen()))))) &&
    2730             :           // fogemorpheme
    2731           0 :           (in_compound ||
    2732           0 :            !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound,
    2733           0 :                                           sptr->getContLen()))))) &&
    2734             :           // needaffix on prefix or first suffix
    2735           0 :           (cclass ||
    2736           0 :            !(sptr->getCont() &&
    2737           0 :              TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
    2738           0 :            (ppfx &&
    2739           0 :             !((ep->getCont()) &&
    2740           0 :               TESTAFF(ep->getCont(), needaffix, ep->getContLen())))))
    2741           0 :         if (in_compound != IN_CPD_END || ppfx ||
    2742           0 :             !(sptr->getCont() &&
    2743           0 :               TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) {
    2744           0 :           rv = sptr->checkword(word, len, sfxopts, ppfx,
    2745             :                                cclass, needflag,
    2746           0 :                                (in_compound ? 0 : onlyincompound));
    2747           0 :           if (rv) {
    2748           0 :             sfx = sptr;                 // BUG: sfx not stateless
    2749           0 :             sfxflag = sptr->getFlag();  // BUG: sfxflag not stateless
    2750           0 :             if (!sptr->getCont())
    2751           0 :               sfxappnd = sptr->getKey();  // BUG: sfxappnd not stateless
    2752             :             // LANG_hu section: spec. Hungarian rule
    2753           0 :             else if (langnum == LANG_hu && sptr->getKeyLen() &&
    2754           0 :                      sptr->getKey()[0] == 'i' && sptr->getKey()[1] != 'y' &&
    2755           0 :                      sptr->getKey()[1] != 't') {
    2756           0 :               sfxextra = 1;
    2757             :             }
    2758             :             // END of LANG_hu section
    2759           0 :             return rv;
    2760             :           }
    2761             :         }
    2762           0 :       sptr = sptr->getNextEQ();
    2763             :     } else {
    2764           0 :       sptr = sptr->getNextNE();
    2765             :     }
    2766             :   }
    2767             : 
    2768           0 :   return NULL;
    2769             : }
    2770             : 
    2771             : // check word for two-level suffixes
    2772             : 
    2773           0 : struct hentry* AffixMgr::suffix_check_twosfx(const char* word,
    2774             :                                              int len,
    2775             :                                              int sfxopts,
    2776             :                                              PfxEntry* ppfx,
    2777             :                                              const FLAG needflag) {
    2778           0 :   struct hentry* rv = NULL;
    2779             : 
    2780             :   // first handle the special case of 0 length suffixes
    2781           0 :   SfxEntry* se = sStart[0];
    2782           0 :   while (se) {
    2783           0 :     if (contclasses[se->getFlag()]) {
    2784           0 :       rv = se->check_twosfx(word, len, sfxopts, ppfx, needflag);
    2785           0 :       if (rv)
    2786           0 :         return rv;
    2787             :     }
    2788           0 :     se = se->getNext();
    2789             :   }
    2790             : 
    2791             :   // now handle the general case
    2792           0 :   if (len == 0)
    2793           0 :     return NULL;  // FULLSTRIP
    2794           0 :   unsigned char sp = *((const unsigned char*)(word + len - 1));
    2795           0 :   SfxEntry* sptr = sStart[sp];
    2796             : 
    2797           0 :   while (sptr) {
    2798           0 :     if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
    2799           0 :       if (contclasses[sptr->getFlag()]) {
    2800           0 :         rv = sptr->check_twosfx(word, len, sfxopts, ppfx, needflag);
    2801           0 :         if (rv) {
    2802           0 :           sfxflag = sptr->getFlag();  // BUG: sfxflag not stateless
    2803           0 :           if (!sptr->getCont())
    2804           0 :             sfxappnd = sptr->getKey();  // BUG: sfxappnd not stateless
    2805           0 :           return rv;
    2806             :         }
    2807             :       }
    2808           0 :       sptr = sptr->getNextEQ();
    2809             :     } else {
    2810           0 :       sptr = sptr->getNextNE();
    2811             :     }
    2812             :   }
    2813             : 
    2814           0 :   return NULL;
    2815             : }
    2816             : 
    2817           0 : std::string AffixMgr::suffix_check_twosfx_morph(const char* word,
    2818             :                                                 int len,
    2819             :                                                 int sfxopts,
    2820             :                                                 PfxEntry* ppfx,
    2821             :                                                 const FLAG needflag) {
    2822           0 :   std::string result;
    2823           0 :   std::string result2;
    2824           0 :   std::string result3;
    2825             : 
    2826             :   // first handle the special case of 0 length suffixes
    2827           0 :   SfxEntry* se = sStart[0];
    2828           0 :   while (se) {
    2829           0 :     if (contclasses[se->getFlag()]) {
    2830           0 :       std::string st = se->check_twosfx_morph(word, len, sfxopts, ppfx, needflag);
    2831           0 :       if (!st.empty()) {
    2832           0 :         if (ppfx) {
    2833           0 :           if (ppfx->getMorph()) {
    2834           0 :             result.append(ppfx->getMorph());
    2835           0 :             result.append(" ");
    2836             :           } else
    2837           0 :             debugflag(result, ppfx->getFlag());
    2838             :         }
    2839           0 :         result.append(st);
    2840           0 :         if (se->getMorph()) {
    2841           0 :           result.append(" ");
    2842           0 :           result.append(se->getMorph());
    2843             :         } else
    2844           0 :           debugflag(result, se->getFlag());
    2845           0 :         result.append("\n");
    2846             :       }
    2847             :     }
    2848           0 :     se = se->getNext();
    2849             :   }
    2850             : 
    2851             :   // now handle the general case
    2852           0 :   if (len == 0)
    2853           0 :     return std::string();  // FULLSTRIP
    2854           0 :   unsigned char sp = *((const unsigned char*)(word + len - 1));
    2855           0 :   SfxEntry* sptr = sStart[sp];
    2856             : 
    2857           0 :   while (sptr) {
    2858           0 :     if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
    2859           0 :       if (contclasses[sptr->getFlag()]) {
    2860           0 :         std::string st = sptr->check_twosfx_morph(word, len, sfxopts, ppfx, needflag);
    2861           0 :         if (!st.empty()) {
    2862           0 :           sfxflag = sptr->getFlag();  // BUG: sfxflag not stateless
    2863           0 :           if (!sptr->getCont())
    2864           0 :             sfxappnd = sptr->getKey();  // BUG: sfxappnd not stateless
    2865           0 :           result2.assign(st);
    2866             : 
    2867           0 :           result3.clear();
    2868             : 
    2869           0 :           if (sptr->getMorph()) {
    2870           0 :             result3.append(" ");
    2871           0 :             result3.append(sptr->getMorph());
    2872             :           } else
    2873           0 :             debugflag(result3, sptr->getFlag());
    2874           0 :           strlinecat(result2, result3);
    2875           0 :           result2.append("\n");
    2876           0 :           result.append(result2);
    2877             :         }
    2878             :       }
    2879           0 :       sptr = sptr->getNextEQ();
    2880             :     } else {
    2881           0 :       sptr = sptr->getNextNE();
    2882             :     }
    2883             :   }
    2884             : 
    2885           0 :   return result;
    2886             : }
    2887             : 
    2888           0 : std::string AffixMgr::suffix_check_morph(const char* word,
    2889             :                                          int len,
    2890             :                                          int sfxopts,
    2891             :                                          PfxEntry* ppfx,
    2892             :                                          const FLAG cclass,
    2893             :                                          const FLAG needflag,
    2894             :                                          char in_compound) {
    2895           0 :   std::string result;
    2896             : 
    2897           0 :   struct hentry* rv = NULL;
    2898             : 
    2899           0 :   PfxEntry* ep = ppfx;
    2900             : 
    2901             :   // first handle the special case of 0 length suffixes
    2902           0 :   SfxEntry* se = sStart[0];
    2903           0 :   while (se) {
    2904           0 :     if (!cclass || se->getCont()) {
    2905             :       // suffixes are not allowed in beginning of compounds
    2906           0 :       if (((((in_compound != IN_CPD_BEGIN)) ||  // && !cclass
    2907             :             // except when signed with compoundpermitflag flag
    2908           0 :             (se->getCont() && compoundpermitflag &&
    2909           0 :              TESTAFF(se->getCont(), compoundpermitflag, se->getContLen()))) &&
    2910           0 :            (!circumfix ||
    2911             :             // no circumfix flag in prefix and suffix
    2912           0 :             ((!ppfx || !(ep->getCont()) ||
    2913           0 :               !TESTAFF(ep->getCont(), circumfix, ep->getContLen())) &&
    2914           0 :              (!se->getCont() ||
    2915           0 :               !(TESTAFF(se->getCont(), circumfix, se->getContLen())))) ||
    2916             :             // circumfix flag in prefix AND suffix
    2917           0 :             ((ppfx && (ep->getCont()) &&
    2918           0 :               TESTAFF(ep->getCont(), circumfix, ep->getContLen())) &&
    2919           0 :              (se->getCont() &&
    2920           0 :               (TESTAFF(se->getCont(), circumfix, se->getContLen()))))) &&
    2921             :            // fogemorpheme
    2922           0 :            (in_compound ||
    2923           0 :             !((se->getCont() &&
    2924           0 :                (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) &&
    2925             :            // needaffix on prefix or first suffix
    2926           0 :            (cclass ||
    2927           0 :             !(se->getCont() &&
    2928           0 :               TESTAFF(se->getCont(), needaffix, se->getContLen())) ||
    2929           0 :             (ppfx &&
    2930           0 :              !((ep->getCont()) &&
    2931           0 :                TESTAFF(ep->getCont(), needaffix, ep->getContLen()))))))
    2932           0 :         rv = se->checkword(word, len, sfxopts, ppfx, cclass,
    2933           0 :                            needflag, FLAG_NULL);
    2934           0 :       while (rv) {
    2935           0 :         if (ppfx) {
    2936           0 :           if (ppfx->getMorph()) {
    2937           0 :             result.append(ppfx->getMorph());
    2938           0 :             result.append(" ");
    2939             :           } else
    2940           0 :             debugflag(result, ppfx->getFlag());
    2941             :         }
    2942           0 :         if (complexprefixes && HENTRY_DATA(rv))
    2943           0 :           result.append(HENTRY_DATA2(rv));
    2944           0 :         if (!HENTRY_FIND(rv, MORPH_STEM)) {
    2945           0 :           result.append(" ");
    2946           0 :           result.append(MORPH_STEM);
    2947           0 :           result.append(HENTRY_WORD(rv));
    2948             :         }
    2949             : 
    2950           0 :         if (!complexprefixes && HENTRY_DATA(rv)) {
    2951           0 :           result.append(" ");
    2952           0 :           result.append(HENTRY_DATA2(rv));
    2953             :         }
    2954           0 :         if (se->getMorph()) {
    2955           0 :           result.append(" ");
    2956           0 :           result.append(se->getMorph());
    2957             :         } else
    2958           0 :           debugflag(result, se->getFlag());
    2959           0 :         result.append("\n");
    2960           0 :         rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
    2961             :       }
    2962             :     }
    2963           0 :     se = se->getNext();
    2964             :   }
    2965             : 
    2966             :   // now handle the general case
    2967           0 :   if (len == 0)
    2968           0 :     return std::string();  // FULLSTRIP
    2969           0 :   unsigned char sp = *((const unsigned char*)(word + len - 1));
    2970           0 :   SfxEntry* sptr = sStart[sp];
    2971             : 
    2972           0 :   while (sptr) {
    2973           0 :     if (isRevSubset(sptr->getKey(), word + len - 1, len)) {
    2974             :       // suffixes are not allowed in beginning of compounds
    2975           0 :       if (((((in_compound != IN_CPD_BEGIN)) ||  // && !cclass
    2976             :             // except when signed with compoundpermitflag flag
    2977           0 :             (sptr->getCont() && compoundpermitflag &&
    2978           0 :              TESTAFF(sptr->getCont(), compoundpermitflag,
    2979           0 :                      sptr->getContLen()))) &&
    2980           0 :            (!circumfix ||
    2981             :             // no circumfix flag in prefix and suffix
    2982           0 :             ((!ppfx || !(ep->getCont()) ||
    2983           0 :               !TESTAFF(ep->getCont(), circumfix, ep->getContLen())) &&
    2984           0 :              (!sptr->getCont() ||
    2985           0 :               !(TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())))) ||
    2986             :             // circumfix flag in prefix AND suffix
    2987           0 :             ((ppfx && (ep->getCont()) &&
    2988           0 :               TESTAFF(ep->getCont(), circumfix, ep->getContLen())) &&
    2989           0 :              (sptr->getCont() &&
    2990           0 :               (TESTAFF(sptr->getCont(), circumfix, sptr->getContLen()))))) &&
    2991             :            // fogemorpheme
    2992           0 :            (in_compound ||
    2993           0 :             !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound,
    2994           0 :                                            sptr->getContLen()))))) &&
    2995             :            // needaffix on first suffix
    2996           0 :            (cclass ||
    2997           0 :             !(sptr->getCont() &&
    2998           0 :               TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())))))
    2999           0 :         rv = sptr->checkword(word, len, sfxopts, ppfx, cclass,
    3000           0 :                              needflag, FLAG_NULL);
    3001           0 :       while (rv) {
    3002           0 :         if (ppfx) {
    3003           0 :           if (ppfx->getMorph()) {
    3004           0 :             result.append(ppfx->getMorph());
    3005           0 :             result.append(" ");
    3006             :           } else
    3007           0 :             debugflag(result, ppfx->getFlag());
    3008             :         }
    3009           0 :         if (complexprefixes && HENTRY_DATA(rv))
    3010           0 :           result.append(HENTRY_DATA2(rv));
    3011           0 :         if (!HENTRY_FIND(rv, MORPH_STEM)) {
    3012           0 :           result.append(" ");
    3013           0 :           result.append(MORPH_STEM);
    3014           0 :           result.append(HENTRY_WORD(rv));
    3015             :         }
    3016             : 
    3017           0 :         if (!complexprefixes && HENTRY_DATA(rv)) {
    3018           0 :           result.append(" ");
    3019           0 :           result.append(HENTRY_DATA2(rv));
    3020             :         }
    3021             : 
    3022           0 :         if (sptr->getMorph()) {
    3023           0 :           result.append(" ");
    3024           0 :           result.append(sptr->getMorph());
    3025             :         } else
    3026           0 :           debugflag(result, sptr->getFlag());
    3027           0 :         result.append("\n");
    3028           0 :         rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag);
    3029             :       }
    3030           0 :       sptr = sptr->getNextEQ();
    3031             :     } else {
    3032           0 :       sptr = sptr->getNextNE();
    3033             :     }
    3034             :   }
    3035             : 
    3036           0 :   return result;
    3037             : }
    3038             : 
    3039             : // check if word with affixes is correctly spelled
    3040           0 : struct hentry* AffixMgr::affix_check(const char* word,
    3041             :                                      int len,
    3042             :                                      const FLAG needflag,
    3043             :                                      char in_compound) {
    3044             : 
    3045             :   // check all prefixes (also crossed with suffixes if allowed)
    3046           0 :   struct hentry* rv = prefix_check(word, len, in_compound, needflag);
    3047           0 :   if (rv)
    3048           0 :     return rv;
    3049             : 
    3050             :   // if still not found check all suffixes
    3051           0 :   rv = suffix_check(word, len, 0, NULL, FLAG_NULL, needflag, in_compound);
    3052             : 
    3053           0 :   if (havecontclass) {
    3054           0 :     sfx = NULL;
    3055           0 :     pfx = NULL;
    3056             : 
    3057           0 :     if (rv)
    3058           0 :       return rv;
    3059             :     // if still not found check all two-level suffixes
    3060           0 :     rv = suffix_check_twosfx(word, len, 0, NULL, needflag);
    3061             : 
    3062           0 :     if (rv)
    3063           0 :       return rv;
    3064             :     // if still not found check all two-level suffixes
    3065           0 :     rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag);
    3066             :   }
    3067             : 
    3068           0 :   return rv;
    3069             : }
    3070             : 
    3071             : // check if word with affixes is correctly spelled
    3072           0 : std::string AffixMgr::affix_check_morph(const char* word,
    3073             :                                   int len,
    3074             :                                   const FLAG needflag,
    3075             :                                   char in_compound) {
    3076           0 :   std::string result;
    3077             : 
    3078             :   // check all prefixes (also crossed with suffixes if allowed)
    3079           0 :   std::string st = prefix_check_morph(word, len, in_compound);
    3080           0 :   if (!st.empty()) {
    3081           0 :     result.append(st);
    3082             :   }
    3083             : 
    3084             :   // if still not found check all suffixes
    3085           0 :   st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound);
    3086           0 :   if (!st.empty()) {
    3087           0 :     result.append(st);
    3088             :   }
    3089             : 
    3090           0 :   if (havecontclass) {
    3091           0 :     sfx = NULL;
    3092           0 :     pfx = NULL;
    3093             :     // if still not found check all two-level suffixes
    3094           0 :     st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag);
    3095           0 :     if (!st.empty()) {
    3096           0 :       result.append(st);
    3097             :     }
    3098             : 
    3099             :     // if still not found check all two-level suffixes
    3100           0 :     st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag);
    3101           0 :     if (!st.empty()) {
    3102           0 :       result.append(st);
    3103             :     }
    3104             :   }
    3105             : 
    3106           0 :   return result;
    3107             : }
    3108             : 
    3109             : // morphcmp(): compare MORPH_DERI_SFX, MORPH_INFL_SFX and MORPH_TERM_SFX fields
    3110             : // in the first line of the inputs
    3111             : // return 0, if inputs equal
    3112             : // return 1, if inputs may equal with a secondary suffix
    3113             : // otherwise return -1
    3114           0 : static int morphcmp(const char* s, const char* t) {
    3115           0 :   int se = 0;
    3116           0 :   int te = 0;
    3117             :   const char* sl;
    3118             :   const char* tl;
    3119             :   const char* olds;
    3120             :   const char* oldt;
    3121           0 :   if (!s || !t)
    3122           0 :     return 1;
    3123           0 :   olds = s;
    3124           0 :   sl = strchr(s, '\n');
    3125           0 :   s = strstr(s, MORPH_DERI_SFX);
    3126           0 :   if (!s || (sl && sl < s))
    3127           0 :     s = strstr(olds, MORPH_INFL_SFX);
    3128           0 :   if (!s || (sl && sl < s)) {
    3129           0 :     s = strstr(olds, MORPH_TERM_SFX);
    3130           0 :     olds = NULL;
    3131             :   }
    3132           0 :   oldt = t;
    3133           0 :   tl = strchr(t, '\n');
    3134           0 :   t = strstr(t, MORPH_DERI_SFX);
    3135           0 :   if (!t || (tl && tl < t))
    3136           0 :     t = strstr(oldt, MORPH_INFL_SFX);
    3137           0 :   if (!t || (tl && tl < t)) {
    3138           0 :     t = strstr(oldt, MORPH_TERM_SFX);
    3139           0 :     oldt = NULL;
    3140             :   }
    3141           0 :   while (s && t && (!sl || sl > s) && (!tl || tl > t)) {
    3142           0 :     s += MORPH_TAG_LEN;
    3143           0 :     t += MORPH_TAG_LEN;
    3144           0 :     se = 0;
    3145           0 :     te = 0;
    3146           0 :     while ((*s == *t) && !se && !te) {
    3147           0 :       s++;
    3148           0 :       t++;
    3149           0 :       switch (*s) {
    3150             :         case ' ':
    3151             :         case '\n':
    3152             :         case '\t':
    3153             :         case '\0':
    3154           0 :           se = 1;
    3155             :       }
    3156           0 :       switch (*t) {
    3157             :         case ' ':
    3158             :         case '\n':
    3159             :         case '\t':
    3160             :         case '\0':
    3161           0 :           te = 1;
    3162             :       }
    3163             :     }
    3164           0 :     if (!se || !te) {
    3165             :       // not terminal suffix difference
    3166           0 :       if (olds)
    3167           0 :         return -1;
    3168           0 :       return 1;
    3169             :     }
    3170           0 :     olds = s;
    3171           0 :     s = strstr(s, MORPH_DERI_SFX);
    3172           0 :     if (!s || (sl && sl < s))
    3173           0 :       s = strstr(olds, MORPH_INFL_SFX);
    3174           0 :     if (!s || (sl && sl < s)) {
    3175           0 :       s = strstr(olds, MORPH_TERM_SFX);
    3176           0 :       olds = NULL;
    3177             :     }
    3178           0 :     oldt = t;
    3179           0 :     t = strstr(t, MORPH_DERI_SFX);
    3180           0 :     if (!t || (tl && tl < t))
    3181           0 :       t = strstr(oldt, MORPH_INFL_SFX);
    3182           0 :     if (!t || (tl && tl < t)) {
    3183           0 :       t = strstr(oldt, MORPH_TERM_SFX);
    3184           0 :       oldt = NULL;
    3185             :     }
    3186             :   }
    3187           0 :   if (!s && !t && se && te)
    3188           0 :     return 0;
    3189           0 :   return 1;
    3190             : }
    3191             : 
    3192           0 : std::string AffixMgr::morphgen(const char* ts,
    3193             :                                int wl,
    3194             :                                const unsigned short* ap,
    3195             :                                unsigned short al,
    3196             :                                const char* morph,
    3197             :                                const char* targetmorph,
    3198             :                          int level) {
    3199             :   // handle suffixes
    3200           0 :   if (!morph)
    3201           0 :     return std::string();
    3202             : 
    3203             :   // check substandard flag
    3204           0 :   if (TESTAFF(ap, substandard, al))
    3205           0 :     return std::string();
    3206             : 
    3207           0 :   if (morphcmp(morph, targetmorph) == 0)
    3208           0 :     return ts;
    3209             : 
    3210             :   size_t stemmorphcatpos;
    3211           0 :   std::string mymorph;
    3212             : 
    3213             :   // use input suffix fields, if exist
    3214           0 :   if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) {
    3215           0 :     mymorph.assign(morph);
    3216           0 :     mymorph.append(" ");
    3217           0 :     stemmorphcatpos = mymorph.size();
    3218             :   } else {
    3219           0 :     stemmorphcatpos = std::string::npos;
    3220             :   }
    3221             : 
    3222           0 :   for (int i = 0; i < al; i++) {
    3223           0 :     const unsigned char c = (unsigned char)(ap[i] & 0x00FF);
    3224           0 :     SfxEntry* sptr = sFlag[c];
    3225           0 :     while (sptr) {
    3226           0 :       if (sptr->getFlag() == ap[i] && sptr->getMorph() &&
    3227           0 :           ((sptr->getContLen() == 0) ||
    3228             :            // don't generate forms with substandard affixes
    3229           0 :            !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) {
    3230             :         const char* stemmorph;
    3231           0 :         if (stemmorphcatpos != std::string::npos) {
    3232           0 :           mymorph.replace(stemmorphcatpos, std::string::npos, sptr->getMorph());
    3233           0 :           stemmorph = mymorph.c_str();
    3234             :         } else {
    3235           0 :           stemmorph = sptr->getMorph();
    3236             :         }
    3237             : 
    3238           0 :         int cmp = morphcmp(stemmorph, targetmorph);
    3239             : 
    3240           0 :         if (cmp == 0) {
    3241           0 :           std::string newword = sptr->add(ts, wl);
    3242           0 :           if (!newword.empty()) {
    3243           0 :             hentry* check = pHMgr->lookup(newword.c_str());  // XXX extra dic
    3244           0 :             if (!check || !check->astr ||
    3245           0 :                 !(TESTAFF(check->astr, forbiddenword, check->alen) ||
    3246           0 :                   TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) {
    3247           0 :               return newword;
    3248             :             }
    3249             :           }
    3250             :         }
    3251             : 
    3252             :         // recursive call for secondary suffixes
    3253           0 :         if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) &&
    3254           0 :             !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) {
    3255           0 :           std::string newword = sptr->add(ts, wl);
    3256           0 :           if (!newword.empty()) {
    3257             :             std::string newword2 =
    3258           0 :                 morphgen(newword.c_str(), newword.size(), sptr->getCont(),
    3259           0 :                          sptr->getContLen(), stemmorph, targetmorph, 1);
    3260             : 
    3261           0 :             if (!newword2.empty()) {
    3262           0 :               return newword2;
    3263             :             }
    3264             :           }
    3265             :         }
    3266             :       }
    3267           0 :       sptr = sptr->getFlgNxt();
    3268             :     }
    3269             :   }
    3270           0 :   return std::string();
    3271             : }
    3272             : 
    3273           0 : int AffixMgr::expand_rootword(struct guessword* wlst,
    3274             :                               int maxn,
    3275             :                               const char* ts,
    3276             :                               int wl,
    3277             :                               const unsigned short* ap,
    3278             :                               unsigned short al,
    3279             :                               const char* bad,
    3280             :                               int badl,
    3281             :                               const char* phon) {
    3282           0 :   int nh = 0;
    3283             :   // first add root word to list
    3284           0 :   if ((nh < maxn) &&
    3285           0 :       !(al && ((needaffix && TESTAFF(ap, needaffix, al)) ||
    3286           0 :                (onlyincompound && TESTAFF(ap, onlyincompound, al))))) {
    3287           0 :     wlst[nh].word = mystrdup(ts);
    3288           0 :     if (!wlst[nh].word)
    3289           0 :       return 0;
    3290           0 :     wlst[nh].allow = false;
    3291           0 :     wlst[nh].orig = NULL;
    3292           0 :     nh++;
    3293             :     // add special phonetic version
    3294           0 :     if (phon && (nh < maxn)) {
    3295           0 :       wlst[nh].word = mystrdup(phon);
    3296           0 :       if (!wlst[nh].word)
    3297           0 :         return nh - 1;
    3298           0 :       wlst[nh].allow = false;
    3299           0 :       wlst[nh].orig = mystrdup(ts);
    3300           0 :       if (!wlst[nh].orig)
    3301           0 :         return nh - 1;
    3302           0 :       nh++;
    3303             :     }
    3304             :   }
    3305             : 
    3306             :   // handle suffixes
    3307           0 :   for (int i = 0; i < al; i++) {
    3308           0 :     const unsigned char c = (unsigned char)(ap[i] & 0x00FF);
    3309           0 :     SfxEntry* sptr = sFlag[c];
    3310           0 :     while (sptr) {
    3311           0 :       if ((sptr->getFlag() == ap[i]) &&
    3312           0 :           (!sptr->getKeyLen() ||
    3313           0 :            ((badl > sptr->getKeyLen()) &&
    3314           0 :             (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) &&
    3315             :           // check needaffix flag
    3316           0 :           !(sptr->getCont() &&
    3317           0 :             ((needaffix &&
    3318           0 :               TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) ||
    3319           0 :              (circumfix &&
    3320           0 :               TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) ||
    3321           0 :              (onlyincompound &&
    3322           0 :               TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) {
    3323           0 :         std::string newword = sptr->add(ts, wl);
    3324           0 :         if (!newword.empty()) {
    3325           0 :           if (nh < maxn) {
    3326           0 :             wlst[nh].word = mystrdup(newword.c_str());
    3327           0 :             wlst[nh].allow = sptr->allowCross();
    3328           0 :             wlst[nh].orig = NULL;
    3329           0 :             nh++;
    3330             :             // add special phonetic version
    3331           0 :             if (phon && (nh < maxn)) {
    3332           0 :               std::string prefix(phon);
    3333           0 :               std::string key(sptr->getKey());
    3334           0 :               reverseword(key);
    3335           0 :               prefix.append(key);
    3336           0 :               wlst[nh].word = mystrdup(prefix.c_str());
    3337           0 :               if (!wlst[nh].word)
    3338           0 :                 return nh - 1;
    3339           0 :               wlst[nh].allow = false;
    3340           0 :               wlst[nh].orig = mystrdup(newword.c_str());
    3341           0 :               if (!wlst[nh].orig)
    3342           0 :                 return nh - 1;
    3343           0 :               nh++;
    3344             :             }
    3345             :           }
    3346             :         }
    3347             :       }
    3348           0 :       sptr = sptr->getFlgNxt();
    3349             :     }
    3350             :   }
    3351             : 
    3352           0 :   int n = nh;
    3353             : 
    3354             :   // handle cross products of prefixes and suffixes
    3355           0 :   for (int j = 1; j < n; j++)
    3356           0 :     if (wlst[j].allow) {
    3357           0 :       for (int k = 0; k < al; k++) {
    3358           0 :         const unsigned char c = (unsigned char)(ap[k] & 0x00FF);
    3359           0 :         PfxEntry* cptr = pFlag[c];
    3360           0 :         while (cptr) {
    3361           0 :           if ((cptr->getFlag() == ap[k]) && cptr->allowCross() &&
    3362           0 :               (!cptr->getKeyLen() ||
    3363           0 :                ((badl > cptr->getKeyLen()) &&
    3364           0 :                 (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) {
    3365           0 :             int l1 = strlen(wlst[j].word);
    3366           0 :             std::string newword = cptr->add(wlst[j].word, l1);
    3367           0 :             if (!newword.empty()) {
    3368           0 :               if (nh < maxn) {
    3369           0 :                 wlst[nh].word = mystrdup(newword.c_str());
    3370           0 :                 wlst[nh].allow = cptr->allowCross();
    3371           0 :                 wlst[nh].orig = NULL;
    3372           0 :                 nh++;
    3373             :               }
    3374             :             }
    3375             :           }
    3376           0 :           cptr = cptr->getFlgNxt();
    3377             :         }
    3378             :       }
    3379             :     }
    3380             : 
    3381             :   // now handle pure prefixes
    3382           0 :   for (int m = 0; m < al; m++) {
    3383           0 :     const unsigned char c = (unsigned char)(ap[m] & 0x00FF);
    3384           0 :     PfxEntry* ptr = pFlag[c];
    3385           0 :     while (ptr) {
    3386           0 :       if ((ptr->getFlag() == ap[m]) &&
    3387           0 :           (!ptr->getKeyLen() ||
    3388           0 :            ((badl > ptr->getKeyLen()) &&
    3389           0 :             (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) &&
    3390             :           // check needaffix flag
    3391           0 :           !(ptr->getCont() &&
    3392           0 :             ((needaffix &&
    3393           0 :               TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) ||
    3394           0 :              (circumfix &&
    3395           0 :               TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) ||
    3396           0 :              (onlyincompound &&
    3397           0 :               TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen()))))) {
    3398           0 :         std::string newword = ptr->add(ts, wl);
    3399           0 :         if (!newword.empty()) {
    3400           0 :           if (nh < maxn) {
    3401           0 :             wlst[nh].word = mystrdup(newword.c_str());
    3402           0 :             wlst[nh].allow = ptr->allowCross();
    3403           0 :             wlst[nh].orig = NULL;
    3404           0 :             nh++;
    3405             :           }
    3406             :         }
    3407             :       }
    3408           0 :       ptr = ptr->getFlgNxt();
    3409             :     }
    3410             :   }
    3411             : 
    3412           0 :   return nh;
    3413             : }
    3414             : 
    3415             : // return replacing table
    3416           0 : const std::vector<replentry>& AffixMgr::get_reptable() const {
    3417           0 :   return reptable;
    3418             : }
    3419             : 
    3420             : // return iconv table
    3421           0 : RepList* AffixMgr::get_iconvtable() const {
    3422           0 :   if (!iconvtable)
    3423           0 :     return NULL;
    3424           0 :   return iconvtable;
    3425             : }
    3426             : 
    3427             : // return oconv table
    3428           0 : RepList* AffixMgr::get_oconvtable() const {
    3429           0 :   if (!oconvtable)
    3430           0 :     return NULL;
    3431           0 :   return oconvtable;
    3432             : }
    3433             : 
    3434             : // return replacing table
    3435           0 : struct phonetable* AffixMgr::get_phonetable() const {
    3436           0 :   if (!phone)
    3437           0 :     return NULL;
    3438           0 :   return phone;
    3439             : }
    3440             : 
    3441             : // return character map table
    3442           0 : const std::vector<mapentry>& AffixMgr::get_maptable() const {
    3443           0 :   return maptable;
    3444             : }
    3445             : 
    3446             : // return character map table
    3447           0 : const std::vector<std::string>& AffixMgr::get_breaktable() const {
    3448           0 :   return breaktable;
    3449             : }
    3450             : 
    3451             : // return text encoding of dictionary
    3452           0 : const std::string& AffixMgr::get_encoding() {
    3453           0 :   if (encoding.empty())
    3454           0 :     encoding = SPELL_ENCODING;
    3455           0 :   return encoding;
    3456             : }
    3457             : 
    3458             : // return text encoding of dictionary
    3459           0 : int AffixMgr::get_langnum() const {
    3460           0 :   return langnum;
    3461             : }
    3462             : 
    3463             : // return double prefix option
    3464           0 : int AffixMgr::get_complexprefixes() const {
    3465           0 :   return complexprefixes;
    3466             : }
    3467             : 
    3468             : // return FULLSTRIP option
    3469           0 : int AffixMgr::get_fullstrip() const {
    3470           0 :   return fullstrip;
    3471             : }
    3472             : 
    3473           0 : FLAG AffixMgr::get_keepcase() const {
    3474           0 :   return keepcase;
    3475             : }
    3476             : 
    3477           0 : FLAG AffixMgr::get_forceucase() const {
    3478           0 :   return forceucase;
    3479             : }
    3480             : 
    3481           0 : FLAG AffixMgr::get_warn() const {
    3482           0 :   return warn;
    3483             : }
    3484             : 
    3485           0 : int AffixMgr::get_forbidwarn() const {
    3486           0 :   return forbidwarn;
    3487             : }
    3488             : 
    3489           0 : int AffixMgr::get_checksharps() const {
    3490           0 :   return checksharps;
    3491             : }
    3492             : 
    3493           0 : char* AffixMgr::encode_flag(unsigned short aflag) const {
    3494           0 :   return pHMgr->encode_flag(aflag);
    3495             : }
    3496             : 
    3497             : // return the preferred ignore string for suggestions
    3498           0 : const char* AffixMgr::get_ignore() const {
    3499           0 :   if (ignorechars.empty())
    3500           0 :     return NULL;
    3501           0 :   return ignorechars.c_str();
    3502             : }
    3503             : 
    3504             : // return the preferred ignore string for suggestions
    3505           0 : const std::vector<w_char>& AffixMgr::get_ignore_utf16() const {
    3506           0 :   return ignorechars_utf16;
    3507             : }
    3508             : 
    3509             : // return the keyboard string for suggestions
    3510           0 : char* AffixMgr::get_key_string() {
    3511           0 :   if (keystring.empty())
    3512           0 :     keystring = SPELL_KEYSTRING;
    3513           0 :   return mystrdup(keystring.c_str());
    3514             : }
    3515             : 
    3516             : // return the preferred try string for suggestions
    3517           0 : char* AffixMgr::get_try_string() const {
    3518           0 :   if (trystring.empty())
    3519           0 :     return NULL;
    3520           0 :   return mystrdup(trystring.c_str());
    3521             : }
    3522             : 
    3523             : // return the preferred try string for suggestions
    3524           0 : const std::string& AffixMgr::get_wordchars() const {
    3525           0 :   return wordchars;
    3526             : }
    3527             : 
    3528           0 : const std::vector<w_char>& AffixMgr::get_wordchars_utf16() const {
    3529           0 :   return wordchars_utf16;
    3530             : }
    3531             : 
    3532             : // is there compounding?
    3533           0 : int AffixMgr::get_compound() const {
    3534           0 :   return compoundflag || compoundbegin || !defcpdtable.empty();
    3535             : }
    3536             : 
    3537             : // return the compound words control flag
    3538           0 : FLAG AffixMgr::get_compoundflag() const {
    3539           0 :   return compoundflag;
    3540             : }
    3541             : 
    3542             : // return the forbidden words control flag
    3543           0 : FLAG AffixMgr::get_forbiddenword() const {
    3544           0 :   return forbiddenword;
    3545             : }
    3546             : 
    3547             : // return the forbidden words control flag
    3548           0 : FLAG AffixMgr::get_nosuggest() const {
    3549           0 :   return nosuggest;
    3550             : }
    3551             : 
    3552             : // return the forbidden words control flag
    3553           0 : FLAG AffixMgr::get_nongramsuggest() const {
    3554           0 :   return nongramsuggest;
    3555             : }
    3556             : 
    3557             : // return the forbidden words flag modify flag
    3558           0 : FLAG AffixMgr::get_needaffix() const {
    3559           0 :   return needaffix;
    3560             : }
    3561             : 
    3562             : // return the onlyincompound flag
    3563           0 : FLAG AffixMgr::get_onlyincompound() const {
    3564           0 :   return onlyincompound;
    3565             : }
    3566             : 
    3567             : // return the value of suffix
    3568           0 : const std::string& AffixMgr::get_version() const {
    3569           0 :   return version;
    3570             : }
    3571             : 
    3572             : // utility method to look up root words in hash table
    3573           0 : struct hentry* AffixMgr::lookup(const char* word) {
    3574           0 :   struct hentry* he = NULL;
    3575           0 :   for (size_t i = 0; i < alldic.size() && !he; ++i) {
    3576           0 :     he = alldic[i]->lookup(word);
    3577             :   }
    3578           0 :   return he;
    3579             : }
    3580             : 
    3581             : // return the value of suffix
    3582           0 : int AffixMgr::have_contclass() const {
    3583           0 :   return havecontclass;
    3584             : }
    3585             : 
    3586             : // return utf8
    3587           0 : int AffixMgr::get_utf8() const {
    3588           0 :   return utf8;
    3589             : }
    3590             : 
    3591           0 : int AffixMgr::get_maxngramsugs(void) const {
    3592           0 :   return maxngramsugs;
    3593             : }
    3594             : 
    3595           0 : int AffixMgr::get_maxcpdsugs(void) const {
    3596           0 :   return maxcpdsugs;
    3597             : }
    3598             : 
    3599           0 : int AffixMgr::get_maxdiff(void) const {
    3600           0 :   return maxdiff;
    3601             : }
    3602             : 
    3603           0 : int AffixMgr::get_onlymaxdiff(void) const {
    3604           0 :   return onlymaxdiff;
    3605             : }
    3606             : 
    3607             : // return nosplitsugs
    3608           0 : int AffixMgr::get_nosplitsugs(void) const {
    3609           0 :   return nosplitsugs;
    3610             : }
    3611             : 
    3612             : // return sugswithdots
    3613           0 : int AffixMgr::get_sugswithdots(void) const {
    3614           0 :   return sugswithdots;
    3615             : }
    3616             : 
    3617             : /* parse flag */
    3618           0 : bool AffixMgr::parse_flag(const std::string& line, unsigned short* out, FileMgr* af) {
    3619           0 :   if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) {
    3620           0 :     HUNSPELL_WARNING(
    3621             :         stderr,
    3622             :         "error: line %d: multiple definitions of an affix file parameter\n",
    3623           0 :         af->getlinenum());
    3624           0 :     return false;
    3625             :   }
    3626           0 :   std::string s;
    3627           0 :   if (!parse_string(line, s, af->getlinenum()))
    3628           0 :     return false;
    3629           0 :   *out = pHMgr->decode_flag(s.c_str());
    3630           0 :   return true;
    3631             : }
    3632             : 
    3633             : /* parse num */
    3634           0 : bool AffixMgr::parse_num(const std::string& line, int* out, FileMgr* af) {
    3635           0 :   if (*out != -1) {
    3636           0 :     HUNSPELL_WARNING(
    3637             :         stderr,
    3638             :         "error: line %d: multiple definitions of an affix file parameter\n",
    3639           0 :         af->getlinenum());
    3640           0 :     return false;
    3641             :   }
    3642           0 :   std::string s;
    3643           0 :   if (!parse_string(line, s, af->getlinenum()))
    3644           0 :     return false;
    3645           0 :   *out = atoi(s.c_str());
    3646           0 :   return true;
    3647             : }
    3648             : 
    3649             : /* parse in the max syllablecount of compound words and  */
    3650           0 : bool AffixMgr::parse_cpdsyllable(const std::string& line, FileMgr* af) {
    3651           0 :   int i = 0;
    3652           0 :   int np = 0;
    3653           0 :   std::string::const_iterator iter = line.begin();
    3654           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    3655           0 :   while (start_piece != line.end()) {
    3656           0 :     switch (i) {
    3657             :       case 0: {
    3658           0 :         np++;
    3659           0 :         break;
    3660             :       }
    3661             :       case 1: {
    3662           0 :         cpdmaxsyllable = atoi(std::string(start_piece, iter).c_str());
    3663           0 :         np++;
    3664           0 :         break;
    3665             :       }
    3666             :       case 2: {
    3667           0 :         if (!utf8) {
    3668           0 :           cpdvowels.assign(start_piece, iter);
    3669           0 :           std::sort(cpdvowels.begin(), cpdvowels.end());
    3670             :         } else {
    3671           0 :           std::string piece(start_piece, iter);
    3672           0 :           u8_u16(cpdvowels_utf16, piece);
    3673           0 :           std::sort(cpdvowels_utf16.begin(), cpdvowels_utf16.end());
    3674             :         }
    3675           0 :         np++;
    3676           0 :         break;
    3677             :       }
    3678             :       default:
    3679           0 :         break;
    3680             :     }
    3681           0 :     ++i;
    3682           0 :     start_piece = mystrsep(line, iter);
    3683             :   }
    3684           0 :   if (np < 2) {
    3685           0 :     HUNSPELL_WARNING(stderr,
    3686             :                      "error: line %d: missing compoundsyllable information\n",
    3687           0 :                      af->getlinenum());
    3688           0 :     return false;
    3689             :   }
    3690           0 :   if (np == 2)
    3691           0 :     cpdvowels = "AEIOUaeiou";
    3692           0 :   return true;
    3693             : }
    3694             : 
    3695             : /* parse in the typical fault correcting table */
    3696           0 : bool AffixMgr::parse_reptable(const std::string& line, FileMgr* af) {
    3697           0 :   if (parsedrep) {
    3698           0 :     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
    3699           0 :                      af->getlinenum());
    3700           0 :     return false;
    3701             :   }
    3702           0 :   parsedrep = true;
    3703           0 :   int numrep = -1;
    3704           0 :   int i = 0;
    3705           0 :   int np = 0;
    3706           0 :   std::string::const_iterator iter = line.begin();
    3707           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    3708           0 :   while (start_piece != line.end()) {
    3709           0 :     switch (i) {
    3710             :       case 0: {
    3711           0 :         np++;
    3712           0 :         break;
    3713             :       }
    3714             :       case 1: {
    3715           0 :         numrep = atoi(std::string(start_piece, iter).c_str());
    3716           0 :         if (numrep < 1) {
    3717           0 :           HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n",
    3718           0 :                            af->getlinenum());
    3719           0 :           return false;
    3720             :         }
    3721           0 :         reptable.reserve(numrep);
    3722           0 :         np++;
    3723           0 :         break;
    3724             :       }
    3725             :       default:
    3726           0 :         break;
    3727             :     }
    3728           0 :     ++i;
    3729           0 :     start_piece = mystrsep(line, iter);
    3730             :   }
    3731           0 :   if (np != 2) {
    3732           0 :     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
    3733           0 :                      af->getlinenum());
    3734           0 :     return false;
    3735             :   }
    3736             : 
    3737             :   /* now parse the numrep lines to read in the remainder of the table */
    3738           0 :   for (int j = 0; j < numrep; ++j) {
    3739           0 :     std::string nl;
    3740           0 :     if (!af->getline(nl))
    3741           0 :       return false;
    3742           0 :     mychomp(nl);
    3743           0 :     reptable.push_back(replentry());
    3744           0 :     iter = nl.begin();
    3745           0 :     i = 0;
    3746           0 :     int type = 0;
    3747           0 :     start_piece = mystrsep(nl, iter);
    3748           0 :     while (start_piece != nl.end()) {
    3749           0 :       switch (i) {
    3750             :         case 0: {
    3751           0 :           if (nl.compare(start_piece - nl.begin(), 3, "REP", 3) != 0) {
    3752           0 :             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    3753           0 :                              af->getlinenum());
    3754           0 :             reptable.clear();
    3755           0 :             return false;
    3756             :           }
    3757           0 :           break;
    3758             :         }
    3759             :         case 1: {
    3760           0 :           if (*start_piece == '^')
    3761           0 :             type = 1;
    3762           0 :           reptable.back().pattern.assign(start_piece + type, iter);
    3763           0 :           mystrrep(reptable.back().pattern, "_", " ");
    3764           0 :           if (!reptable.back().pattern.empty() && reptable.back().pattern[reptable.back().pattern.size() - 1] == '$') {
    3765           0 :             type += 2;
    3766           0 :             reptable.back().pattern.resize(reptable.back().pattern.size() - 1);
    3767             :           }
    3768           0 :           break;
    3769             :         }
    3770             :         case 2: {
    3771           0 :           reptable.back().outstrings[type].assign(start_piece, iter);
    3772           0 :           mystrrep(reptable.back().outstrings[type], "_", " ");
    3773           0 :           break;
    3774             :         }
    3775             :         default:
    3776           0 :           break;
    3777             :       }
    3778           0 :       ++i;
    3779           0 :       start_piece = mystrsep(nl, iter);
    3780             :     }
    3781           0 :     if (reptable.back().pattern.empty() || reptable.back().outstrings[type].empty()) {
    3782           0 :       HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    3783           0 :                        af->getlinenum());
    3784           0 :       reptable.clear();
    3785           0 :       return false;
    3786             :     }
    3787             :   }
    3788           0 :   return true;
    3789             : }
    3790             : 
    3791             : /* parse in the typical fault correcting table */
    3792           0 : bool AffixMgr::parse_convtable(const std::string& line,
    3793             :                               FileMgr* af,
    3794             :                               RepList** rl,
    3795             :                               const std::string& keyword) {
    3796           0 :   if (*rl) {
    3797           0 :     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
    3798           0 :                      af->getlinenum());
    3799           0 :     return false;
    3800             :   }
    3801           0 :   int i = 0;
    3802           0 :   int np = 0;
    3803           0 :   int numrl = 0;
    3804           0 :   std::string::const_iterator iter = line.begin();
    3805           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    3806           0 :   while (start_piece != line.end()) {
    3807           0 :     switch (i) {
    3808             :       case 0: {
    3809           0 :         np++;
    3810           0 :         break;
    3811             :       }
    3812             :       case 1: {
    3813           0 :         numrl = atoi(std::string(start_piece, iter).c_str());
    3814           0 :         if (numrl < 1) {
    3815           0 :           HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n",
    3816           0 :                            af->getlinenum());
    3817           0 :           return false;
    3818             :         }
    3819           0 :         *rl = new RepList(numrl);
    3820           0 :         if (!*rl)
    3821           0 :           return false;
    3822           0 :         np++;
    3823           0 :         break;
    3824             :       }
    3825             :       default:
    3826           0 :         break;
    3827             :     }
    3828           0 :     ++i;
    3829           0 :     start_piece = mystrsep(line, iter);
    3830             :   }
    3831           0 :   if (np != 2) {
    3832           0 :     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
    3833           0 :                      af->getlinenum());
    3834           0 :     return false;
    3835             :   }
    3836             : 
    3837             :   /* now parse the num lines to read in the remainder of the table */
    3838           0 :   for (int j = 0; j < numrl; j++) {
    3839           0 :     std::string nl;
    3840           0 :     if (!af->getline(nl))
    3841           0 :       return false;
    3842           0 :     mychomp(nl);
    3843           0 :     i = 0;
    3844           0 :     std::string pattern;
    3845           0 :     std::string pattern2;
    3846           0 :     iter = nl.begin();
    3847           0 :     start_piece = mystrsep(nl, iter);
    3848           0 :     while (start_piece != nl.end()) {
    3849             :       {
    3850           0 :         switch (i) {
    3851             :           case 0: {
    3852           0 :             if (nl.compare(start_piece - nl.begin(), keyword.size(), keyword, 0, keyword.size()) != 0) {
    3853           0 :               HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    3854           0 :                                af->getlinenum());
    3855           0 :               delete *rl;
    3856           0 :               *rl = NULL;
    3857           0 :               return false;
    3858             :             }
    3859           0 :             break;
    3860             :           }
    3861             :           case 1: {
    3862           0 :             pattern.assign(start_piece, iter);
    3863           0 :             break;
    3864             :           }
    3865             :           case 2: {
    3866           0 :             pattern2.assign(start_piece, iter);
    3867           0 :             break;
    3868             :           }
    3869             :           default:
    3870           0 :             break;
    3871             :         }
    3872           0 :         ++i;
    3873             :       }
    3874           0 :       start_piece = mystrsep(nl, iter);
    3875             :     }
    3876           0 :     if (pattern.empty() || pattern2.empty()) {
    3877           0 :       HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    3878           0 :                        af->getlinenum());
    3879           0 :       return false;
    3880             :     }
    3881           0 :     (*rl)->add(pattern, pattern2);
    3882             :   }
    3883           0 :   return true;
    3884             : }
    3885             : 
    3886             : /* parse in the typical fault correcting table */
    3887           0 : bool AffixMgr::parse_phonetable(const std::string& line, FileMgr* af) {
    3888           0 :   if (phone) {
    3889           0 :     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
    3890           0 :                      af->getlinenum());
    3891           0 :     return false;
    3892             :   }
    3893           0 :   int num = -1;
    3894           0 :   int i = 0;
    3895           0 :   int np = 0;
    3896           0 :   std::string::const_iterator iter = line.begin();
    3897           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    3898           0 :   while (start_piece != line.end()) {
    3899           0 :     switch (i) {
    3900             :       case 0: {
    3901           0 :         np++;
    3902           0 :         break;
    3903             :       }
    3904             :       case 1: {
    3905           0 :         num = atoi(std::string(start_piece, iter).c_str());
    3906           0 :         if (num < 1) {
    3907           0 :           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
    3908           0 :                            af->getlinenum());
    3909           0 :           return false;
    3910             :         }
    3911           0 :         phone = new phonetable;
    3912           0 :         phone->utf8 = (char)utf8;
    3913           0 :         np++;
    3914           0 :         break;
    3915             :       }
    3916             :       default:
    3917           0 :         break;
    3918             :     }
    3919           0 :     ++i;
    3920           0 :     start_piece = mystrsep(line, iter);
    3921             :   }
    3922           0 :   if (np != 2) {
    3923           0 :     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
    3924           0 :                      af->getlinenum());
    3925           0 :     return false;
    3926             :   }
    3927             : 
    3928             :   /* now parse the phone->num lines to read in the remainder of the table */
    3929           0 :   for (int j = 0; j < num; ++j) {
    3930           0 :     std::string nl;
    3931           0 :     if (!af->getline(nl))
    3932           0 :       return false;
    3933           0 :     mychomp(nl);
    3934           0 :     i = 0;
    3935           0 :     const size_t old_size = phone->rules.size();
    3936           0 :     iter = nl.begin();
    3937           0 :     start_piece = mystrsep(nl, iter);
    3938           0 :     while (start_piece != nl.end()) {
    3939             :       {
    3940           0 :         switch (i) {
    3941             :           case 0: {
    3942           0 :             if (nl.compare(start_piece - nl.begin(), 5, "PHONE", 5) != 0) {
    3943           0 :               HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    3944           0 :                                af->getlinenum());
    3945           0 :               return false;
    3946             :             }
    3947           0 :             break;
    3948             :           }
    3949             :           case 1: {
    3950           0 :             phone->rules.push_back(std::string(start_piece, iter));
    3951           0 :             break;
    3952             :           }
    3953             :           case 2: {
    3954           0 :             phone->rules.push_back(std::string(start_piece, iter));
    3955           0 :             mystrrep(phone->rules.back(), "_", "");
    3956           0 :             break;
    3957             :           }
    3958             :           default:
    3959           0 :             break;
    3960             :         }
    3961           0 :         ++i;
    3962             :       }
    3963           0 :       start_piece = mystrsep(nl, iter);
    3964             :     }
    3965           0 :     if (phone->rules.size() != old_size + 2) {
    3966           0 :       HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    3967           0 :                        af->getlinenum());
    3968           0 :       phone->rules.clear();
    3969           0 :       return false;
    3970             :     }
    3971             :   }
    3972           0 :   phone->rules.push_back("");
    3973           0 :   phone->rules.push_back("");
    3974           0 :   init_phonet_hash(*phone);
    3975           0 :   return true;
    3976             : }
    3977             : 
    3978             : /* parse in the checkcompoundpattern table */
    3979           0 : bool AffixMgr::parse_checkcpdtable(const std::string& line, FileMgr* af) {
    3980           0 :   if (parsedcheckcpd) {
    3981           0 :     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
    3982           0 :                      af->getlinenum());
    3983           0 :     return false;
    3984             :   }
    3985           0 :   parsedcheckcpd = true;
    3986           0 :   int numcheckcpd = -1;
    3987           0 :   int i = 0;
    3988           0 :   int np = 0;
    3989           0 :   std::string::const_iterator iter = line.begin();
    3990           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    3991           0 :   while (start_piece != line.end()) {
    3992           0 :     switch (i) {
    3993             :       case 0: {
    3994           0 :         np++;
    3995           0 :         break;
    3996             :       }
    3997             :       case 1: {
    3998           0 :         numcheckcpd = atoi(std::string(start_piece, iter).c_str());
    3999           0 :         if (numcheckcpd < 1) {
    4000           0 :           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
    4001           0 :                            af->getlinenum());
    4002           0 :           return false;
    4003             :         }
    4004           0 :         checkcpdtable.reserve(numcheckcpd);
    4005           0 :         np++;
    4006           0 :         break;
    4007             :       }
    4008             :       default:
    4009           0 :         break;
    4010             :     }
    4011           0 :     ++i;
    4012           0 :     start_piece = mystrsep(line, iter);
    4013             :   }
    4014           0 :   if (np != 2) {
    4015           0 :     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
    4016           0 :                      af->getlinenum());
    4017           0 :     return false;
    4018             :   }
    4019             : 
    4020             :   /* now parse the numcheckcpd lines to read in the remainder of the table */
    4021           0 :   for (int j = 0; j < numcheckcpd; ++j) {
    4022           0 :     std::string nl;
    4023           0 :     if (!af->getline(nl))
    4024           0 :       return false;
    4025           0 :     mychomp(nl);
    4026           0 :     i = 0;
    4027           0 :     checkcpdtable.push_back(patentry());
    4028           0 :     iter = nl.begin();
    4029           0 :     start_piece = mystrsep(nl, iter);
    4030           0 :     while (start_piece != nl.end()) {
    4031           0 :       switch (i) {
    4032             :         case 0: {
    4033           0 :           if (nl.compare(start_piece - nl.begin(), 20, "CHECKCOMPOUNDPATTERN", 20) != 0) {
    4034           0 :             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    4035           0 :                              af->getlinenum());
    4036           0 :             return false;
    4037             :           }
    4038           0 :           break;
    4039             :         }
    4040             :         case 1: {
    4041           0 :           checkcpdtable.back().pattern.assign(start_piece, iter);
    4042           0 :           size_t slash_pos = checkcpdtable.back().pattern.find('/');
    4043           0 :           if (slash_pos != std::string::npos) {
    4044           0 :             std::string chunk(checkcpdtable.back().pattern, slash_pos + 1);
    4045           0 :             checkcpdtable.back().pattern.resize(slash_pos);
    4046           0 :             checkcpdtable.back().cond = pHMgr->decode_flag(chunk.c_str());
    4047             :           }
    4048           0 :           break;
    4049             :         }
    4050             :         case 2: {
    4051           0 :           checkcpdtable.back().pattern2.assign(start_piece, iter);
    4052           0 :           size_t slash_pos = checkcpdtable.back().pattern2.find('/');
    4053           0 :           if (slash_pos != std::string::npos) {
    4054           0 :             std::string chunk(checkcpdtable.back().pattern2, slash_pos + 1);
    4055           0 :             checkcpdtable.back().pattern2.resize(slash_pos);
    4056           0 :             checkcpdtable.back().cond2 = pHMgr->decode_flag(chunk.c_str());
    4057             :           }
    4058           0 :           break;
    4059             :         }
    4060             :         case 3: {
    4061           0 :           checkcpdtable.back().pattern3.assign(start_piece, iter);
    4062           0 :           simplifiedcpd = 1;
    4063           0 :           break;
    4064             :         }
    4065             :         default:
    4066           0 :           break;
    4067             :       }
    4068           0 :       i++;
    4069           0 :       start_piece = mystrsep(nl, iter);
    4070             :     }
    4071             :   }
    4072           0 :   return true;
    4073             : }
    4074             : 
    4075             : /* parse in the compound rule table */
    4076           0 : bool AffixMgr::parse_defcpdtable(const std::string& line, FileMgr* af) {
    4077           0 :   if (parseddefcpd) {
    4078           0 :     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
    4079           0 :                      af->getlinenum());
    4080           0 :     return false;
    4081             :   }
    4082           0 :   parseddefcpd = true;
    4083           0 :   int numdefcpd = -1;
    4084           0 :   int i = 0;
    4085           0 :   int np = 0;
    4086           0 :   std::string::const_iterator iter = line.begin();
    4087           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    4088           0 :   while (start_piece != line.end()) {
    4089           0 :     switch (i) {
    4090             :       case 0: {
    4091           0 :         np++;
    4092           0 :         break;
    4093             :       }
    4094             :       case 1: {
    4095           0 :         numdefcpd = atoi(std::string(start_piece, iter).c_str());
    4096           0 :         if (numdefcpd < 1) {
    4097           0 :           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
    4098           0 :                            af->getlinenum());
    4099           0 :           return false;
    4100             :         }
    4101           0 :         defcpdtable.reserve(numdefcpd);
    4102           0 :         np++;
    4103           0 :         break;
    4104             :       }
    4105             :       default:
    4106           0 :         break;
    4107             :     }
    4108           0 :     ++i;
    4109           0 :     start_piece = mystrsep(line, iter);
    4110             :   }
    4111           0 :   if (np != 2) {
    4112           0 :     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
    4113           0 :                      af->getlinenum());
    4114           0 :     return false;
    4115             :   }
    4116             : 
    4117             :   /* now parse the numdefcpd lines to read in the remainder of the table */
    4118           0 :   for (int j = 0; j < numdefcpd; ++j) {
    4119           0 :     std::string nl;
    4120           0 :     if (!af->getline(nl))
    4121           0 :       return false;
    4122           0 :     mychomp(nl);
    4123           0 :     i = 0;
    4124           0 :     defcpdtable.push_back(flagentry());
    4125           0 :     iter = nl.begin();
    4126           0 :     start_piece = mystrsep(nl, iter);
    4127           0 :     while (start_piece != nl.end()) {
    4128           0 :       switch (i) {
    4129             :         case 0: {
    4130           0 :           if (nl.compare(start_piece - nl.begin(), 12, "COMPOUNDRULE", 12) != 0) {
    4131           0 :             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    4132           0 :                              af->getlinenum());
    4133           0 :             numdefcpd = 0;
    4134           0 :             return false;
    4135             :           }
    4136           0 :           break;
    4137             :         }
    4138             :         case 1: {  // handle parenthesized flags
    4139           0 :           if (std::find(start_piece, iter, '(') != iter) {
    4140           0 :             for (std::string::const_iterator k = start_piece; k != iter; ++k) {
    4141           0 :               std::string::const_iterator chb = k;
    4142           0 :               std::string::const_iterator che = k + 1;
    4143           0 :               if (*k == '(') {
    4144           0 :                 std::string::const_iterator parpos = std::find(k, iter, ')');
    4145           0 :                 if (parpos != iter) {
    4146           0 :                   chb = k + 1;
    4147           0 :                   che = parpos;
    4148           0 :                   k = parpos;
    4149             :                 }
    4150             :               }
    4151             : 
    4152           0 :               if (*chb == '*' || *chb == '?') {
    4153           0 :                 defcpdtable.back().push_back((FLAG)*chb);
    4154             :               } else {
    4155           0 :                 pHMgr->decode_flags(defcpdtable.back(), std::string(chb, che), af);
    4156             :               }
    4157             :             }
    4158             :           } else {
    4159           0 :             pHMgr->decode_flags(defcpdtable.back(), std::string(start_piece, iter), af);
    4160             :           }
    4161           0 :           break;
    4162             :         }
    4163             :         default:
    4164           0 :           break;
    4165             :       }
    4166           0 :       ++i;
    4167           0 :       start_piece = mystrsep(nl, iter);
    4168             :     }
    4169           0 :     if (defcpdtable.back().empty()) {
    4170           0 :       HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    4171           0 :                        af->getlinenum());
    4172           0 :       return false;
    4173             :     }
    4174             :   }
    4175           0 :   return true;
    4176             : }
    4177             : 
    4178             : /* parse in the character map table */
    4179           0 : bool AffixMgr::parse_maptable(const std::string& line, FileMgr* af) {
    4180           0 :   if (parsedmaptable) {
    4181           0 :     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
    4182           0 :                      af->getlinenum());
    4183           0 :     return false;
    4184             :   }
    4185           0 :   parsedmaptable = true;
    4186           0 :   int nummap = -1;
    4187           0 :   int i = 0;
    4188           0 :   int np = 0;
    4189           0 :   std::string::const_iterator iter = line.begin();
    4190           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    4191           0 :   while (start_piece != line.end()) {
    4192           0 :     switch (i) {
    4193             :       case 0: {
    4194           0 :         np++;
    4195           0 :         break;
    4196             :       }
    4197             :       case 1: {
    4198           0 :         nummap = atoi(std::string(start_piece, iter).c_str());
    4199           0 :         if (nummap < 1) {
    4200           0 :           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
    4201           0 :                            af->getlinenum());
    4202           0 :           return false;
    4203             :         }
    4204           0 :         maptable.reserve(nummap);
    4205           0 :         np++;
    4206           0 :         break;
    4207             :       }
    4208             :       default:
    4209           0 :         break;
    4210             :     }
    4211           0 :     ++i;
    4212           0 :     start_piece = mystrsep(line, iter);
    4213             :   }
    4214           0 :   if (np != 2) {
    4215           0 :     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
    4216           0 :                      af->getlinenum());
    4217           0 :     return false;
    4218             :   }
    4219             : 
    4220             :   /* now parse the nummap lines to read in the remainder of the table */
    4221           0 :   for (int j = 0; j < nummap; ++j) {
    4222           0 :     std::string nl;
    4223           0 :     if (!af->getline(nl))
    4224           0 :       return false;
    4225           0 :     mychomp(nl);
    4226           0 :     i = 0;
    4227           0 :     maptable.push_back(mapentry());
    4228           0 :     iter = nl.begin();
    4229           0 :     start_piece = mystrsep(nl, iter);
    4230           0 :     while (start_piece != nl.end()) {
    4231           0 :       switch (i) {
    4232             :         case 0: {
    4233           0 :           if (nl.compare(start_piece - nl.begin(), 3, "MAP", 3) != 0) {
    4234           0 :             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    4235           0 :                              af->getlinenum());
    4236           0 :             nummap = 0;
    4237           0 :             return false;
    4238             :           }
    4239           0 :           break;
    4240             :         }
    4241             :         case 1: {
    4242           0 :           for (std::string::const_iterator k = start_piece; k != iter; ++k) {
    4243           0 :             std::string::const_iterator chb = k;
    4244           0 :             std::string::const_iterator che = k + 1;
    4245           0 :             if (*k == '(') {
    4246           0 :               std::string::const_iterator parpos = std::find(k, iter, ')');
    4247           0 :               if (parpos != iter) {
    4248           0 :                 chb = k + 1;
    4249           0 :                 che = parpos;
    4250           0 :                 k = parpos;
    4251             :               }
    4252             :             } else {
    4253           0 :               if (utf8 && (*k & 0xc0) == 0xc0) {
    4254           0 :                 ++k;
    4255           0 :                 while (k != iter && (*k & 0xc0) == 0x80)
    4256           0 :                     ++k;
    4257           0 :                 che = k;
    4258           0 :                 --k;
    4259             :               }
    4260             :             }
    4261           0 :             maptable.back().push_back(std::string(chb, che));
    4262             :           }
    4263           0 :           break;
    4264             :         }
    4265             :         default:
    4266           0 :           break;
    4267             :       }
    4268           0 :       ++i;
    4269           0 :       start_piece = mystrsep(nl, iter);
    4270             :     }
    4271           0 :     if (maptable.back().empty()) {
    4272           0 :       HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    4273           0 :                        af->getlinenum());
    4274           0 :       return false;
    4275             :     }
    4276             :   }
    4277           0 :   return true;
    4278             : }
    4279             : 
    4280             : /* parse in the word breakpoint table */
    4281           0 : bool AffixMgr::parse_breaktable(const std::string& line, FileMgr* af) {
    4282           0 :   if (parsedbreaktable) {
    4283           0 :     HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n",
    4284           0 :                      af->getlinenum());
    4285           0 :     return false;
    4286             :   }
    4287           0 :   parsedbreaktable = true;
    4288           0 :   int numbreak = -1;
    4289           0 :   int i = 0;
    4290           0 :   int np = 0;
    4291           0 :   std::string::const_iterator iter = line.begin();
    4292           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    4293           0 :   while (start_piece != line.end()) {
    4294           0 :     switch (i) {
    4295             :       case 0: {
    4296           0 :         np++;
    4297           0 :         break;
    4298             :       }
    4299             :       case 1: {
    4300           0 :         numbreak = atoi(std::string(start_piece, iter).c_str());
    4301           0 :         if (numbreak < 0) {
    4302           0 :           HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
    4303           0 :                            af->getlinenum());
    4304           0 :           return false;
    4305             :         }
    4306           0 :         if (numbreak == 0)
    4307           0 :           return true;
    4308           0 :         breaktable.reserve(numbreak);
    4309           0 :         np++;
    4310           0 :         break;
    4311             :       }
    4312             :       default:
    4313           0 :         break;
    4314             :     }
    4315           0 :     ++i;
    4316           0 :     start_piece = mystrsep(line, iter);
    4317             :   }
    4318           0 :   if (np != 2) {
    4319           0 :     HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
    4320           0 :                      af->getlinenum());
    4321           0 :     return false;
    4322             :   }
    4323             : 
    4324             :   /* now parse the numbreak lines to read in the remainder of the table */
    4325           0 :   for (int j = 0; j < numbreak; ++j) {
    4326           0 :     std::string nl;
    4327           0 :     if (!af->getline(nl))
    4328           0 :       return false;
    4329           0 :     mychomp(nl);
    4330           0 :     i = 0;
    4331           0 :     iter = nl.begin();
    4332           0 :     start_piece = mystrsep(nl, iter);
    4333           0 :     while (start_piece != nl.end()) {
    4334           0 :       switch (i) {
    4335             :         case 0: {
    4336           0 :           if (nl.compare(start_piece - nl.begin(), 5, "BREAK", 5) != 0) {
    4337           0 :             HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    4338           0 :                              af->getlinenum());
    4339           0 :             numbreak = 0;
    4340           0 :             return false;
    4341             :           }
    4342           0 :           break;
    4343             :         }
    4344             :         case 1: {
    4345           0 :           breaktable.push_back(std::string(start_piece, iter));
    4346           0 :           break;
    4347             :         }
    4348             :         default:
    4349           0 :           break;
    4350             :       }
    4351           0 :       ++i;
    4352           0 :       start_piece = mystrsep(nl, iter);
    4353             :     }
    4354             :   }
    4355             : 
    4356           0 :   if (breaktable.size() != static_cast<size_t>(numbreak)) {
    4357           0 :     HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n",
    4358           0 :                      af->getlinenum());
    4359           0 :     return false;
    4360             :   }
    4361             : 
    4362           0 :   return true;
    4363             : }
    4364             : 
    4365           0 : void AffixMgr::reverse_condition(std::string& piece) {
    4366           0 :   if (piece.empty())
    4367           0 :       return;
    4368             : 
    4369           0 :   int neg = 0;
    4370           0 :   for (std::string::reverse_iterator k = piece.rbegin(); k != piece.rend(); ++k) {
    4371           0 :     switch (*k) {
    4372             :       case '[': {
    4373           0 :         if (neg)
    4374           0 :           *(k - 1) = '[';
    4375             :         else
    4376           0 :           *k = ']';
    4377           0 :         break;
    4378             :       }
    4379             :       case ']': {
    4380           0 :         *k = '[';
    4381           0 :         if (neg)
    4382           0 :           *(k - 1) = '^';
    4383           0 :         neg = 0;
    4384           0 :         break;
    4385             :       }
    4386             :       case '^': {
    4387           0 :         if (*(k - 1) == ']')
    4388           0 :           neg = 1;
    4389             :         else
    4390           0 :           *(k - 1) = *k;
    4391           0 :         break;
    4392             :       }
    4393             :       default: {
    4394           0 :         if (neg)
    4395           0 :           *(k - 1) = *k;
    4396             :       }
    4397             :     }
    4398             :   }
    4399             : }
    4400             : 
    4401             : class entries_container {
    4402             :   std::vector<AffEntry*> entries;
    4403             :   AffixMgr* m_mgr;
    4404             :   char m_at;
    4405             : public:
    4406           0 :   entries_container(char at, AffixMgr* mgr)
    4407           0 :     : m_mgr(mgr)
    4408           0 :     , m_at(at) {
    4409           0 :   }
    4410           0 :   void release() {
    4411           0 :     entries.clear();
    4412           0 :   }
    4413           0 :   void initialize(int numents,
    4414             :                   char opts, unsigned short aflag) {
    4415           0 :     entries.reserve(numents);
    4416             : 
    4417           0 :     if (m_at == 'P') {
    4418           0 :       entries.push_back(new PfxEntry(m_mgr));
    4419             :     } else {
    4420           0 :       entries.push_back(new SfxEntry(m_mgr));
    4421             :     }
    4422             : 
    4423           0 :     entries.back()->opts = opts;
    4424           0 :     entries.back()->aflag = aflag;
    4425           0 :   }
    4426             : 
    4427           0 :   AffEntry* add_entry(char opts) {
    4428           0 :     if (m_at == 'P') {
    4429           0 :       entries.push_back(new PfxEntry(m_mgr));
    4430             :     } else {
    4431           0 :       entries.push_back(new SfxEntry(m_mgr));
    4432             :     }
    4433           0 :     AffEntry* ret = entries.back();
    4434           0 :     ret->opts = entries[0]->opts & opts;
    4435           0 :     return ret;
    4436             :   }
    4437             : 
    4438           0 :   AffEntry* first_entry() {
    4439           0 :     return entries.empty() ? NULL : entries[0];
    4440             :   }
    4441             : 
    4442           0 :   ~entries_container() {
    4443           0 :     for (size_t i = 0; i < entries.size(); ++i) {
    4444           0 :         delete entries[i];
    4445             :     }
    4446           0 :   }
    4447             : 
    4448           0 :   std::vector<AffEntry*>::iterator begin() { return entries.begin(); }
    4449           0 :   std::vector<AffEntry*>::iterator end() { return entries.end(); }
    4450             : };
    4451             : 
    4452           0 : bool AffixMgr::parse_affix(const std::string& line,
    4453             :                           const char at,
    4454             :                           FileMgr* af,
    4455             :                           char* dupflags) {
    4456           0 :   int numents = 0;  // number of AffEntry structures to parse
    4457             : 
    4458           0 :   unsigned short aflag = 0;  // affix char identifier
    4459             : 
    4460           0 :   char ff = 0;
    4461           0 :   entries_container affentries(at, this);
    4462             : 
    4463           0 :   int i = 0;
    4464             : 
    4465             : // checking lines with bad syntax
    4466             : #ifdef DEBUG
    4467           0 :   int basefieldnum = 0;
    4468             : #endif
    4469             : 
    4470             :   // split affix header line into pieces
    4471             : 
    4472           0 :   int np = 0;
    4473           0 :   std::string::const_iterator iter = line.begin();
    4474           0 :   std::string::const_iterator start_piece = mystrsep(line, iter);
    4475           0 :   while (start_piece != line.end()) {
    4476           0 :     switch (i) {
    4477             :       // piece 1 - is type of affix
    4478             :       case 0: {
    4479           0 :         np++;
    4480           0 :         break;
    4481             :       }
    4482             : 
    4483             :       // piece 2 - is affix char
    4484             :       case 1: {
    4485           0 :         np++;
    4486           0 :         aflag = pHMgr->decode_flag(std::string(start_piece, iter).c_str());
    4487           0 :         if (((at == 'S') && (dupflags[aflag] & dupSFX)) ||
    4488           0 :             ((at == 'P') && (dupflags[aflag] & dupPFX))) {
    4489           0 :           HUNSPELL_WARNING(
    4490             :               stderr,
    4491             :               "error: line %d: multiple definitions of an affix flag\n",
    4492           0 :               af->getlinenum());
    4493             :         }
    4494           0 :         dupflags[aflag] += (char)((at == 'S') ? dupSFX : dupPFX);
    4495           0 :         break;
    4496             :       }
    4497             :       // piece 3 - is cross product indicator
    4498             :       case 2: {
    4499           0 :         np++;
    4500           0 :         if (*start_piece == 'Y')
    4501           0 :           ff = aeXPRODUCT;
    4502           0 :         break;
    4503             :       }
    4504             : 
    4505             :       // piece 4 - is number of affentries
    4506             :       case 3: {
    4507           0 :         np++;
    4508           0 :         numents = atoi(std::string(start_piece, iter).c_str());
    4509           0 :         if ((numents <= 0) || ((std::numeric_limits<size_t>::max() /
    4510           0 :                                 sizeof(AffEntry)) < static_cast<size_t>(numents))) {
    4511           0 :           char* err = pHMgr->encode_flag(aflag);
    4512           0 :           if (err) {
    4513           0 :             HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n",
    4514           0 :                              af->getlinenum());
    4515           0 :             free(err);
    4516             :           }
    4517           0 :           return false;
    4518             :         }
    4519             : 
    4520           0 :         char opts = ff;
    4521           0 :         if (utf8)
    4522           0 :           opts += aeUTF8;
    4523           0 :         if (pHMgr->is_aliasf())
    4524           0 :           opts += aeALIASF;
    4525           0 :         if (pHMgr->is_aliasm())
    4526           0 :           opts += aeALIASM;
    4527           0 :         affentries.initialize(numents, opts, aflag);
    4528             :       }
    4529             : 
    4530             :       default:
    4531           0 :         break;
    4532             :     }
    4533           0 :     ++i;
    4534           0 :     start_piece = mystrsep(line, iter);
    4535             :   }
    4536             :   // check to make sure we parsed enough pieces
    4537           0 :   if (np != 4) {
    4538           0 :     char* err = pHMgr->encode_flag(aflag);
    4539           0 :     if (err) {
    4540           0 :       HUNSPELL_WARNING(stderr, "error: line %d: missing data\n",
    4541           0 :                        af->getlinenum());
    4542           0 :       free(err);
    4543             :     }
    4544           0 :     return false;
    4545             :   }
    4546             : 
    4547             :   // now parse numents affentries for this affix
    4548           0 :   AffEntry* entry = affentries.first_entry();
    4549           0 :   for (int ent = 0; ent < numents; ++ent) {
    4550           0 :     std::string nl;
    4551           0 :     if (!af->getline(nl))
    4552           0 :       return false;
    4553           0 :     mychomp(nl);
    4554             : 
    4555           0 :     iter = nl.begin();
    4556           0 :     i = 0;
    4557           0 :     np = 0;
    4558             : 
    4559             :     // split line into pieces
    4560           0 :     start_piece = mystrsep(nl, iter);
    4561           0 :     while (start_piece != nl.end()) {
    4562           0 :       switch (i) {
    4563             :         // piece 1 - is type
    4564             :         case 0: {
    4565           0 :           np++;
    4566           0 :           if (ent != 0)
    4567           0 :             entry = affentries.add_entry((char)(aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM));
    4568           0 :           break;
    4569             :         }
    4570             : 
    4571             :         // piece 2 - is affix char
    4572             :         case 1: {
    4573           0 :           np++;
    4574           0 :           std::string chunk(start_piece, iter);
    4575           0 :           if (pHMgr->decode_flag(chunk.c_str()) != aflag) {
    4576           0 :             char* err = pHMgr->encode_flag(aflag);
    4577           0 :             if (err) {
    4578           0 :               HUNSPELL_WARNING(stderr,
    4579             :                                "error: line %d: affix %s is corrupt\n",
    4580           0 :                                af->getlinenum(), err);
    4581           0 :               free(err);
    4582             :             }
    4583           0 :             return false;
    4584             :           }
    4585             : 
    4586           0 :           if (ent != 0) {
    4587           0 :             AffEntry* start_entry = affentries.first_entry();
    4588           0 :             entry->aflag = start_entry->aflag;
    4589             :           }
    4590           0 :           break;
    4591             :         }
    4592             : 
    4593             :         // piece 3 - is string to strip or 0 for null
    4594             :         case 2: {
    4595           0 :           np++;
    4596           0 :           entry->strip = std::string(start_piece, iter);
    4597           0 :           if (complexprefixes) {
    4598           0 :             if (utf8)
    4599           0 :               reverseword_utf(entry->strip);
    4600             :             else
    4601           0 :               reverseword(entry->strip);
    4602             :           }
    4603           0 :           if (entry->strip.compare("0") == 0) {
    4604           0 :             entry->strip.clear();
    4605             :           }
    4606           0 :           break;
    4607             :         }
    4608             : 
    4609             :         // piece 4 - is affix string or 0 for null
    4610             :         case 3: {
    4611           0 :           entry->morphcode = NULL;
    4612           0 :           entry->contclass = NULL;
    4613           0 :           entry->contclasslen = 0;
    4614           0 :           np++;
    4615           0 :           std::string::const_iterator dash = std::find(start_piece, iter, '/');
    4616           0 :           if (dash != iter) {
    4617           0 :             entry->appnd = std::string(start_piece, dash);
    4618           0 :             std::string dash_str(dash + 1, iter);
    4619             : 
    4620           0 :             if (!ignorechars.empty()) {
    4621           0 :               if (utf8) {
    4622           0 :                 remove_ignored_chars_utf(entry->appnd, ignorechars_utf16);
    4623             :               } else {
    4624           0 :                 remove_ignored_chars(entry->appnd, ignorechars);
    4625             :               }
    4626             :             }
    4627             : 
    4628           0 :             if (complexprefixes) {
    4629           0 :               if (utf8)
    4630           0 :                 reverseword_utf(entry->appnd);
    4631             :               else
    4632           0 :                 reverseword(entry->appnd);
    4633             :             }
    4634             : 
    4635           0 :             if (pHMgr->is_aliasf()) {
    4636           0 :               int index = atoi(dash_str.c_str());
    4637           0 :               entry->contclasslen = (unsigned short)pHMgr->get_aliasf(
    4638             :                   index, &(entry->contclass), af);
    4639           0 :               if (!entry->contclasslen)
    4640           0 :                 HUNSPELL_WARNING(stderr,
    4641             :                                  "error: bad affix flag alias: \"%s\"\n",
    4642           0 :                                  dash_str.c_str());
    4643             :             } else {
    4644           0 :               entry->contclasslen = (unsigned short)pHMgr->decode_flags(
    4645             :                   &(entry->contclass), dash_str.c_str(), af);
    4646           0 :               std::sort(entry->contclass, entry->contclass + entry->contclasslen);
    4647             :             }
    4648             : 
    4649           0 :             havecontclass = 1;
    4650           0 :             for (unsigned short _i = 0; _i < entry->contclasslen; _i++) {
    4651           0 :               contclasses[(entry->contclass)[_i]] = 1;
    4652             :             }
    4653             :           } else {
    4654           0 :             entry->appnd = std::string(start_piece, iter);
    4655             : 
    4656           0 :             if (!ignorechars.empty()) {
    4657           0 :               if (utf8) {
    4658           0 :                 remove_ignored_chars_utf(entry->appnd, ignorechars_utf16);
    4659             :               } else {
    4660           0 :                 remove_ignored_chars(entry->appnd, ignorechars);
    4661             :               }
    4662             :             }
    4663             : 
    4664           0 :             if (complexprefixes) {
    4665           0 :               if (utf8)
    4666           0 :                 reverseword_utf(entry->appnd);
    4667             :               else
    4668           0 :                 reverseword(entry->appnd);
    4669             :             }
    4670             :           }
    4671             : 
    4672           0 :           if (entry->appnd.compare("0") == 0) {
    4673           0 :             entry->appnd.clear();
    4674             :           }
    4675           0 :           break;
    4676             :         }
    4677             : 
    4678             :         // piece 5 - is the conditions descriptions
    4679             :         case 4: {
    4680           0 :           std::string chunk(start_piece, iter);
    4681           0 :           np++;
    4682           0 :           if (complexprefixes) {
    4683           0 :             if (utf8)
    4684           0 :               reverseword_utf(chunk);
    4685             :             else
    4686           0 :               reverseword(chunk);
    4687           0 :             reverse_condition(chunk);
    4688             :           }
    4689           0 :           if (!entry->strip.empty() && chunk != "." &&
    4690           0 :               redundant_condition(at, entry->strip.c_str(), entry->strip.size(), chunk.c_str(),
    4691             :                                   af->getlinenum()))
    4692           0 :             chunk = ".";
    4693           0 :           if (at == 'S') {
    4694           0 :             reverseword(chunk);
    4695           0 :             reverse_condition(chunk);
    4696             :           }
    4697           0 :           if (encodeit(*entry, chunk.c_str()))
    4698           0 :             return false;
    4699           0 :           break;
    4700             :         }
    4701             : 
    4702             :         case 5: {
    4703           0 :           std::string chunk(start_piece, iter);
    4704           0 :           np++;
    4705           0 :           if (pHMgr->is_aliasm()) {
    4706           0 :             int index = atoi(chunk.c_str());
    4707           0 :             entry->morphcode = pHMgr->get_aliasm(index);
    4708             :           } else {
    4709           0 :             if (complexprefixes) {  // XXX - fix me for morph. gen.
    4710           0 :               if (utf8)
    4711           0 :                 reverseword_utf(chunk);
    4712             :               else
    4713           0 :                 reverseword(chunk);
    4714             :             }
    4715             :             // add the remaining of the line
    4716           0 :             std::string::const_iterator end = nl.end();
    4717           0 :             if (iter != end) {
    4718           0 :               chunk.append(iter, end);
    4719             :             }
    4720           0 :             entry->morphcode = mystrdup(chunk.c_str());
    4721           0 :             if (!entry->morphcode)
    4722           0 :               return false;
    4723             :           }
    4724           0 :           break;
    4725             :         }
    4726             :         default:
    4727           0 :           break;
    4728             :       }
    4729           0 :       i++;
    4730           0 :       start_piece = mystrsep(nl, iter);
    4731             :     }
    4732             :     // check to make sure we parsed enough pieces
    4733           0 :     if (np < 4) {
    4734           0 :       char* err = pHMgr->encode_flag(aflag);
    4735           0 :       if (err) {
    4736           0 :         HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n",
    4737           0 :                          af->getlinenum(), err);
    4738           0 :         free(err);
    4739             :       }
    4740           0 :       return false;
    4741             :     }
    4742             : 
    4743             : #ifdef DEBUG
    4744             :     // detect unnecessary fields, excepting comments
    4745           0 :     if (basefieldnum) {
    4746             :       int fieldnum =
    4747           0 :           !(entry->morphcode) ? 5 : ((*(entry->morphcode) == '#') ? 5 : 6);
    4748           0 :       if (fieldnum != basefieldnum)
    4749           0 :         HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n",
    4750           0 :                          af->getlinenum());
    4751             :     } else {
    4752           0 :       basefieldnum =
    4753           0 :           !(entry->morphcode) ? 5 : ((*(entry->morphcode) == '#') ? 5 : 6);
    4754             :     }
    4755             : #endif
    4756             :   }
    4757             : 
    4758             :   // now create SfxEntry or PfxEntry objects and use links to
    4759             :   // build an ordered (sorted by affix string) list
    4760           0 :   std::vector<AffEntry*>::iterator start = affentries.begin();
    4761           0 :   std::vector<AffEntry*>::iterator end = affentries.end();
    4762           0 :   for (std::vector<AffEntry*>::iterator affentry = start; affentry != end; ++affentry) {
    4763           0 :     if (at == 'P') {
    4764           0 :       build_pfxtree(static_cast<PfxEntry*>(*affentry));
    4765             :     } else {
    4766           0 :       build_sfxtree(static_cast<SfxEntry*>(*affentry));
    4767             :     }
    4768             :   }
    4769             : 
    4770             :   //contents belong to AffixMgr now
    4771           0 :   affentries.release();
    4772             : 
    4773           0 :   return true;
    4774             : }
    4775             : 
    4776           0 : int AffixMgr::redundant_condition(char ft,
    4777             :                                   const char* strip,
    4778             :                                   int stripl,
    4779             :                                   const char* cond,
    4780             :                                   int linenum) {
    4781           0 :   int condl = strlen(cond);
    4782             :   int i;
    4783             :   int j;
    4784             :   int neg;
    4785             :   int in;
    4786           0 :   if (ft == 'P') {  // prefix
    4787           0 :     if (strncmp(strip, cond, condl) == 0)
    4788           0 :       return 1;
    4789           0 :     if (utf8) {
    4790             :     } else {
    4791           0 :       for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) {
    4792           0 :         if (cond[j] != '[') {
    4793           0 :           if (cond[j] != strip[i]) {
    4794             :             HUNSPELL_WARNING(stderr,
    4795             :                              "warning: line %d: incompatible stripping "
    4796             :                              "characters and condition\n",
    4797           0 :                              linenum);
    4798           0 :             return 0;
    4799             :           }
    4800             :         } else {
    4801           0 :           neg = (cond[j + 1] == '^') ? 1 : 0;
    4802           0 :           in = 0;
    4803           0 :           do {
    4804           0 :             j++;
    4805           0 :             if (strip[i] == cond[j])
    4806           0 :               in = 1;
    4807           0 :           } while ((j < (condl - 1)) && (cond[j] != ']'));
    4808           0 :           if (j == (condl - 1) && (cond[j] != ']')) {
    4809             :             HUNSPELL_WARNING(stderr,
    4810             :                              "error: line %d: missing ] in condition:\n%s\n",
    4811           0 :                              linenum, cond);
    4812           0 :             return 0;
    4813             :           }
    4814           0 :           if ((!neg && !in) || (neg && in)) {
    4815             :             HUNSPELL_WARNING(stderr,
    4816             :                              "warning: line %d: incompatible stripping "
    4817             :                              "characters and condition\n",
    4818           0 :                              linenum);
    4819           0 :             return 0;
    4820             :           }
    4821             :         }
    4822             :       }
    4823           0 :       if (j >= condl)
    4824           0 :         return 1;
    4825             :     }
    4826             :   } else {  // suffix
    4827           0 :     if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0)
    4828           0 :       return 1;
    4829           0 :     if (utf8) {
    4830             :     } else {
    4831           0 :       for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) {
    4832           0 :         if (cond[j] != ']') {
    4833           0 :           if (cond[j] != strip[i]) {
    4834             :             HUNSPELL_WARNING(stderr,
    4835             :                              "warning: line %d: incompatible stripping "
    4836             :                              "characters and condition\n",
    4837           0 :                              linenum);
    4838           0 :             return 0;
    4839             :           }
    4840             :         } else {
    4841           0 :           in = 0;
    4842           0 :           do {
    4843           0 :             j--;
    4844           0 :             if (strip[i] == cond[j])
    4845           0 :               in = 1;
    4846           0 :           } while ((j > 0) && (cond[j] != '['));
    4847           0 :           if ((j == 0) && (cond[j] != '[')) {
    4848             :             HUNSPELL_WARNING(stderr,
    4849             :                              "error: line: %d: missing ] in condition:\n%s\n",
    4850           0 :                              linenum, cond);
    4851           0 :             return 0;
    4852             :           }
    4853           0 :           neg = (cond[j + 1] == '^') ? 1 : 0;
    4854           0 :           if ((!neg && !in) || (neg && in)) {
    4855             :             HUNSPELL_WARNING(stderr,
    4856             :                              "warning: line %d: incompatible stripping "
    4857             :                              "characters and condition\n",
    4858           0 :                              linenum);
    4859           0 :             return 0;
    4860             :           }
    4861             :         }
    4862             :       }
    4863           0 :       if (j < 0)
    4864           0 :         return 1;
    4865             :     }
    4866             :   }
    4867           0 :   return 0;
    4868             : }
    4869             : 
    4870           0 : std::vector<std::string> AffixMgr::get_suffix_words(short unsigned* suff,
    4871             :                                int len,
    4872             :                                const char* root_word) {
    4873           0 :   std::vector<std::string> slst;
    4874           0 :   short unsigned* start_ptr = suff;
    4875           0 :   for (int j = 0; j < SETSIZE; j++) {
    4876           0 :     SfxEntry* ptr = sStart[j];
    4877           0 :     while (ptr) {
    4878           0 :       suff = start_ptr;
    4879           0 :       for (int i = 0; i < len; i++) {
    4880           0 :         if ((*suff) == ptr->getFlag()) {
    4881           0 :           std::string nw(root_word);
    4882           0 :           nw.append(ptr->getAffix());
    4883           0 :           hentry* ht = ptr->checkword(nw.c_str(), nw.size(), 0, NULL, 0, 0, 0);
    4884           0 :           if (ht) {
    4885           0 :             slst.push_back(nw);
    4886             :           }
    4887             :         }
    4888           0 :         suff++;
    4889             :       }
    4890           0 :       ptr = ptr->getNext();
    4891             :     }
    4892             :   }
    4893           0 :   return slst;
    4894             : }

Generated by: LCOV version 1.13