LCOV - code coverage report
Current view: directory - extensions/spellcheck/hunspell/src - affixmgr.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 2545 1504 59.1 %
Date: 2012-06-02 Functions: 98 63 64.3 %

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

Generated by: LCOV version 1.7