michael@0: /******* BEGIN LICENSE BLOCK ******* michael@0: * Version: MPL 1.1/GPL 2.0/LGPL 2.1 michael@0: * michael@0: * The contents of this file are subject to the Mozilla Public License Version michael@0: * 1.1 (the "License"); you may not use this file except in compliance with michael@0: * the License. You may obtain a copy of the License at michael@0: * http://www.mozilla.org/MPL/ michael@0: * michael@0: * Software distributed under the License is distributed on an "AS IS" basis, michael@0: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License michael@0: * for the specific language governing rights and limitations under the michael@0: * License. michael@0: * michael@0: * The Initial Developers of the Original Code are Kevin Hendricks (MySpell) michael@0: * and László Németh (Hunspell). Portions created by the Initial Developers michael@0: * are Copyright (C) 2002-2005 the Initial Developers. All Rights Reserved. michael@0: * michael@0: * Contributor(s): Kevin Hendricks (kevin.hendricks@sympatico.ca) michael@0: * David Einstein (deinst@world.std.com) michael@0: * László Németh (nemethl@gyorsposta.hu) michael@0: * Caolan McNamara (caolanm@redhat.com) michael@0: * Davide Prina michael@0: * Giuseppe Modugno michael@0: * Gianluca Turconi michael@0: * Simon Brouwer michael@0: * Noll Janos michael@0: * Biro Arpad michael@0: * Goldman Eleonora michael@0: * Sarlos Tamas michael@0: * Bencsath Boldizsar michael@0: * Halacsy Peter michael@0: * Dvornik Laszlo michael@0: * Gefferth Andras michael@0: * Nagy Viktor michael@0: * Varga Daniel michael@0: * Chris Halls michael@0: * Rene Engelhard michael@0: * Bram Moolenaar michael@0: * Dafydd Jones michael@0: * Harri Pitkanen michael@0: * Andras Timar michael@0: * Tor Lillqvist michael@0: * michael@0: * Alternatively, the contents of this file may be used under the terms of michael@0: * either the GNU General Public License Version 2 or later (the "GPL"), or michael@0: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), michael@0: * in which case the provisions of the GPL or the LGPL are applicable instead michael@0: * of those above. If you wish to allow use of your version of this file only michael@0: * under the terms of either the GPL or the LGPL, and not to allow others to michael@0: * use your version of this file under the terms of the MPL, indicate your michael@0: * decision by deleting the provisions above and replace them with the notice michael@0: * and other provisions required by the GPL or the LGPL. If you do not delete michael@0: * the provisions above, a recipient may use your version of this file under michael@0: * the terms of any one of the MPL, the GPL or the LGPL. michael@0: * michael@0: ******* END LICENSE BLOCK *******/ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: #include "affixmgr.hxx" michael@0: #include "affentry.hxx" michael@0: #include "langnum.hxx" michael@0: michael@0: #include "csutil.hxx" michael@0: michael@0: AffixMgr::AffixMgr(const char * affpath, HashMgr** ptr, int * md, const char * key) michael@0: { michael@0: // register hash manager and load affix data from aff file michael@0: pHMgr = ptr[0]; michael@0: alldic = ptr; michael@0: maxdic = md; michael@0: keystring = NULL; michael@0: trystring = NULL; michael@0: encoding=NULL; michael@0: csconv=NULL; michael@0: utf8 = 0; michael@0: complexprefixes = 0; michael@0: maptable = NULL; michael@0: nummap = 0; michael@0: breaktable = NULL; michael@0: numbreak = -1; michael@0: reptable = NULL; michael@0: numrep = 0; michael@0: iconvtable = NULL; michael@0: oconvtable = NULL; michael@0: checkcpdtable = NULL; michael@0: // allow simplified compound forms (see 3rd field of CHECKCOMPOUNDPATTERN) michael@0: simplifiedcpd = 0; michael@0: numcheckcpd = 0; michael@0: defcpdtable = NULL; michael@0: numdefcpd = 0; michael@0: phone = NULL; michael@0: compoundflag = FLAG_NULL; // permits word in compound forms michael@0: compoundbegin = FLAG_NULL; // may be first word in compound forms michael@0: compoundmiddle = FLAG_NULL; // may be middle word in compound forms michael@0: compoundend = FLAG_NULL; // may be last word in compound forms michael@0: compoundroot = FLAG_NULL; // compound word signing flag michael@0: compoundpermitflag = FLAG_NULL; // compound permitting flag for suffixed word michael@0: compoundforbidflag = FLAG_NULL; // compound fordidden flag for suffixed word michael@0: checkcompounddup = 0; // forbid double words in compounds michael@0: checkcompoundrep = 0; // forbid bad compounds (may be non compound word with a REP substitution) michael@0: checkcompoundcase = 0; // forbid upper and lowercase combinations at word bounds michael@0: checkcompoundtriple = 0; // forbid compounds with triple letters michael@0: simplifiedtriple = 0; // allow simplified triple letters in compounds (Schiff+fahrt -> Schiffahrt) michael@0: forbiddenword = FORBIDDENWORD; // forbidden word signing flag michael@0: nosuggest = FLAG_NULL; // don't suggest words signed with NOSUGGEST flag michael@0: nongramsuggest = FLAG_NULL; michael@0: lang = NULL; // language michael@0: langnum = 0; // language code (see http://l10n.openoffice.org/languages.html) michael@0: needaffix = FLAG_NULL; // forbidden root, allowed only with suffixes michael@0: cpdwordmax = -1; // default: unlimited wordcount in compound words michael@0: cpdmin = -1; // undefined michael@0: cpdmaxsyllable = 0; // default: unlimited syllablecount in compound words michael@0: cpdvowels=NULL; // vowels (for calculating of Hungarian compounding limit, O(n) search! XXX) michael@0: cpdvowels_utf16=NULL; // vowels for UTF-8 encoding (bsearch instead of O(n) search) michael@0: cpdvowels_utf16_len=0; // vowels michael@0: pfxappnd=NULL; // previous prefix for counting the syllables of prefix BUG michael@0: sfxappnd=NULL; // previous suffix for counting a special syllables BUG michael@0: cpdsyllablenum=NULL; // syllable count incrementing flag michael@0: checknum=0; // checking numbers, and word with numbers michael@0: wordchars=NULL; // letters + spec. word characters michael@0: wordchars_utf16=NULL; // letters + spec. word characters michael@0: wordchars_utf16_len=0; // letters + spec. word characters michael@0: ignorechars=NULL; // letters + spec. word characters michael@0: ignorechars_utf16=NULL; // letters + spec. word characters michael@0: ignorechars_utf16_len=0; // letters + spec. word characters michael@0: version=NULL; // affix and dictionary file version string michael@0: havecontclass=0; // flags of possible continuing classes (double affix) michael@0: // LEMMA_PRESENT: not put root into the morphological output. Lemma presents michael@0: // in morhological description in dictionary file. It's often combined with PSEUDOROOT. michael@0: lemma_present = FLAG_NULL; michael@0: circumfix = FLAG_NULL; michael@0: onlyincompound = FLAG_NULL; michael@0: maxngramsugs = -1; // undefined michael@0: maxdiff = -1; // undefined michael@0: onlymaxdiff = 0; michael@0: maxcpdsugs = -1; // undefined michael@0: nosplitsugs = 0; michael@0: sugswithdots = 0; michael@0: keepcase = 0; michael@0: forceucase = 0; michael@0: warn = 0; michael@0: forbidwarn = 0; michael@0: checksharps = 0; michael@0: substandard = FLAG_NULL; michael@0: fullstrip = 0; michael@0: michael@0: sfx = NULL; michael@0: pfx = NULL; michael@0: michael@0: for (int i=0; i < SETSIZE; i++) { michael@0: pStart[i] = NULL; michael@0: sStart[i] = NULL; michael@0: pFlag[i] = NULL; michael@0: sFlag[i] = NULL; michael@0: } michael@0: michael@0: for (int j=0; j < CONTSIZE; j++) { michael@0: contclasses[j] = 0; michael@0: } michael@0: michael@0: if (parse_file(affpath, key)) { michael@0: HUNSPELL_WARNING(stderr, "Failure loading aff file %s\n",affpath); michael@0: } michael@0: michael@0: if (cpdmin == -1) cpdmin = MINCPDLEN; michael@0: michael@0: } michael@0: michael@0: michael@0: AffixMgr::~AffixMgr() michael@0: { michael@0: // pass through linked prefix entries and clean up michael@0: for (int i=0; i < SETSIZE ;i++) { michael@0: pFlag[i] = NULL; michael@0: PfxEntry * ptr = pStart[i]; michael@0: PfxEntry * nptr = NULL; michael@0: while (ptr) { michael@0: nptr = ptr->getNext(); michael@0: delete(ptr); michael@0: ptr = nptr; michael@0: nptr = NULL; michael@0: } michael@0: } michael@0: michael@0: // pass through linked suffix entries and clean up michael@0: for (int j=0; j < SETSIZE ; j++) { michael@0: sFlag[j] = NULL; michael@0: SfxEntry * ptr = sStart[j]; michael@0: SfxEntry * nptr = NULL; michael@0: while (ptr) { michael@0: nptr = ptr->getNext(); michael@0: delete(ptr); michael@0: ptr = nptr; michael@0: nptr = NULL; michael@0: } michael@0: sStart[j] = NULL; michael@0: } michael@0: michael@0: if (keystring) free(keystring); michael@0: keystring=NULL; michael@0: if (trystring) free(trystring); michael@0: trystring=NULL; michael@0: if (encoding) free(encoding); michael@0: encoding=NULL; michael@0: if (maptable) { michael@0: for (int j=0; j < nummap; j++) { michael@0: for (int k=0; k < maptable[j].len; k++) { michael@0: if (maptable[j].set[k]) free(maptable[j].set[k]); michael@0: } michael@0: free(maptable[j].set); michael@0: maptable[j].set = NULL; michael@0: maptable[j].len = 0; michael@0: } michael@0: free(maptable); michael@0: maptable = NULL; michael@0: } michael@0: nummap = 0; michael@0: if (breaktable) { michael@0: for (int j=0; j < numbreak; j++) { michael@0: if (breaktable[j]) free(breaktable[j]); michael@0: breaktable[j] = NULL; michael@0: } michael@0: free(breaktable); michael@0: breaktable = NULL; michael@0: } michael@0: numbreak = 0; michael@0: if (reptable) { michael@0: for (int j=0; j < numrep; j++) { michael@0: free(reptable[j].pattern); michael@0: free(reptable[j].pattern2); michael@0: } michael@0: free(reptable); michael@0: reptable = NULL; michael@0: } michael@0: if (iconvtable) delete iconvtable; michael@0: if (oconvtable) delete oconvtable; michael@0: if (phone && phone->rules) { michael@0: for (int j=0; j < phone->num + 1; j++) { michael@0: free(phone->rules[j * 2]); michael@0: free(phone->rules[j * 2 + 1]); michael@0: } michael@0: free(phone->rules); michael@0: free(phone); michael@0: phone = NULL; michael@0: } michael@0: michael@0: if (defcpdtable) { michael@0: for (int j=0; j < numdefcpd; j++) { michael@0: free(defcpdtable[j].def); michael@0: defcpdtable[j].def = NULL; michael@0: } michael@0: free(defcpdtable); michael@0: defcpdtable = NULL; michael@0: } michael@0: numrep = 0; michael@0: if (checkcpdtable) { michael@0: for (int j=0; j < numcheckcpd; j++) { michael@0: free(checkcpdtable[j].pattern); michael@0: free(checkcpdtable[j].pattern2); michael@0: free(checkcpdtable[j].pattern3); michael@0: checkcpdtable[j].pattern = NULL; michael@0: checkcpdtable[j].pattern2 = NULL; michael@0: checkcpdtable[j].pattern3 = NULL; michael@0: } michael@0: free(checkcpdtable); michael@0: checkcpdtable = NULL; michael@0: } michael@0: numcheckcpd = 0; michael@0: FREE_FLAG(compoundflag); michael@0: FREE_FLAG(compoundbegin); michael@0: FREE_FLAG(compoundmiddle); michael@0: FREE_FLAG(compoundend); michael@0: FREE_FLAG(compoundpermitflag); michael@0: FREE_FLAG(compoundforbidflag); michael@0: FREE_FLAG(compoundroot); michael@0: FREE_FLAG(forbiddenword); michael@0: FREE_FLAG(nosuggest); michael@0: FREE_FLAG(nongramsuggest); michael@0: FREE_FLAG(needaffix); michael@0: FREE_FLAG(lemma_present); michael@0: FREE_FLAG(circumfix); michael@0: FREE_FLAG(onlyincompound); michael@0: michael@0: cpdwordmax = 0; michael@0: pHMgr = NULL; michael@0: cpdmin = 0; michael@0: cpdmaxsyllable = 0; michael@0: if (cpdvowels) free(cpdvowels); michael@0: if (cpdvowels_utf16) free(cpdvowels_utf16); michael@0: if (cpdsyllablenum) free(cpdsyllablenum); michael@0: free_utf_tbl(); michael@0: if (lang) free(lang); michael@0: if (wordchars) free(wordchars); michael@0: if (wordchars_utf16) free(wordchars_utf16); michael@0: if (ignorechars) free(ignorechars); michael@0: if (ignorechars_utf16) free(ignorechars_utf16); michael@0: if (version) free(version); michael@0: checknum=0; michael@0: #ifdef MOZILLA_CLIENT michael@0: delete [] csconv; michael@0: #endif michael@0: } michael@0: michael@0: michael@0: // read in aff file and build up prefix and suffix entry objects michael@0: int AffixMgr::parse_file(const char * affpath, const char * key) michael@0: { michael@0: char * line; // io buffers michael@0: char ft; // affix type michael@0: michael@0: // checking flag duplication michael@0: char dupflags[CONTSIZE]; michael@0: char dupflags_ini = 1; michael@0: michael@0: // first line indicator for removing byte order mark michael@0: int firstline = 1; michael@0: michael@0: // open the affix file michael@0: FileMgr * afflst = new FileMgr(affpath, key); michael@0: if (!afflst) { michael@0: HUNSPELL_WARNING(stderr, "error: could not open affix description file %s\n",affpath); michael@0: return 1; michael@0: } michael@0: michael@0: // step one is to parse the affix file building up the internal michael@0: // affix data structures michael@0: michael@0: // read in each line ignoring any that do not michael@0: // start with a known line type indicator michael@0: while ((line = afflst->getline())) { michael@0: mychomp(line); michael@0: michael@0: /* remove byte order mark */ michael@0: if (firstline) { michael@0: firstline = 0; michael@0: // Affix file begins with byte order mark: possible incompatibility with old Hunspell versions michael@0: if (strncmp(line,"\xEF\xBB\xBF",3) == 0) { michael@0: memmove(line, line+3, strlen(line+3)+1); michael@0: } michael@0: } michael@0: michael@0: /* parse in the keyboard string */ michael@0: if (strncmp(line,"KEY",3) == 0) { michael@0: if (parse_string(line, &keystring, afflst->getlinenum())) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the try string */ michael@0: if (strncmp(line,"TRY",3) == 0) { michael@0: if (parse_string(line, &trystring, afflst->getlinenum())) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the name of the character set used by the .dict and .aff */ michael@0: if (strncmp(line,"SET",3) == 0) { michael@0: if (parse_string(line, &encoding, afflst->getlinenum())) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: if (strcmp(encoding, "UTF-8") == 0) { michael@0: utf8 = 1; michael@0: #ifndef OPENOFFICEORG michael@0: #ifndef MOZILLA_CLIENT michael@0: if (initialize_utf_tbl()) return 1; michael@0: #endif michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: /* parse COMPLEXPREFIXES for agglutinative languages with right-to-left writing system */ michael@0: if (strncmp(line,"COMPLEXPREFIXES",15) == 0) michael@0: complexprefixes = 1; michael@0: michael@0: /* parse in the flag used by the controlled compound words */ michael@0: if (strncmp(line,"COMPOUNDFLAG",12) == 0) { michael@0: if (parse_flag(line, &compoundflag, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by compound words */ michael@0: if (strncmp(line,"COMPOUNDBEGIN",13) == 0) { michael@0: if (complexprefixes) { michael@0: if (parse_flag(line, &compoundend, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } else { michael@0: if (parse_flag(line, &compoundbegin, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by compound words */ michael@0: if (strncmp(line,"COMPOUNDMIDDLE",14) == 0) { michael@0: if (parse_flag(line, &compoundmiddle, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: /* parse in the flag used by compound words */ michael@0: if (strncmp(line,"COMPOUNDEND",11) == 0) { michael@0: if (complexprefixes) { michael@0: if (parse_flag(line, &compoundbegin, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } else { michael@0: if (parse_flag(line, &compoundend, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /* parse in the data used by compound_check() method */ michael@0: if (strncmp(line,"COMPOUNDWORDMAX",15) == 0) { michael@0: if (parse_num(line, &cpdwordmax, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag sign compounds in dictionary */ michael@0: if (strncmp(line,"COMPOUNDROOT",12) == 0) { michael@0: if (parse_flag(line, &compoundroot, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by compound_check() method */ michael@0: if (strncmp(line,"COMPOUNDPERMITFLAG",18) == 0) { michael@0: if (parse_flag(line, &compoundpermitflag, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by compound_check() method */ michael@0: if (strncmp(line,"COMPOUNDFORBIDFLAG",18) == 0) { michael@0: if (parse_flag(line, &compoundforbidflag, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: if (strncmp(line,"CHECKCOMPOUNDDUP",16) == 0) { michael@0: checkcompounddup = 1; michael@0: } michael@0: michael@0: if (strncmp(line,"CHECKCOMPOUNDREP",16) == 0) { michael@0: checkcompoundrep = 1; michael@0: } michael@0: michael@0: if (strncmp(line,"CHECKCOMPOUNDTRIPLE",19) == 0) { michael@0: checkcompoundtriple = 1; michael@0: } michael@0: michael@0: if (strncmp(line,"SIMPLIFIEDTRIPLE",16) == 0) { michael@0: simplifiedtriple = 1; michael@0: } michael@0: michael@0: if (strncmp(line,"CHECKCOMPOUNDCASE",17) == 0) { michael@0: checkcompoundcase = 1; michael@0: } michael@0: michael@0: if (strncmp(line,"NOSUGGEST",9) == 0) { michael@0: if (parse_flag(line, &nosuggest, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: if (strncmp(line,"NONGRAMSUGGEST",14) == 0) { michael@0: if (parse_flag(line, &nongramsuggest, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by forbidden words */ michael@0: if (strncmp(line,"FORBIDDENWORD",13) == 0) { michael@0: if (parse_flag(line, &forbiddenword, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by forbidden words */ michael@0: if (strncmp(line,"LEMMA_PRESENT",13) == 0) { michael@0: if (parse_flag(line, &lemma_present, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by circumfixes */ michael@0: if (strncmp(line,"CIRCUMFIX",9) == 0) { michael@0: if (parse_flag(line, &circumfix, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by fogemorphemes */ michael@0: if (strncmp(line,"ONLYINCOMPOUND",14) == 0) { michael@0: if (parse_flag(line, &onlyincompound, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by `needaffixs' */ michael@0: if (strncmp(line,"PSEUDOROOT",10) == 0) { michael@0: if (parse_flag(line, &needaffix, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by `needaffixs' */ michael@0: if (strncmp(line,"NEEDAFFIX",9) == 0) { michael@0: if (parse_flag(line, &needaffix, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the minimal length for words in compounds */ michael@0: if (strncmp(line,"COMPOUNDMIN",11) == 0) { michael@0: if (parse_num(line, &cpdmin, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: if (cpdmin < 1) cpdmin = 1; michael@0: } michael@0: michael@0: /* parse in the max. words and syllables in compounds */ michael@0: if (strncmp(line,"COMPOUNDSYLLABLE",16) == 0) { michael@0: if (parse_cpdsyllable(line, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by compound_check() method */ michael@0: if (strncmp(line,"SYLLABLENUM",11) == 0) { michael@0: if (parse_string(line, &cpdsyllablenum, afflst->getlinenum())) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by the controlled compound words */ michael@0: if (strncmp(line,"CHECKNUM",8) == 0) { michael@0: checknum=1; michael@0: } michael@0: michael@0: /* parse in the extra word characters */ michael@0: if (strncmp(line,"WORDCHARS",9) == 0) { michael@0: if (parse_array(line, &wordchars, &wordchars_utf16, &wordchars_utf16_len, utf8, afflst->getlinenum())) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the ignored characters (for example, Arabic optional diacretics charachters */ michael@0: if (strncmp(line,"IGNORE",6) == 0) { michael@0: if (parse_array(line, &ignorechars, &ignorechars_utf16, &ignorechars_utf16_len, utf8, afflst->getlinenum())) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the typical fault correcting table */ michael@0: if (strncmp(line,"REP",3) == 0) { michael@0: if (parse_reptable(line, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the input conversion table */ michael@0: if (strncmp(line,"ICONV",5) == 0) { michael@0: if (parse_convtable(line, afflst, &iconvtable, "ICONV")) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the input conversion table */ michael@0: if (strncmp(line,"OCONV",5) == 0) { michael@0: if (parse_convtable(line, afflst, &oconvtable, "OCONV")) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the phonetic translation table */ michael@0: if (strncmp(line,"PHONE",5) == 0) { michael@0: if (parse_phonetable(line, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the checkcompoundpattern table */ michael@0: if (strncmp(line,"CHECKCOMPOUNDPATTERN",20) == 0) { michael@0: if (parse_checkcpdtable(line, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the defcompound table */ michael@0: if (strncmp(line,"COMPOUNDRULE",12) == 0) { michael@0: if (parse_defcpdtable(line, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the related character map table */ michael@0: if (strncmp(line,"MAP",3) == 0) { michael@0: if (parse_maptable(line, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the word breakpoints table */ michael@0: if (strncmp(line,"BREAK",5) == 0) { michael@0: if (parse_breaktable(line, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the language for language specific codes */ michael@0: if (strncmp(line,"LANG",4) == 0) { michael@0: if (parse_string(line, &lang, afflst->getlinenum())) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: langnum = get_lang_num(lang); michael@0: } michael@0: michael@0: if (strncmp(line,"VERSION",7) == 0) { michael@0: for(line = line + 7; *line == ' ' || *line == '\t'; line++); michael@0: version = mystrdup(line); michael@0: } michael@0: michael@0: if (strncmp(line,"MAXNGRAMSUGS",12) == 0) { michael@0: if (parse_num(line, &maxngramsugs, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: if (strncmp(line,"ONLYMAXDIFF", 11) == 0) michael@0: onlymaxdiff = 1; michael@0: michael@0: if (strncmp(line,"MAXDIFF",7) == 0) { michael@0: if (parse_num(line, &maxdiff, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: if (strncmp(line,"MAXCPDSUGS",10) == 0) { michael@0: if (parse_num(line, &maxcpdsugs, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: if (strncmp(line,"NOSPLITSUGS",11) == 0) { michael@0: nosplitsugs=1; michael@0: } michael@0: michael@0: if (strncmp(line,"FULLSTRIP",9) == 0) { michael@0: fullstrip=1; michael@0: } michael@0: michael@0: if (strncmp(line,"SUGSWITHDOTS",12) == 0) { michael@0: sugswithdots=1; michael@0: } michael@0: michael@0: /* parse in the flag used by forbidden words */ michael@0: if (strncmp(line,"KEEPCASE",8) == 0) { michael@0: if (parse_flag(line, &keepcase, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by `forceucase' */ michael@0: if (strncmp(line,"FORCEUCASE",10) == 0) { michael@0: if (parse_flag(line, &forceucase, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: /* parse in the flag used by `warn' */ michael@0: if (strncmp(line,"WARN",4) == 0) { michael@0: if (parse_flag(line, &warn, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: if (strncmp(line,"FORBIDWARN",10) == 0) { michael@0: forbidwarn=1; michael@0: } michael@0: michael@0: /* parse in the flag used by the affix generator */ michael@0: if (strncmp(line,"SUBSTANDARD",11) == 0) { michael@0: if (parse_flag(line, &substandard, afflst)) { michael@0: delete afflst; michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: if (strncmp(line,"CHECKSHARPS",11) == 0) { michael@0: checksharps=1; michael@0: } michael@0: michael@0: /* parse this affix: P - prefix, S - suffix */ michael@0: ft = ' '; michael@0: if (strncmp(line,"PFX",3) == 0) ft = complexprefixes ? 'S' : 'P'; michael@0: if (strncmp(line,"SFX",3) == 0) ft = complexprefixes ? 'P' : 'S'; michael@0: if (ft != ' ') { michael@0: if (dupflags_ini) { michael@0: memset(dupflags, 0, sizeof(dupflags)); michael@0: dupflags_ini = 0; michael@0: } michael@0: if (parse_affix(line, ft, afflst, dupflags)) { michael@0: delete afflst; michael@0: process_pfx_tree_to_list(); michael@0: process_sfx_tree_to_list(); michael@0: return 1; michael@0: } michael@0: } michael@0: michael@0: } michael@0: delete afflst; michael@0: michael@0: // convert affix trees to sorted list michael@0: process_pfx_tree_to_list(); michael@0: process_sfx_tree_to_list(); michael@0: michael@0: // now we can speed up performance greatly taking advantage of the michael@0: // relationship between the affixes and the idea of "subsets". michael@0: michael@0: // View each prefix as a potential leading subset of another and view michael@0: // each suffix (reversed) as a potential trailing subset of another. michael@0: michael@0: // To illustrate this relationship if we know the prefix "ab" is found in the michael@0: // word to examine, only prefixes that "ab" is a leading subset of need be examined. michael@0: // Furthermore is "ab" is not present then none of the prefixes that "ab" is michael@0: // is a subset need be examined. michael@0: // The same argument goes for suffix string that are reversed. michael@0: michael@0: // Then to top this off why not examine the first char of the word to quickly michael@0: // limit the set of prefixes to examine (i.e. the prefixes to examine must michael@0: // be leading supersets of the first character of the word (if they exist) michael@0: michael@0: // To take advantage of this "subset" relationship, we need to add two links michael@0: // from entry. One to take next if the current prefix is found (call it nexteq) michael@0: // and one to take next if the current prefix is not found (call it nextne). michael@0: michael@0: // Since we have built ordered lists, all that remains is to properly initialize michael@0: // the nextne and nexteq pointers that relate them michael@0: michael@0: process_pfx_order(); michael@0: process_sfx_order(); michael@0: michael@0: /* get encoding for CHECKCOMPOUNDCASE */ michael@0: if (!utf8) { michael@0: char * enc = get_encoding(); michael@0: csconv = get_current_cs(enc); michael@0: free(enc); michael@0: enc = NULL; michael@0: michael@0: char expw[MAXLNLEN]; michael@0: if (wordchars) { michael@0: strcpy(expw, wordchars); michael@0: free(wordchars); michael@0: } else *expw = '\0'; michael@0: michael@0: for (int i = 0; i <= 255; i++) { michael@0: if ( (csconv[i].cupper != csconv[i].clower) && michael@0: (! strchr(expw, (char) i))) { michael@0: *(expw + strlen(expw) + 1) = '\0'; michael@0: *(expw + strlen(expw)) = (char) i; michael@0: } michael@0: } michael@0: michael@0: wordchars = mystrdup(expw); michael@0: } michael@0: michael@0: // default BREAK definition michael@0: if (numbreak == -1) { michael@0: breaktable = (char **) malloc(sizeof(char *) * 3); michael@0: if (!breaktable) return 1; michael@0: breaktable[0] = mystrdup("-"); michael@0: breaktable[1] = mystrdup("^-"); michael@0: breaktable[2] = mystrdup("-$"); michael@0: if (breaktable[0] && breaktable[1] && breaktable[2]) numbreak = 3; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: // we want to be able to quickly access prefix information michael@0: // both by prefix flag, and sorted by prefix string itself michael@0: // so we need to set up two indexes michael@0: michael@0: int AffixMgr::build_pfxtree(PfxEntry* pfxptr) michael@0: { michael@0: PfxEntry * ptr; michael@0: PfxEntry * pptr; michael@0: PfxEntry * ep = pfxptr; michael@0: michael@0: // get the right starting points michael@0: const char * key = ep->getKey(); michael@0: const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF); michael@0: michael@0: // first index by flag which must exist michael@0: ptr = pFlag[flg]; michael@0: ep->setFlgNxt(ptr); michael@0: pFlag[flg] = ep; michael@0: michael@0: michael@0: // handle the special case of null affix string michael@0: if (strlen(key) == 0) { michael@0: // always inset them at head of list at element 0 michael@0: ptr = pStart[0]; michael@0: ep->setNext(ptr); michael@0: pStart[0] = ep; michael@0: return 0; michael@0: } michael@0: michael@0: // now handle the normal case michael@0: ep->setNextEQ(NULL); michael@0: ep->setNextNE(NULL); michael@0: michael@0: unsigned char sp = *((const unsigned char *)key); michael@0: ptr = pStart[sp]; michael@0: michael@0: // handle the first insert michael@0: if (!ptr) { michael@0: pStart[sp] = ep; michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: // otherwise use binary tree insertion so that a sorted michael@0: // list can easily be generated later michael@0: pptr = NULL; michael@0: for (;;) { michael@0: pptr = ptr; michael@0: if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) { michael@0: ptr = ptr->getNextEQ(); michael@0: if (!ptr) { michael@0: pptr->setNextEQ(ep); michael@0: break; michael@0: } michael@0: } else { michael@0: ptr = ptr->getNextNE(); michael@0: if (!ptr) { michael@0: pptr->setNextNE(ep); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // we want to be able to quickly access suffix information michael@0: // both by suffix flag, and sorted by the reverse of the michael@0: // suffix string itself; so we need to set up two indexes michael@0: int AffixMgr::build_sfxtree(SfxEntry* sfxptr) michael@0: { michael@0: SfxEntry * ptr; michael@0: SfxEntry * pptr; michael@0: SfxEntry * ep = sfxptr; michael@0: michael@0: /* get the right starting point */ michael@0: const char * key = ep->getKey(); michael@0: const unsigned char flg = (unsigned char) (ep->getFlag() & 0x00FF); michael@0: michael@0: // first index by flag which must exist michael@0: ptr = sFlag[flg]; michael@0: ep->setFlgNxt(ptr); michael@0: sFlag[flg] = ep; michael@0: michael@0: // next index by affix string michael@0: michael@0: // handle the special case of null affix string michael@0: if (strlen(key) == 0) { michael@0: // always inset them at head of list at element 0 michael@0: ptr = sStart[0]; michael@0: ep->setNext(ptr); michael@0: sStart[0] = ep; michael@0: return 0; michael@0: } michael@0: michael@0: // now handle the normal case michael@0: ep->setNextEQ(NULL); michael@0: ep->setNextNE(NULL); michael@0: michael@0: unsigned char sp = *((const unsigned char *)key); michael@0: ptr = sStart[sp]; michael@0: michael@0: // handle the first insert michael@0: if (!ptr) { michael@0: sStart[sp] = ep; michael@0: return 0; michael@0: } michael@0: michael@0: // otherwise use binary tree insertion so that a sorted michael@0: // list can easily be generated later michael@0: pptr = NULL; michael@0: for (;;) { michael@0: pptr = ptr; michael@0: if (strcmp(ep->getKey(), ptr->getKey() ) <= 0) { michael@0: ptr = ptr->getNextEQ(); michael@0: if (!ptr) { michael@0: pptr->setNextEQ(ep); michael@0: break; michael@0: } michael@0: } else { michael@0: ptr = ptr->getNextNE(); michael@0: if (!ptr) { michael@0: pptr->setNextNE(ep); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // convert from binary tree to sorted list michael@0: int AffixMgr::process_pfx_tree_to_list() michael@0: { michael@0: for (int i=1; i< SETSIZE; i++) { michael@0: pStart[i] = process_pfx_in_order(pStart[i],NULL); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: PfxEntry* AffixMgr::process_pfx_in_order(PfxEntry* ptr, PfxEntry* nptr) michael@0: { michael@0: if (ptr) { michael@0: nptr = process_pfx_in_order(ptr->getNextNE(), nptr); michael@0: ptr->setNext(nptr); michael@0: nptr = process_pfx_in_order(ptr->getNextEQ(), ptr); michael@0: } michael@0: return nptr; michael@0: } michael@0: michael@0: michael@0: // convert from binary tree to sorted list michael@0: int AffixMgr:: process_sfx_tree_to_list() michael@0: { michael@0: for (int i=1; i< SETSIZE; i++) { michael@0: sStart[i] = process_sfx_in_order(sStart[i],NULL); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: SfxEntry* AffixMgr::process_sfx_in_order(SfxEntry* ptr, SfxEntry* nptr) michael@0: { michael@0: if (ptr) { michael@0: nptr = process_sfx_in_order(ptr->getNextNE(), nptr); michael@0: ptr->setNext(nptr); michael@0: nptr = process_sfx_in_order(ptr->getNextEQ(), ptr); michael@0: } michael@0: return nptr; michael@0: } michael@0: michael@0: michael@0: // reinitialize the PfxEntry links NextEQ and NextNE to speed searching michael@0: // using the idea of leading subsets this time michael@0: int AffixMgr::process_pfx_order() michael@0: { michael@0: PfxEntry* ptr; michael@0: michael@0: // loop through each prefix list starting point michael@0: for (int i=1; i < SETSIZE; i++) { michael@0: michael@0: ptr = pStart[i]; michael@0: michael@0: // look through the remainder of the list michael@0: // and find next entry with affix that michael@0: // the current one is not a subset of michael@0: // mark that as destination for NextNE michael@0: // use next in list that you are a subset michael@0: // of as NextEQ michael@0: michael@0: for (; ptr != NULL; ptr = ptr->getNext()) { michael@0: michael@0: PfxEntry * nptr = ptr->getNext(); michael@0: for (; nptr != NULL; nptr = nptr->getNext()) { michael@0: if (! isSubset( ptr->getKey() , nptr->getKey() )) break; michael@0: } michael@0: ptr->setNextNE(nptr); michael@0: ptr->setNextEQ(NULL); michael@0: if ((ptr->getNext()) && isSubset(ptr->getKey() , (ptr->getNext())->getKey())) michael@0: ptr->setNextEQ(ptr->getNext()); michael@0: } michael@0: michael@0: // now clean up by adding smart search termination strings: michael@0: // if you are already a superset of the previous prefix michael@0: // but not a subset of the next, search can end here michael@0: // so set NextNE properly michael@0: michael@0: ptr = pStart[i]; michael@0: for (; ptr != NULL; ptr = ptr->getNext()) { michael@0: PfxEntry * nptr = ptr->getNext(); michael@0: PfxEntry * mptr = NULL; michael@0: for (; nptr != NULL; nptr = nptr->getNext()) { michael@0: if (! isSubset(ptr->getKey(),nptr->getKey())) break; michael@0: mptr = nptr; michael@0: } michael@0: if (mptr) mptr->setNextNE(NULL); michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // initialize the SfxEntry links NextEQ and NextNE to speed searching michael@0: // using the idea of leading subsets this time michael@0: int AffixMgr::process_sfx_order() michael@0: { michael@0: SfxEntry* ptr; michael@0: michael@0: // loop through each prefix list starting point michael@0: for (int i=1; i < SETSIZE; i++) { michael@0: michael@0: ptr = sStart[i]; michael@0: michael@0: // look through the remainder of the list michael@0: // and find next entry with affix that michael@0: // the current one is not a subset of michael@0: // mark that as destination for NextNE michael@0: // use next in list that you are a subset michael@0: // of as NextEQ michael@0: michael@0: for (; ptr != NULL; ptr = ptr->getNext()) { michael@0: SfxEntry * nptr = ptr->getNext(); michael@0: for (; nptr != NULL; nptr = nptr->getNext()) { michael@0: if (! isSubset(ptr->getKey(),nptr->getKey())) break; michael@0: } michael@0: ptr->setNextNE(nptr); michael@0: ptr->setNextEQ(NULL); michael@0: if ((ptr->getNext()) && isSubset(ptr->getKey(),(ptr->getNext())->getKey())) michael@0: ptr->setNextEQ(ptr->getNext()); michael@0: } michael@0: michael@0: michael@0: // now clean up by adding smart search termination strings: michael@0: // if you are already a superset of the previous suffix michael@0: // but not a subset of the next, search can end here michael@0: // so set NextNE properly michael@0: michael@0: ptr = sStart[i]; michael@0: for (; ptr != NULL; ptr = ptr->getNext()) { michael@0: SfxEntry * nptr = ptr->getNext(); michael@0: SfxEntry * mptr = NULL; michael@0: for (; nptr != NULL; nptr = nptr->getNext()) { michael@0: if (! isSubset(ptr->getKey(),nptr->getKey())) break; michael@0: mptr = nptr; michael@0: } michael@0: if (mptr) mptr->setNextNE(NULL); michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // add flags to the result for dictionary debugging michael@0: void AffixMgr::debugflag(char * result, unsigned short flag) { michael@0: char * st = encode_flag(flag); michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: mystrcat(result, MORPH_FLAG, MAXLNLEN); michael@0: if (st) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: } michael@0: } michael@0: michael@0: // calculate the character length of the condition michael@0: int AffixMgr::condlen(char * st) michael@0: { michael@0: int l = 0; michael@0: bool group = false; michael@0: for(; *st; st++) { michael@0: if (*st == '[') { michael@0: group = true; michael@0: l++; michael@0: } else if (*st == ']') group = false; michael@0: else if (!group && (!utf8 || michael@0: (!(*st & 0x80) || ((*st & 0xc0) == 0x80)))) l++; michael@0: } michael@0: return l; michael@0: } michael@0: michael@0: int AffixMgr::encodeit(affentry &entry, char * cs) michael@0: { michael@0: if (strcmp(cs,".") != 0) { michael@0: entry.numconds = (char) condlen(cs); michael@0: strncpy(entry.c.conds, cs, MAXCONDLEN); michael@0: // long condition (end of conds padded by strncpy) michael@0: if (entry.c.conds[MAXCONDLEN - 1] && cs[MAXCONDLEN]) { michael@0: entry.opts += aeLONGCOND; michael@0: entry.c.l.conds2 = mystrdup(cs + MAXCONDLEN_1); michael@0: if (!entry.c.l.conds2) return 1; michael@0: } michael@0: } else { michael@0: entry.numconds = 0; michael@0: entry.c.conds[0] = '\0'; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // return 1 if s1 is a leading subset of s2 (dots are for infixes) michael@0: inline int AffixMgr::isSubset(const char * s1, const char * s2) michael@0: { michael@0: while (((*s1 == *s2) || (*s1 == '.')) && (*s1 != '\0')) { michael@0: s1++; michael@0: s2++; michael@0: } michael@0: return (*s1 == '\0'); michael@0: } michael@0: michael@0: michael@0: // check word for prefixes michael@0: struct hentry * AffixMgr::prefix_check(const char * word, int len, char in_compound, michael@0: const FLAG needflag) michael@0: { michael@0: struct hentry * rv= NULL; michael@0: michael@0: pfx = NULL; michael@0: pfxappnd = NULL; michael@0: sfxappnd = NULL; michael@0: michael@0: // first handle the special case of 0 length prefixes michael@0: PfxEntry * pe = pStart[0]; michael@0: while (pe) { michael@0: if ( michael@0: // fogemorpheme michael@0: ((in_compound != IN_CPD_NOT) || !(pe->getCont() && michael@0: (TESTAFF(pe->getCont(), onlyincompound, pe->getContLen())))) && michael@0: // permit prefixes in compounds michael@0: ((in_compound != IN_CPD_END) || (pe->getCont() && michael@0: (TESTAFF(pe->getCont(), compoundpermitflag, pe->getContLen())))) michael@0: ) { michael@0: // check prefix michael@0: rv = pe->checkword(word, len, in_compound, needflag); michael@0: if (rv) { michael@0: pfx=pe; // BUG: pfx not stateless michael@0: return rv; michael@0: } michael@0: } michael@0: pe = pe->getNext(); michael@0: } michael@0: michael@0: // now handle the general case michael@0: unsigned char sp = *((const unsigned char *)word); michael@0: PfxEntry * pptr = pStart[sp]; michael@0: michael@0: while (pptr) { michael@0: if (isSubset(pptr->getKey(),word)) { michael@0: if ( michael@0: // fogemorpheme michael@0: ((in_compound != IN_CPD_NOT) || !(pptr->getCont() && michael@0: (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen())))) && michael@0: // permit prefixes in compounds michael@0: ((in_compound != IN_CPD_END) || (pptr->getCont() && michael@0: (TESTAFF(pptr->getCont(), compoundpermitflag, pptr->getContLen())))) michael@0: ) { michael@0: // check prefix michael@0: rv = pptr->checkword(word, len, in_compound, needflag); michael@0: if (rv) { michael@0: pfx=pptr; // BUG: pfx not stateless michael@0: return rv; michael@0: } michael@0: } michael@0: pptr = pptr->getNextEQ(); michael@0: } else { michael@0: pptr = pptr->getNextNE(); michael@0: } michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: // check word for prefixes michael@0: struct hentry * AffixMgr::prefix_check_twosfx(const char * word, int len, michael@0: char in_compound, const FLAG needflag) michael@0: { michael@0: struct hentry * rv= NULL; michael@0: michael@0: pfx = NULL; michael@0: sfxappnd = NULL; michael@0: michael@0: // first handle the special case of 0 length prefixes michael@0: PfxEntry * pe = pStart[0]; michael@0: michael@0: while (pe) { michael@0: rv = pe->check_twosfx(word, len, in_compound, needflag); michael@0: if (rv) return rv; michael@0: pe = pe->getNext(); michael@0: } michael@0: michael@0: // now handle the general case michael@0: unsigned char sp = *((const unsigned char *)word); michael@0: PfxEntry * pptr = pStart[sp]; michael@0: michael@0: while (pptr) { michael@0: if (isSubset(pptr->getKey(),word)) { michael@0: rv = pptr->check_twosfx(word, len, in_compound, needflag); michael@0: if (rv) { michael@0: pfx = pptr; michael@0: return rv; michael@0: } michael@0: pptr = pptr->getNextEQ(); michael@0: } else { michael@0: pptr = pptr->getNextNE(); michael@0: } michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: // check word for prefixes michael@0: char * AffixMgr::prefix_check_morph(const char * word, int len, char in_compound, michael@0: const FLAG needflag) michael@0: { michael@0: char * st; michael@0: michael@0: char result[MAXLNLEN]; michael@0: result[0] = '\0'; michael@0: michael@0: pfx = NULL; michael@0: sfxappnd = NULL; michael@0: michael@0: // first handle the special case of 0 length prefixes michael@0: PfxEntry * pe = pStart[0]; michael@0: while (pe) { michael@0: st = pe->check_morph(word,len,in_compound, needflag); michael@0: if (st) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: } michael@0: // if (rv) return rv; michael@0: pe = pe->getNext(); michael@0: } michael@0: michael@0: // now handle the general case michael@0: unsigned char sp = *((const unsigned char *)word); michael@0: PfxEntry * pptr = pStart[sp]; michael@0: michael@0: while (pptr) { michael@0: if (isSubset(pptr->getKey(),word)) { michael@0: st = pptr->check_morph(word,len,in_compound, needflag); michael@0: if (st) { michael@0: // fogemorpheme michael@0: if ((in_compound != IN_CPD_NOT) || !((pptr->getCont() && michael@0: (TESTAFF(pptr->getCont(), onlyincompound, pptr->getContLen()))))) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: pfx = pptr; michael@0: } michael@0: free(st); michael@0: } michael@0: pptr = pptr->getNextEQ(); michael@0: } else { michael@0: pptr = pptr->getNextNE(); michael@0: } michael@0: } michael@0: michael@0: if (*result) return mystrdup(result); michael@0: return NULL; michael@0: } michael@0: michael@0: michael@0: // check word for prefixes michael@0: char * AffixMgr::prefix_check_twosfx_morph(const char * word, int len, michael@0: char in_compound, const FLAG needflag) michael@0: { michael@0: char * st; michael@0: michael@0: char result[MAXLNLEN]; michael@0: result[0] = '\0'; michael@0: michael@0: pfx = NULL; michael@0: sfxappnd = NULL; michael@0: michael@0: // first handle the special case of 0 length prefixes michael@0: PfxEntry * pe = pStart[0]; michael@0: while (pe) { michael@0: st = pe->check_twosfx_morph(word,len,in_compound, needflag); michael@0: if (st) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: } michael@0: pe = pe->getNext(); michael@0: } michael@0: michael@0: // now handle the general case michael@0: unsigned char sp = *((const unsigned char *)word); michael@0: PfxEntry * pptr = pStart[sp]; michael@0: michael@0: while (pptr) { michael@0: if (isSubset(pptr->getKey(),word)) { michael@0: st = pptr->check_twosfx_morph(word, len, in_compound, needflag); michael@0: if (st) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: pfx = pptr; michael@0: } michael@0: pptr = pptr->getNextEQ(); michael@0: } else { michael@0: pptr = pptr->getNextNE(); michael@0: } michael@0: } michael@0: michael@0: if (*result) return mystrdup(result); michael@0: return NULL; michael@0: } michael@0: michael@0: // Is word a non compound with a REP substitution (see checkcompoundrep)? michael@0: int AffixMgr::cpdrep_check(const char * word, int wl) michael@0: { michael@0: char candidate[MAXLNLEN]; michael@0: const char * r; michael@0: int lenr, lenp; michael@0: michael@0: if ((wl < 2) || !numrep) return 0; michael@0: michael@0: for (int i=0; i < numrep; i++ ) { michael@0: r = word; michael@0: lenr = strlen(reptable[i].pattern2); michael@0: lenp = strlen(reptable[i].pattern); michael@0: // search every occurence of the pattern in the word michael@0: while ((r=strstr(r, reptable[i].pattern)) != NULL) { michael@0: strcpy(candidate, word); michael@0: if (r-word + lenr + strlen(r+lenp) >= MAXLNLEN) break; michael@0: strcpy(candidate+(r-word),reptable[i].pattern2); michael@0: strcpy(candidate+(r-word)+lenr, r+lenp); michael@0: if (candidate_check(candidate,strlen(candidate))) return 1; michael@0: r++; // search for the next letter michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // forbid compoundings when there are special patterns at word bound michael@0: int AffixMgr::cpdpat_check(const char * word, int pos, hentry * r1, hentry * r2, const char affixed) michael@0: { michael@0: int len; michael@0: for (int i = 0; i < numcheckcpd; i++) { michael@0: if (isSubset(checkcpdtable[i].pattern2, word + pos) && michael@0: (!r1 || !checkcpdtable[i].cond || michael@0: (r1->astr && TESTAFF(r1->astr, checkcpdtable[i].cond, r1->alen))) && michael@0: (!r2 || !checkcpdtable[i].cond2 || michael@0: (r2->astr && TESTAFF(r2->astr, checkcpdtable[i].cond2, r2->alen))) && michael@0: // zero length pattern => only TESTAFF michael@0: // zero pattern (0/flag) => unmodified stem (zero affixes allowed) michael@0: (!*(checkcpdtable[i].pattern) || ( michael@0: (*(checkcpdtable[i].pattern)=='0' && r1->blen <= pos && strncmp(word + pos - r1->blen, r1->word, r1->blen) == 0) || michael@0: (*(checkcpdtable[i].pattern)!='0' && (len = strlen(checkcpdtable[i].pattern)) && michael@0: strncmp(word + pos - len, checkcpdtable[i].pattern, len) == 0)))) { michael@0: return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // forbid compounding with neighbouring upper and lower case characters at word bounds michael@0: int AffixMgr::cpdcase_check(const char * word, int pos) michael@0: { michael@0: if (utf8) { michael@0: w_char u, w; michael@0: const char * p; michael@0: u8_u16(&u, 1, word + pos); michael@0: for (p = word + pos - 1; (*p & 0xc0) == 0x80; p--); michael@0: u8_u16(&w, 1, p); michael@0: unsigned short a = (u.h << 8) + u.l; michael@0: unsigned short b = (w.h << 8) + w.l; michael@0: if (((unicodetoupper(a, langnum) == a) || (unicodetoupper(b, langnum) == b)) && michael@0: (a != '-') && (b != '-')) return 1; michael@0: } else { michael@0: unsigned char a = *(word + pos - 1); michael@0: unsigned char b = *(word + pos); michael@0: if ((csconv[a].ccase || csconv[b].ccase) && (a != '-') && (b != '-')) return 1; michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: // check compound patterns michael@0: int AffixMgr::defcpd_check(hentry *** words, short wnum, hentry * rv, hentry ** def, char all) michael@0: { michael@0: signed short btpp[MAXWORDLEN]; // metacharacter (*, ?) positions for backtracking michael@0: signed short btwp[MAXWORDLEN]; // word positions for metacharacters michael@0: int btnum[MAXWORDLEN]; // number of matched characters in metacharacter positions michael@0: short bt = 0; michael@0: int i, j; michael@0: int ok; michael@0: int w = 0; michael@0: michael@0: if (!*words) { michael@0: w = 1; michael@0: *words = def; michael@0: } michael@0: michael@0: if (!*words) { michael@0: return 0; michael@0: } michael@0: michael@0: (*words)[wnum] = rv; michael@0: michael@0: // has the last word COMPOUNDRULE flag? michael@0: if (rv->alen == 0) { michael@0: (*words)[wnum] = NULL; michael@0: if (w) *words = NULL; michael@0: return 0; michael@0: } michael@0: ok = 0; michael@0: for (i = 0; i < numdefcpd; i++) { michael@0: for (j = 0; j < defcpdtable[i].len; j++) { michael@0: if (defcpdtable[i].def[j] != '*' && defcpdtable[i].def[j] != '?' && michael@0: TESTAFF(rv->astr, defcpdtable[i].def[j], rv->alen)) ok = 1; michael@0: } michael@0: } michael@0: if (ok == 0) { michael@0: (*words)[wnum] = NULL; michael@0: if (w) *words = NULL; michael@0: return 0; michael@0: } michael@0: michael@0: for (i = 0; i < numdefcpd; i++) { michael@0: signed short pp = 0; // pattern position michael@0: signed short wp = 0; // "words" position michael@0: int ok2; michael@0: ok = 1; michael@0: ok2 = 1; michael@0: do { michael@0: while ((pp < defcpdtable[i].len) && (wp <= wnum)) { michael@0: if (((pp+1) < defcpdtable[i].len) && michael@0: ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) { michael@0: int wend = (defcpdtable[i].def[pp+1] == '?') ? wp : wnum; michael@0: ok2 = 1; michael@0: pp+=2; michael@0: btpp[bt] = pp; michael@0: btwp[bt] = wp; michael@0: while (wp <= wend) { michael@0: if (!(*words)[wp]->alen || michael@0: !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp-2], (*words)[wp]->alen)) { michael@0: ok2 = 0; michael@0: break; michael@0: } michael@0: wp++; michael@0: } michael@0: if (wp <= wnum) ok2 = 0; michael@0: btnum[bt] = wp - btwp[bt]; michael@0: if (btnum[bt] > 0) bt++; michael@0: if (ok2) break; michael@0: } else { michael@0: ok2 = 1; michael@0: if (!(*words)[wp] || !(*words)[wp]->alen || michael@0: !TESTAFF((*words)[wp]->astr, defcpdtable[i].def[pp], (*words)[wp]->alen)) { michael@0: ok = 0; michael@0: break; michael@0: } michael@0: pp++; michael@0: wp++; michael@0: if ((defcpdtable[i].len == pp) && !(wp > wnum)) ok = 0; michael@0: } michael@0: } michael@0: if (ok && ok2) { michael@0: int r = pp; michael@0: while ((defcpdtable[i].len > r) && ((r+1) < defcpdtable[i].len) && michael@0: ((defcpdtable[i].def[r+1] == '*') || (defcpdtable[i].def[r+1] == '?'))) r+=2; michael@0: if (defcpdtable[i].len <= r) return 1; michael@0: } michael@0: // backtrack michael@0: if (bt) do { michael@0: ok = 1; michael@0: btnum[bt - 1]--; michael@0: pp = btpp[bt - 1]; michael@0: wp = btwp[bt - 1] + (signed short) btnum[bt - 1]; michael@0: } while ((btnum[bt - 1] < 0) && --bt); michael@0: } while (bt); michael@0: michael@0: if (ok && ok2 && (!all || (defcpdtable[i].len <= pp))) return 1; michael@0: michael@0: // check zero ending michael@0: while (ok && ok2 && (defcpdtable[i].len > pp) && ((pp+1) < defcpdtable[i].len) && michael@0: ((defcpdtable[i].def[pp+1] == '*') || (defcpdtable[i].def[pp+1] == '?'))) pp+=2; michael@0: if (ok && ok2 && (defcpdtable[i].len <= pp)) return 1; michael@0: } michael@0: (*words)[wnum] = NULL; michael@0: if (w) *words = NULL; michael@0: return 0; michael@0: } michael@0: michael@0: inline int AffixMgr::candidate_check(const char * word, int len) michael@0: { michael@0: struct hentry * rv=NULL; michael@0: michael@0: rv = lookup(word); michael@0: if (rv) return 1; michael@0: michael@0: // rv = prefix_check(word,len,1); michael@0: // if (rv) return 1; michael@0: michael@0: rv = affix_check(word,len); michael@0: if (rv) return 1; michael@0: return 0; michael@0: } michael@0: michael@0: // calculate number of syllable for compound-checking michael@0: short AffixMgr::get_syllable(const char * word, int wlen) michael@0: { michael@0: if (cpdmaxsyllable==0) return 0; michael@0: michael@0: short num=0; michael@0: michael@0: if (!utf8) { michael@0: for (int i=0; i 0; i--) { michael@0: if (flag_bsearch((unsigned short *) cpdvowels_utf16, michael@0: ((unsigned short *) w)[i - 1], cpdvowels_utf16_len)) num++; michael@0: } michael@0: } michael@0: return num; michael@0: } michael@0: michael@0: void AffixMgr::setcminmax(int * cmin, int * cmax, const char * word, int len) { michael@0: if (utf8) { michael@0: int i; michael@0: for (*cmin = 0, i = 0; (i < cpdmin) && word[*cmin]; i++) { michael@0: for ((*cmin)++; (word[*cmin] & 0xc0) == 0x80; (*cmin)++); michael@0: } michael@0: for (*cmax = len, i = 0; (i < (cpdmin - 1)) && *cmax; i++) { michael@0: for ((*cmax)--; (word[*cmax] & 0xc0) == 0x80; (*cmax)--); michael@0: } michael@0: } else { michael@0: *cmin = cpdmin; michael@0: *cmax = len - cpdmin + 1; michael@0: } michael@0: } michael@0: michael@0: michael@0: // check if compound word is correctly spelled michael@0: // hu_mov_rule = spec. Hungarian rule (XXX) michael@0: struct hentry * AffixMgr::compound_check(const char * word, int len, michael@0: short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words = NULL, michael@0: char hu_mov_rule = 0, char is_sug = 0, int * info = NULL) michael@0: { michael@0: int i; michael@0: short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2; michael@0: struct hentry * rv = NULL; michael@0: struct hentry * rv_first; michael@0: struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking michael@0: char st [MAXWORDUTF8LEN + 4]; michael@0: char ch = '\0'; michael@0: int cmin; michael@0: int cmax; michael@0: int striple = 0; michael@0: int scpd = 0; michael@0: int soldi = 0; michael@0: int oldcmin = 0; michael@0: int oldcmax = 0; michael@0: int oldlen = 0; michael@0: int checkedstriple = 0; michael@0: int onlycpdrule; michael@0: int affixed = 0; michael@0: hentry ** oldwords = words; michael@0: michael@0: int checked_prefix; michael@0: michael@0: setcminmax(&cmin, &cmax, word, len); michael@0: michael@0: strcpy(st, word); michael@0: michael@0: for (i = cmin; i < cmax; i++) { michael@0: // go to end of the UTF-8 character michael@0: if (utf8) { michael@0: for (; (st[i] & 0xc0) == 0x80; i++); michael@0: if (i >= cmax) return NULL; michael@0: } michael@0: michael@0: words = oldwords; michael@0: onlycpdrule = (words) ? 1 : 0; michael@0: michael@0: do { // onlycpdrule loop michael@0: michael@0: oldnumsyllable = numsyllable; michael@0: oldwordnum = wordnum; michael@0: checked_prefix = 0; michael@0: michael@0: michael@0: do { // simplified checkcompoundpattern loop michael@0: michael@0: if (scpd > 0) { michael@0: for (; scpd <= numcheckcpd && (!checkcpdtable[scpd-1].pattern3 || michael@0: strncmp(word + i, checkcpdtable[scpd-1].pattern3, strlen(checkcpdtable[scpd-1].pattern3)) != 0); scpd++); michael@0: michael@0: if (scpd > numcheckcpd) break; // break simplified checkcompoundpattern loop michael@0: strcpy(st + i, checkcpdtable[scpd-1].pattern); michael@0: soldi = i; michael@0: i += strlen(checkcpdtable[scpd-1].pattern); michael@0: strcpy(st + i, checkcpdtable[scpd-1].pattern2); michael@0: strcpy(st + i + strlen(checkcpdtable[scpd-1].pattern2), word + soldi + strlen(checkcpdtable[scpd-1].pattern3)); michael@0: michael@0: oldlen = len; michael@0: len += strlen(checkcpdtable[scpd-1].pattern) + strlen(checkcpdtable[scpd-1].pattern2) - strlen(checkcpdtable[scpd-1].pattern3); michael@0: oldcmin = cmin; michael@0: oldcmax = cmax; michael@0: setcminmax(&cmin, &cmax, st, len); michael@0: michael@0: cmax = len - cpdmin + 1; michael@0: } michael@0: michael@0: ch = st[i]; michael@0: st[i] = '\0'; michael@0: michael@0: sfx = NULL; michael@0: pfx = NULL; michael@0: michael@0: // FIRST WORD michael@0: michael@0: affixed = 1; michael@0: rv = lookup(st); // perhaps without prefix michael@0: michael@0: // search homonym with compound flag michael@0: while ((rv) && !hu_mov_rule && michael@0: ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || michael@0: !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) || michael@0: (compoundbegin && !wordnum && !onlycpdrule && michael@0: TESTAFF(rv->astr, compoundbegin, rv->alen)) || michael@0: (compoundmiddle && wordnum && !words && !onlycpdrule && michael@0: TESTAFF(rv->astr, compoundmiddle, rv->alen)) || michael@0: (numdefcpd && onlycpdrule && michael@0: ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) || michael@0: (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0))))) || michael@0: (scpd != 0 && checkcpdtable[scpd-1].cond != FLAG_NULL && michael@0: !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen))) michael@0: ) { michael@0: rv = rv->next_homonym; michael@0: } michael@0: michael@0: if (rv) affixed = 0; michael@0: michael@0: if (!rv) { michael@0: if (onlycpdrule) break; michael@0: if (compoundflag && michael@0: !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) { michael@0: if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, michael@0: FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule && michael@0: sfx->getCont() && michael@0: ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag, michael@0: sfx->getContLen())) || (compoundend && michael@0: TESTAFF(sfx->getCont(), compoundend, michael@0: sfx->getContLen())))) { michael@0: rv = NULL; michael@0: } michael@0: } michael@0: michael@0: if (rv || michael@0: (((wordnum == 0) && compoundbegin && michael@0: ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || michael@0: (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) || michael@0: ((wordnum > 0) && compoundmiddle && michael@0: ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || michael@0: (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle))))) michael@0: ) checked_prefix = 1; michael@0: // else check forbiddenwords and needaffix michael@0: } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) || michael@0: TESTAFF(rv->astr, needaffix, rv->alen) || michael@0: TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || michael@0: (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)) michael@0: )) { michael@0: st[i] = ch; michael@0: //continue; michael@0: break; michael@0: } michael@0: michael@0: // check non_compound flag in suffix and prefix michael@0: if ((rv) && !hu_mov_rule && michael@0: ((pfx && pfx->getCont() && michael@0: TESTAFF(pfx->getCont(), compoundforbidflag, michael@0: pfx->getContLen())) || michael@0: (sfx && sfx->getCont() && michael@0: TESTAFF(sfx->getCont(), compoundforbidflag, michael@0: sfx->getContLen())))) { michael@0: rv = NULL; michael@0: } michael@0: michael@0: // check compoundend flag in suffix and prefix michael@0: if ((rv) && !checked_prefix && compoundend && !hu_mov_rule && michael@0: ((pfx && pfx->getCont() && michael@0: TESTAFF(pfx->getCont(), compoundend, michael@0: pfx->getContLen())) || michael@0: (sfx && sfx->getCont() && michael@0: TESTAFF(sfx->getCont(), compoundend, michael@0: sfx->getContLen())))) { michael@0: rv = NULL; michael@0: } michael@0: michael@0: // check compoundmiddle flag in suffix and prefix michael@0: if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule && michael@0: ((pfx && pfx->getCont() && michael@0: TESTAFF(pfx->getCont(), compoundmiddle, michael@0: pfx->getContLen())) || michael@0: (sfx && sfx->getCont() && michael@0: TESTAFF(sfx->getCont(), compoundmiddle, michael@0: sfx->getContLen())))) { michael@0: rv = NULL; michael@0: } michael@0: michael@0: // check forbiddenwords michael@0: if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || michael@0: TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || michael@0: (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) { michael@0: return NULL; michael@0: } michael@0: michael@0: // increment word number, if the second root has a compoundroot flag michael@0: if ((rv) && compoundroot && michael@0: (TESTAFF(rv->astr, compoundroot, rv->alen))) { michael@0: wordnum++; michael@0: } michael@0: michael@0: // first word is acceptable in compound words? michael@0: if (((rv) && michael@0: ( checked_prefix || (words && words[wnum]) || michael@0: (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || michael@0: ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) || michael@0: ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen))// || michael@0: // (numdefcpd && ) michael@0: michael@0: // LANG_hu section: spec. Hungarian rule michael@0: || ((langnum == LANG_hu) && hu_mov_rule && ( michael@0: TESTAFF(rv->astr, 'F', rv->alen) || // XXX hardwired Hungarian dictionary codes michael@0: TESTAFF(rv->astr, 'G', rv->alen) || michael@0: TESTAFF(rv->astr, 'H', rv->alen) michael@0: ) michael@0: ) michael@0: // END of LANG_hu section michael@0: ) && michael@0: ( michael@0: // test CHECKCOMPOUNDPATTERN conditions michael@0: scpd == 0 || checkcpdtable[scpd-1].cond == FLAG_NULL || michael@0: TESTAFF(rv->astr, checkcpdtable[scpd-1].cond, rv->alen) michael@0: ) michael@0: && ! (( checkcompoundtriple && scpd == 0 && !words && // test triple letters michael@0: (word[i-1]==word[i]) && ( michael@0: ((i>1) && (word[i-1]==word[i-2])) || michael@0: ((word[i-1]==word[i+1])) // may be word[i+1] == '\0' michael@0: ) michael@0: ) || michael@0: ( michael@0: checkcompoundcase && scpd == 0 && !words && cpdcase_check(word, i) michael@0: )) michael@0: ) michael@0: // LANG_hu section: spec. Hungarian rule michael@0: || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) && michael@0: (sfx && sfx->getCont() && ( // XXX hardwired Hungarian dic. codes michael@0: TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) || michael@0: TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen()) michael@0: ) michael@0: ) michael@0: ) michael@0: ) { // first word is ok condition michael@0: michael@0: // LANG_hu section: spec. Hungarian rule michael@0: if (langnum == LANG_hu) { michael@0: // calculate syllable number of the word michael@0: numsyllable += get_syllable(st, i); michael@0: // + 1 word, if syllable number of the prefix > 1 (hungarian convention) michael@0: if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; michael@0: } michael@0: // END of LANG_hu section michael@0: michael@0: // NEXT WORD(S) michael@0: rv_first = rv; michael@0: st[i] = ch; michael@0: michael@0: do { // striple loop michael@0: michael@0: // check simplifiedtriple michael@0: if (simplifiedtriple) { michael@0: if (striple) { michael@0: checkedstriple = 1; michael@0: i--; // check "fahrt" instead of "ahrt" in "Schiffahrt" michael@0: } else if (i > 2 && *(word+i - 1) == *(word + i - 2)) striple = 1; michael@0: } michael@0: michael@0: rv = lookup((st+i)); // perhaps without prefix michael@0: michael@0: // search homonym with compound flag michael@0: while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || michael@0: !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) || michael@0: (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) || michael@0: (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))) || michael@0: (scpd != 0 && checkcpdtable[scpd-1].cond2 != FLAG_NULL && michael@0: !TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen)) michael@0: )) { michael@0: rv = rv->next_homonym; michael@0: } michael@0: michael@0: // check FORCEUCASE michael@0: if (rv && forceucase && (rv) && michael@0: (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL; michael@0: michael@0: if (rv && words && words[wnum + 1]) return rv_first; michael@0: michael@0: oldnumsyllable2 = numsyllable; michael@0: oldwordnum2 = wordnum; michael@0: michael@0: michael@0: // LANG_hu section: spec. Hungarian rule, XXX hardwired dictionary code michael@0: if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) { michael@0: numsyllable--; michael@0: } michael@0: // END of LANG_hu section michael@0: michael@0: // increment word number, if the second root has a compoundroot flag michael@0: if ((rv) && (compoundroot) && michael@0: (TESTAFF(rv->astr, compoundroot, rv->alen))) { michael@0: wordnum++; michael@0: } michael@0: michael@0: // check forbiddenwords michael@0: if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || michael@0: TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || michael@0: (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL; michael@0: michael@0: // second word is acceptable, as a root? michael@0: // hungarian conventions: compounding is acceptable, michael@0: // when compound forms consist of 2 words, or if more, michael@0: // then the syllable number of root words must be 6, or lesser. michael@0: michael@0: if ((rv) && ( michael@0: (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || michael@0: (compoundend && TESTAFF(rv->astr, compoundend, rv->alen)) michael@0: ) michael@0: && ( michael@0: ((cpdwordmax==-1) || (wordnum+1clen)<=cpdmaxsyllable)) michael@0: ) && michael@0: ( michael@0: // test CHECKCOMPOUNDPATTERN michael@0: !numcheckcpd || scpd != 0 || !cpdpat_check(word, i, rv_first, rv, 0) michael@0: ) && michael@0: ( michael@0: (!checkcompounddup || (rv != rv_first)) michael@0: ) michael@0: // test CHECKCOMPOUNDPATTERN conditions michael@0: && (scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL || michael@0: TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen)) michael@0: ) michael@0: { michael@0: // forbid compound word, if it is a non compound word with typical fault michael@0: if (checkcompoundrep && cpdrep_check(word,len)) return NULL; michael@0: return rv_first; michael@0: } michael@0: michael@0: numsyllable = oldnumsyllable2; michael@0: wordnum = oldwordnum2; michael@0: michael@0: // perhaps second word has prefix or/and suffix michael@0: sfx = NULL; michael@0: sfxflag = FLAG_NULL; michael@0: rv = (compoundflag && !onlycpdrule) ? affix_check((word+i),strlen(word+i), compoundflag, IN_CPD_END) : NULL; michael@0: if (!rv && compoundend && !onlycpdrule) { michael@0: sfx = NULL; michael@0: pfx = NULL; michael@0: rv = affix_check((word+i),strlen(word+i), compoundend, IN_CPD_END); michael@0: } michael@0: michael@0: if (!rv && numdefcpd && words) { michael@0: rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END); michael@0: if (rv && defcpd_check(&words, wnum + 1, rv, NULL, 1)) return rv_first; michael@0: rv = NULL; michael@0: } michael@0: michael@0: // test CHECKCOMPOUNDPATTERN conditions (allowed forms) michael@0: if (rv && !(scpd == 0 || checkcpdtable[scpd-1].cond2 == FLAG_NULL || michael@0: TESTAFF(rv->astr, checkcpdtable[scpd-1].cond2, rv->alen))) rv = NULL; michael@0: michael@0: // test CHECKCOMPOUNDPATTERN conditions (forbidden compounds) michael@0: if (rv && numcheckcpd && scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) rv = NULL; michael@0: michael@0: // check non_compound flag in suffix and prefix michael@0: if ((rv) && michael@0: ((pfx && pfx->getCont() && michael@0: TESTAFF(pfx->getCont(), compoundforbidflag, michael@0: pfx->getContLen())) || michael@0: (sfx && sfx->getCont() && michael@0: TESTAFF(sfx->getCont(), compoundforbidflag, michael@0: sfx->getContLen())))) { michael@0: rv = NULL; michael@0: } michael@0: michael@0: // check FORCEUCASE michael@0: if (rv && forceucase && (rv) && michael@0: (TESTAFF(rv->astr, forceucase, rv->alen)) && !(info && *info & SPELL_ORIGCAP)) rv = NULL; michael@0: michael@0: // check forbiddenwords michael@0: if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || michael@0: TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || michael@0: (is_sug && nosuggest && TESTAFF(rv->astr, nosuggest, rv->alen)))) return NULL; michael@0: michael@0: // pfxappnd = prefix of word+i, or NULL michael@0: // calculate syllable number of prefix. michael@0: // hungarian convention: when syllable number of prefix is more, michael@0: // than 1, the prefix+word counts as two words. michael@0: michael@0: if (langnum == LANG_hu) { michael@0: // calculate syllable number of the word michael@0: numsyllable += get_syllable(word + i, strlen(word + i)); michael@0: michael@0: // - affix syllable num. michael@0: // XXX only second suffix (inflections, not derivations) michael@0: if (sfxappnd) { michael@0: char * tmp = myrevstrdup(sfxappnd); michael@0: numsyllable -= get_syllable(tmp, strlen(tmp)); michael@0: free(tmp); michael@0: } michael@0: michael@0: // + 1 word, if syllable number of the prefix > 1 (hungarian convention) michael@0: if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; michael@0: michael@0: // increment syllable num, if last word has a SYLLABLENUM flag michael@0: // and the suffix is beginning `s' michael@0: michael@0: if (cpdsyllablenum) { michael@0: switch (sfxflag) { michael@0: case 'c': { numsyllable+=2; break; } michael@0: case 'J': { numsyllable += 1; break; } michael@0: case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // increment word number, if the second word has a compoundroot flag michael@0: if ((rv) && (compoundroot) && michael@0: (TESTAFF(rv->astr, compoundroot, rv->alen))) { michael@0: wordnum++; michael@0: } michael@0: michael@0: // second word is acceptable, as a word with prefix or/and suffix? michael@0: // hungarian conventions: compounding is acceptable, michael@0: // when compound forms consist 2 word, otherwise michael@0: // the syllable number of root words is 6, or lesser. michael@0: if ((rv) && michael@0: ( michael@0: ((cpdwordmax == -1) || (wordnum + 1 < cpdwordmax)) || michael@0: ((cpdmaxsyllable != 0) && michael@0: (numsyllable <= cpdmaxsyllable)) michael@0: ) michael@0: && ( michael@0: (!checkcompounddup || (rv != rv_first)) michael@0: )) { michael@0: // forbid compound word, if it is a non compound word with typical fault michael@0: if (checkcompoundrep && cpdrep_check(word, len)) return NULL; michael@0: return rv_first; michael@0: } michael@0: michael@0: numsyllable = oldnumsyllable2; michael@0: wordnum = oldwordnum2; michael@0: michael@0: // perhaps second word is a compound word (recursive call) michael@0: if (wordnum < maxwordnum) { michael@0: rv = compound_check((st+i),strlen(st+i), wordnum+1, michael@0: numsyllable, maxwordnum, wnum + 1, words, 0, is_sug, info); michael@0: michael@0: if (rv && numcheckcpd && ((scpd == 0 && cpdpat_check(word, i, rv_first, rv, affixed)) || michael@0: (scpd != 0 && !cpdpat_check(word, i, rv_first, rv, affixed)))) rv = NULL; michael@0: } else { michael@0: rv=NULL; michael@0: } michael@0: if (rv) { michael@0: // forbid compound word, if it is a non compound word with typical fault michael@0: if (checkcompoundrep || forbiddenword) { michael@0: struct hentry * rv2 = NULL; michael@0: michael@0: if (checkcompoundrep && cpdrep_check(word, len)) return NULL; michael@0: michael@0: // check first part michael@0: if (strncmp(rv->word, word + i, rv->blen) == 0) { michael@0: char r = *(st + i + rv->blen); michael@0: *(st + i + rv->blen) = '\0'; michael@0: michael@0: if (checkcompoundrep && cpdrep_check(st, i + rv->blen)) { michael@0: *(st + i + rv->blen) = r; michael@0: continue; michael@0: } michael@0: michael@0: if (forbiddenword) { michael@0: rv2 = lookup(word); michael@0: if (!rv2) rv2 = affix_check(word, len); michael@0: if (rv2 && rv2->astr && TESTAFF(rv2->astr, forbiddenword, rv2->alen) && michael@0: (strncmp(rv2->word, st, i + rv->blen) == 0)) { michael@0: return NULL; michael@0: } michael@0: } michael@0: *(st + i + rv->blen) = r; michael@0: } michael@0: } michael@0: return rv_first; michael@0: } michael@0: } while (striple && !checkedstriple); // end of striple loop michael@0: michael@0: if (checkedstriple) { michael@0: i++; michael@0: checkedstriple = 0; michael@0: striple = 0; michael@0: } michael@0: michael@0: } // first word is ok condition michael@0: michael@0: if (soldi != 0) { michael@0: i = soldi; michael@0: soldi = 0; michael@0: len = oldlen; michael@0: cmin = oldcmin; michael@0: cmax = oldcmax; michael@0: } michael@0: scpd++; michael@0: michael@0: michael@0: } while (!onlycpdrule && simplifiedcpd && scpd <= numcheckcpd); // end of simplifiedcpd loop michael@0: michael@0: scpd = 0; michael@0: wordnum = oldwordnum; michael@0: numsyllable = oldnumsyllable; michael@0: michael@0: if (soldi != 0) { michael@0: i = soldi; michael@0: strcpy(st, word); // XXX add more optim. michael@0: soldi = 0; michael@0: } else st[i] = ch; michael@0: michael@0: } while (numdefcpd && oldwordnum == 0 && !onlycpdrule && (onlycpdrule = 1)); // end of onlycpd loop michael@0: michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: // check if compound word is correctly spelled michael@0: // hu_mov_rule = spec. Hungarian rule (XXX) michael@0: int AffixMgr::compound_check_morph(const char * word, int len, michael@0: short wordnum, short numsyllable, short maxwordnum, short wnum, hentry ** words, michael@0: char hu_mov_rule = 0, char ** result = NULL, char * partresult = NULL) michael@0: { michael@0: int i; michael@0: short oldnumsyllable, oldnumsyllable2, oldwordnum, oldwordnum2; michael@0: int ok = 0; michael@0: michael@0: struct hentry * rv = NULL; michael@0: struct hentry * rv_first; michael@0: struct hentry * rwords[MAXWORDLEN]; // buffer for COMPOUND pattern checking michael@0: char st [MAXWORDUTF8LEN + 4]; michael@0: char ch; michael@0: michael@0: int checked_prefix; michael@0: char presult[MAXLNLEN]; michael@0: michael@0: int cmin; michael@0: int cmax; michael@0: michael@0: int onlycpdrule; michael@0: int affixed = 0; michael@0: hentry ** oldwords = words; michael@0: michael@0: setcminmax(&cmin, &cmax, word, len); michael@0: michael@0: strcpy(st, word); michael@0: michael@0: for (i = cmin; i < cmax; i++) { michael@0: oldnumsyllable = numsyllable; michael@0: oldwordnum = wordnum; michael@0: checked_prefix = 0; michael@0: michael@0: // go to end of the UTF-8 character michael@0: if (utf8) { michael@0: for (; (st[i] & 0xc0) == 0x80; i++); michael@0: if (i >= cmax) return 0; michael@0: } michael@0: michael@0: words = oldwords; michael@0: onlycpdrule = (words) ? 1 : 0; michael@0: michael@0: do { // onlycpdrule loop michael@0: michael@0: oldnumsyllable = numsyllable; michael@0: oldwordnum = wordnum; michael@0: checked_prefix = 0; michael@0: michael@0: ch = st[i]; michael@0: st[i] = '\0'; michael@0: sfx = NULL; michael@0: michael@0: // FIRST WORD michael@0: michael@0: affixed = 1; michael@0: michael@0: *presult = '\0'; michael@0: if (partresult) mystrcat(presult, partresult, MAXLNLEN); michael@0: michael@0: rv = lookup(st); // perhaps without prefix michael@0: michael@0: // search homonym with compound flag michael@0: while ((rv) && !hu_mov_rule && michael@0: ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || michael@0: !((compoundflag && !words && !onlycpdrule && TESTAFF(rv->astr, compoundflag, rv->alen)) || michael@0: (compoundbegin && !wordnum && !onlycpdrule && michael@0: TESTAFF(rv->astr, compoundbegin, rv->alen)) || michael@0: (compoundmiddle && wordnum && !words && !onlycpdrule && michael@0: TESTAFF(rv->astr, compoundmiddle, rv->alen)) || michael@0: (numdefcpd && onlycpdrule && michael@0: ((!words && !wordnum && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)) || michael@0: (words && defcpd_check(&words, wnum, rv, (hentry **) &rwords, 0)))) michael@0: ))) { michael@0: rv = rv->next_homonym; michael@0: } michael@0: michael@0: if (rv) affixed = 0; michael@0: michael@0: if (rv) { michael@0: sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_PART, st); michael@0: if (!HENTRY_FIND(rv, MORPH_STEM)) { michael@0: sprintf(presult + strlen(presult), "%c%s%s", MSEP_FLD, MORPH_STEM, st); michael@0: } michael@0: // store the pointer of the hash entry michael@0: // sprintf(presult + strlen(presult), "%c%s%p", MSEP_FLD, MORPH_HENTRY, rv); michael@0: if (HENTRY_DATA(rv)) { michael@0: sprintf(presult + strlen(presult), "%c%s", MSEP_FLD, HENTRY_DATA2(rv)); michael@0: } michael@0: } michael@0: michael@0: if (!rv) { michael@0: if (onlycpdrule) break; michael@0: if (compoundflag && michael@0: !(rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundflag))) { michael@0: if ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, michael@0: FLAG_NULL, compoundflag, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) && !hu_mov_rule && michael@0: sfx->getCont() && michael@0: ((compoundforbidflag && TESTAFF(sfx->getCont(), compoundforbidflag, michael@0: sfx->getContLen())) || (compoundend && michael@0: TESTAFF(sfx->getCont(), compoundend, michael@0: sfx->getContLen())))) { michael@0: rv = NULL; michael@0: } michael@0: } michael@0: michael@0: if (rv || michael@0: (((wordnum == 0) && compoundbegin && michael@0: ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundbegin, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || michael@0: (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundbegin)))) || michael@0: ((wordnum > 0) && compoundmiddle && michael@0: ((rv = suffix_check(st, i, 0, NULL, NULL, 0, NULL, FLAG_NULL, compoundmiddle, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN)) || michael@0: (rv = prefix_check(st, i, hu_mov_rule ? IN_CPD_OTHER : IN_CPD_BEGIN, compoundmiddle))))) michael@0: ) { michael@0: // char * p = prefix_check_morph(st, i, 0, compound); michael@0: char * p = NULL; michael@0: if (compoundflag) p = affix_check_morph(st, i, compoundflag); michael@0: if (!p || (*p == '\0')) { michael@0: if (p) free(p); michael@0: p = NULL; michael@0: if ((wordnum == 0) && compoundbegin) { michael@0: p = affix_check_morph(st, i, compoundbegin); michael@0: } else if ((wordnum > 0) && compoundmiddle) { michael@0: p = affix_check_morph(st, i, compoundmiddle); michael@0: } michael@0: } michael@0: if (p && (*p != '\0')) { michael@0: sprintf(presult + strlen(presult), "%c%s%s%s", MSEP_FLD, michael@0: MORPH_PART, st, line_uniq_app(&p, MSEP_REC)); michael@0: } michael@0: if (p) free(p); michael@0: checked_prefix = 1; michael@0: } michael@0: // else check forbiddenwords michael@0: } else if (rv->astr && (TESTAFF(rv->astr, forbiddenword, rv->alen) || michael@0: TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen) || michael@0: TESTAFF(rv->astr, needaffix, rv->alen))) { michael@0: st[i] = ch; michael@0: continue; michael@0: } michael@0: michael@0: // check non_compound flag in suffix and prefix michael@0: if ((rv) && !hu_mov_rule && michael@0: ((pfx && pfx->getCont() && michael@0: TESTAFF(pfx->getCont(), compoundforbidflag, michael@0: pfx->getContLen())) || michael@0: (sfx && sfx->getCont() && michael@0: TESTAFF(sfx->getCont(), compoundforbidflag, michael@0: sfx->getContLen())))) { michael@0: continue; michael@0: } michael@0: michael@0: // check compoundend flag in suffix and prefix michael@0: if ((rv) && !checked_prefix && compoundend && !hu_mov_rule && michael@0: ((pfx && pfx->getCont() && michael@0: TESTAFF(pfx->getCont(), compoundend, michael@0: pfx->getContLen())) || michael@0: (sfx && sfx->getCont() && michael@0: TESTAFF(sfx->getCont(), compoundend, michael@0: sfx->getContLen())))) { michael@0: continue; michael@0: } michael@0: michael@0: // check compoundmiddle flag in suffix and prefix michael@0: if ((rv) && !checked_prefix && (wordnum==0) && compoundmiddle && !hu_mov_rule && michael@0: ((pfx && pfx->getCont() && michael@0: TESTAFF(pfx->getCont(), compoundmiddle, michael@0: pfx->getContLen())) || michael@0: (sfx && sfx->getCont() && michael@0: TESTAFF(sfx->getCont(), compoundmiddle, michael@0: sfx->getContLen())))) { michael@0: rv = NULL; michael@0: } michael@0: michael@0: // check forbiddenwords michael@0: if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) michael@0: || TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) continue; michael@0: michael@0: // increment word number, if the second root has a compoundroot flag michael@0: if ((rv) && (compoundroot) && michael@0: (TESTAFF(rv->astr, compoundroot, rv->alen))) { michael@0: wordnum++; michael@0: } michael@0: michael@0: // first word is acceptable in compound words? michael@0: if (((rv) && michael@0: ( checked_prefix || (words && words[wnum]) || michael@0: (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || michael@0: ((oldwordnum == 0) && compoundbegin && TESTAFF(rv->astr, compoundbegin, rv->alen)) || michael@0: ((oldwordnum > 0) && compoundmiddle && TESTAFF(rv->astr, compoundmiddle, rv->alen)) michael@0: // LANG_hu section: spec. Hungarian rule michael@0: || ((langnum == LANG_hu) && // hu_mov_rule michael@0: hu_mov_rule && ( michael@0: TESTAFF(rv->astr, 'F', rv->alen) || michael@0: TESTAFF(rv->astr, 'G', rv->alen) || michael@0: TESTAFF(rv->astr, 'H', rv->alen) michael@0: ) michael@0: ) michael@0: // END of LANG_hu section michael@0: ) michael@0: && ! (( checkcompoundtriple && !words && // test triple letters michael@0: (word[i-1]==word[i]) && ( michael@0: ((i>1) && (word[i-1]==word[i-2])) || michael@0: ((word[i-1]==word[i+1])) // may be word[i+1] == '\0' michael@0: ) michael@0: ) || michael@0: ( michael@0: // test CHECKCOMPOUNDPATTERN michael@0: numcheckcpd && !words && cpdpat_check(word, i, rv, NULL, affixed) michael@0: ) || michael@0: ( michael@0: checkcompoundcase && !words && cpdcase_check(word, i) michael@0: )) michael@0: ) michael@0: // LANG_hu section: spec. Hungarian rule michael@0: || ((!rv) && (langnum == LANG_hu) && hu_mov_rule && (rv = affix_check(st,i)) && michael@0: (sfx && sfx->getCont() && ( michael@0: TESTAFF(sfx->getCont(), (unsigned short) 'x', sfx->getContLen()) || michael@0: TESTAFF(sfx->getCont(), (unsigned short) '%', sfx->getContLen()) michael@0: ) michael@0: ) michael@0: ) michael@0: // END of LANG_hu section michael@0: ) { michael@0: michael@0: // LANG_hu section: spec. Hungarian rule michael@0: if (langnum == LANG_hu) { michael@0: // calculate syllable number of the word michael@0: numsyllable += get_syllable(st, i); michael@0: michael@0: // + 1 word, if syllable number of the prefix > 1 (hungarian convention) michael@0: if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; michael@0: } michael@0: // END of LANG_hu section michael@0: michael@0: // NEXT WORD(S) michael@0: rv_first = rv; michael@0: rv = lookup((word+i)); // perhaps without prefix michael@0: michael@0: // search homonym with compound flag michael@0: while ((rv) && ((needaffix && TESTAFF(rv->astr, needaffix, rv->alen)) || michael@0: !((compoundflag && !words && TESTAFF(rv->astr, compoundflag, rv->alen)) || michael@0: (compoundend && !words && TESTAFF(rv->astr, compoundend, rv->alen)) || michael@0: (numdefcpd && words && defcpd_check(&words, wnum + 1, rv, NULL,1))))) { michael@0: rv = rv->next_homonym; michael@0: } michael@0: michael@0: if (rv && words && words[wnum + 1]) { michael@0: mystrcat(*result, presult, MAXLNLEN); michael@0: mystrcat(*result, " ", MAXLNLEN); michael@0: mystrcat(*result, MORPH_PART, MAXLNLEN); michael@0: mystrcat(*result, word+i, MAXLNLEN); michael@0: if (complexprefixes && HENTRY_DATA(rv)) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); michael@0: if (!HENTRY_FIND(rv, MORPH_STEM)) { michael@0: mystrcat(*result, " ", MAXLNLEN); michael@0: mystrcat(*result, MORPH_STEM, MAXLNLEN); michael@0: mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN); michael@0: } michael@0: // store the pointer of the hash entry michael@0: // sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv); michael@0: if (!complexprefixes && HENTRY_DATA(rv)) { michael@0: mystrcat(*result, " ", MAXLNLEN); michael@0: mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); michael@0: } michael@0: mystrcat(*result, "\n", MAXLNLEN); michael@0: ok = 1; michael@0: return 0; michael@0: } michael@0: michael@0: oldnumsyllable2 = numsyllable; michael@0: oldwordnum2 = wordnum; michael@0: michael@0: // LANG_hu section: spec. Hungarian rule michael@0: if ((rv) && (langnum == LANG_hu) && (TESTAFF(rv->astr, 'I', rv->alen)) && !(TESTAFF(rv->astr, 'J', rv->alen))) { michael@0: numsyllable--; michael@0: } michael@0: // END of LANG_hu section michael@0: // increment word number, if the second root has a compoundroot flag michael@0: if ((rv) && (compoundroot) && michael@0: (TESTAFF(rv->astr, compoundroot, rv->alen))) { michael@0: wordnum++; michael@0: } michael@0: michael@0: // check forbiddenwords michael@0: if ((rv) && (rv->astr) && (TESTAFF(rv->astr, forbiddenword, rv->alen) || michael@0: TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen))) { michael@0: st[i] = ch; michael@0: continue; michael@0: } michael@0: michael@0: // second word is acceptable, as a root? michael@0: // hungarian conventions: compounding is acceptable, michael@0: // when compound forms consist of 2 words, or if more, michael@0: // then the syllable number of root words must be 6, or lesser. michael@0: if ((rv) && ( michael@0: (compoundflag && TESTAFF(rv->astr, compoundflag, rv->alen)) || michael@0: (compoundend && TESTAFF(rv->astr, compoundend, rv->alen)) michael@0: ) michael@0: && ( michael@0: ((cpdwordmax==-1) || (wordnum+1blen)<=cpdmaxsyllable)) michael@0: ) michael@0: && ( michael@0: (!checkcompounddup || (rv != rv_first)) michael@0: ) michael@0: ) michael@0: { michael@0: // bad compound word michael@0: mystrcat(*result, presult, MAXLNLEN); michael@0: mystrcat(*result, " ", MAXLNLEN); michael@0: mystrcat(*result, MORPH_PART, MAXLNLEN); michael@0: mystrcat(*result, word+i, MAXLNLEN); michael@0: michael@0: if (HENTRY_DATA(rv)) { michael@0: if (complexprefixes) mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); michael@0: if (! HENTRY_FIND(rv, MORPH_STEM)) { michael@0: mystrcat(*result, " ", MAXLNLEN); michael@0: mystrcat(*result, MORPH_STEM, MAXLNLEN); michael@0: mystrcat(*result, HENTRY_WORD(rv), MAXLNLEN); michael@0: } michael@0: // store the pointer of the hash entry michael@0: // sprintf(*result + strlen(*result), " %s%p", MORPH_HENTRY, rv); michael@0: if (!complexprefixes) { michael@0: mystrcat(*result, " ", MAXLNLEN); michael@0: mystrcat(*result, HENTRY_DATA2(rv), MAXLNLEN); michael@0: } michael@0: } michael@0: mystrcat(*result, "\n", MAXLNLEN); michael@0: ok = 1; michael@0: } michael@0: michael@0: numsyllable = oldnumsyllable2 ; michael@0: wordnum = oldwordnum2; michael@0: michael@0: // perhaps second word has prefix or/and suffix michael@0: sfx = NULL; michael@0: sfxflag = FLAG_NULL; michael@0: michael@0: if (compoundflag && !onlycpdrule) rv = affix_check((word+i),strlen(word+i), compoundflag); else rv = NULL; michael@0: michael@0: if (!rv && compoundend && !onlycpdrule) { michael@0: sfx = NULL; michael@0: pfx = NULL; michael@0: rv = affix_check((word+i),strlen(word+i), compoundend); michael@0: } michael@0: michael@0: if (!rv && numdefcpd && words) { michael@0: rv = affix_check((word+i),strlen(word+i), 0, IN_CPD_END); michael@0: if (rv && words && defcpd_check(&words, wnum + 1, rv, NULL, 1)) { michael@0: char * m = NULL; michael@0: if (compoundflag) m = affix_check_morph((word+i),strlen(word+i), compoundflag); michael@0: if ((!m || *m == '\0') && compoundend) { michael@0: if (m) free(m); michael@0: m = affix_check_morph((word+i),strlen(word+i), compoundend); michael@0: } michael@0: mystrcat(*result, presult, MAXLNLEN); michael@0: if (m || (*m != '\0')) { michael@0: sprintf(*result + strlen(*result), "%c%s%s%s", MSEP_FLD, michael@0: MORPH_PART, word + i, line_uniq_app(&m, MSEP_REC)); michael@0: } michael@0: if (m) free(m); michael@0: mystrcat(*result, "\n", MAXLNLEN); michael@0: ok = 1; michael@0: } michael@0: } michael@0: michael@0: // check non_compound flag in suffix and prefix michael@0: if ((rv) && michael@0: ((pfx && pfx->getCont() && michael@0: TESTAFF(pfx->getCont(), compoundforbidflag, michael@0: pfx->getContLen())) || michael@0: (sfx && sfx->getCont() && michael@0: TESTAFF(sfx->getCont(), compoundforbidflag, michael@0: sfx->getContLen())))) { michael@0: rv = NULL; michael@0: } michael@0: michael@0: // check forbiddenwords michael@0: if ((rv) && (rv->astr) && (TESTAFF(rv->astr,forbiddenword,rv->alen) || michael@0: TESTAFF(rv->astr, ONLYUPCASEFLAG, rv->alen)) michael@0: && (! TESTAFF(rv->astr, needaffix, rv->alen))) { michael@0: st[i] = ch; michael@0: continue; michael@0: } michael@0: michael@0: if (langnum == LANG_hu) { michael@0: // calculate syllable number of the word michael@0: numsyllable += get_syllable(word + i, strlen(word + i)); michael@0: michael@0: // - affix syllable num. michael@0: // XXX only second suffix (inflections, not derivations) michael@0: if (sfxappnd) { michael@0: char * tmp = myrevstrdup(sfxappnd); michael@0: numsyllable -= get_syllable(tmp, strlen(tmp)); michael@0: free(tmp); michael@0: } michael@0: michael@0: // + 1 word, if syllable number of the prefix > 1 (hungarian convention) michael@0: if (pfx && (get_syllable(pfx->getKey(),strlen(pfx->getKey())) > 1)) wordnum++; michael@0: michael@0: // increment syllable num, if last word has a SYLLABLENUM flag michael@0: // and the suffix is beginning `s' michael@0: michael@0: if (cpdsyllablenum) { michael@0: switch (sfxflag) { michael@0: case 'c': { numsyllable+=2; break; } michael@0: case 'J': { numsyllable += 1; break; } michael@0: case 'I': { if (rv && TESTAFF(rv->astr, 'J', rv->alen)) numsyllable += 1; break; } michael@0: } michael@0: } michael@0: } michael@0: michael@0: // increment word number, if the second word has a compoundroot flag michael@0: if ((rv) && (compoundroot) && michael@0: (TESTAFF(rv->astr, compoundroot, rv->alen))) { michael@0: wordnum++; michael@0: } michael@0: // second word is acceptable, as a word with prefix or/and suffix? michael@0: // hungarian conventions: compounding is acceptable, michael@0: // when compound forms consist 2 word, otherwise michael@0: // the syllable number of root words is 6, or lesser. michael@0: if ((rv) && michael@0: ( michael@0: ((cpdwordmax==-1) || (wordnum+1 0) && *s1 && (*s1 == *end_of_s2)) { michael@0: s1++; michael@0: end_of_s2--; michael@0: len--; michael@0: } michael@0: return (*s1 == '\0'); michael@0: } michael@0: */ michael@0: michael@0: inline int AffixMgr::isRevSubset(const char * s1, const char * end_of_s2, int len) michael@0: { michael@0: while ((len > 0) && (*s1 != '\0') && ((*s1 == *end_of_s2) || (*s1 == '.'))) { michael@0: s1++; michael@0: end_of_s2--; michael@0: len--; michael@0: } michael@0: return (*s1 == '\0'); michael@0: } michael@0: michael@0: // check word for suffixes michael@0: michael@0: struct hentry * AffixMgr::suffix_check (const char * word, int len, michael@0: int sfxopts, PfxEntry * ppfx, char ** wlst, int maxSug, int * ns, michael@0: const FLAG cclass, const FLAG needflag, char in_compound) michael@0: { michael@0: struct hentry * rv = NULL; michael@0: PfxEntry* ep = ppfx; michael@0: michael@0: // first handle the special case of 0 length suffixes michael@0: SfxEntry * se = sStart[0]; michael@0: michael@0: while (se) { michael@0: if (!cclass || se->getCont()) { michael@0: // suffixes are not allowed in beginning of compounds michael@0: if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass michael@0: // except when signed with compoundpermitflag flag michael@0: (se->getCont() && compoundpermitflag && michael@0: TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix || michael@0: // no circumfix flag in prefix and suffix michael@0: ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), michael@0: circumfix, ep->getContLen())) && michael@0: (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) || michael@0: // circumfix flag in prefix AND suffix michael@0: ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), michael@0: circumfix, ep->getContLen())) && michael@0: (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) && michael@0: // fogemorpheme michael@0: (in_compound || michael@0: !(se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen())))) && michael@0: // needaffix on prefix or first suffix michael@0: (cclass || michael@0: !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) || michael@0: (ppfx && !((ep->getCont()) && michael@0: TESTAFF(ep->getCont(), needaffix, michael@0: ep->getContLen()))) michael@0: )) { michael@0: rv = se->checkword(word,len, sfxopts, ppfx, wlst, maxSug, ns, (FLAG) cclass, michael@0: needflag, (in_compound ? 0 : onlyincompound)); michael@0: if (rv) { michael@0: sfx=se; // BUG: sfx not stateless michael@0: return rv; michael@0: } michael@0: } michael@0: } michael@0: se = se->getNext(); michael@0: } michael@0: michael@0: // now handle the general case michael@0: if (len == 0) return NULL; // FULLSTRIP michael@0: unsigned char sp= *((const unsigned char *)(word + len - 1)); michael@0: SfxEntry * sptr = sStart[sp]; michael@0: michael@0: while (sptr) { michael@0: if (isRevSubset(sptr->getKey(), word + len - 1, len) michael@0: ) { michael@0: // suffixes are not allowed in beginning of compounds michael@0: if ((((in_compound != IN_CPD_BEGIN)) || // && !cclass michael@0: // except when signed with compoundpermitflag flag michael@0: (sptr->getCont() && compoundpermitflag && michael@0: TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix || michael@0: // no circumfix flag in prefix and suffix michael@0: ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), michael@0: circumfix, ep->getContLen())) && michael@0: (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) || michael@0: // circumfix flag in prefix AND suffix michael@0: ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), michael@0: circumfix, ep->getContLen())) && michael@0: (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) && michael@0: // fogemorpheme michael@0: (in_compound || michael@0: !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) && michael@0: // needaffix on prefix or first suffix michael@0: (cclass || michael@0: !(sptr->getCont() && TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) || michael@0: (ppfx && !((ep->getCont()) && michael@0: TESTAFF(ep->getCont(), needaffix, michael@0: ep->getContLen()))) michael@0: ) michael@0: ) if (in_compound != IN_CPD_END || ppfx || !(sptr->getCont() && TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))) { michael@0: rv = sptr->checkword(word,len, sfxopts, ppfx, wlst, michael@0: maxSug, ns, cclass, needflag, (in_compound ? 0 : onlyincompound)); michael@0: if (rv) { michael@0: sfx=sptr; // BUG: sfx not stateless michael@0: sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless michael@0: if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless michael@0: return rv; michael@0: } michael@0: } michael@0: sptr = sptr->getNextEQ(); michael@0: } else { michael@0: sptr = sptr->getNextNE(); michael@0: } michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: // check word for two-level suffixes michael@0: michael@0: struct hentry * AffixMgr::suffix_check_twosfx(const char * word, int len, michael@0: int sfxopts, PfxEntry * ppfx, const FLAG needflag) michael@0: { michael@0: struct hentry * rv = NULL; michael@0: michael@0: // first handle the special case of 0 length suffixes michael@0: SfxEntry * se = sStart[0]; michael@0: while (se) { michael@0: if (contclasses[se->getFlag()]) michael@0: { michael@0: rv = se->check_twosfx(word,len, sfxopts, ppfx, needflag); michael@0: if (rv) return rv; michael@0: } michael@0: se = se->getNext(); michael@0: } michael@0: michael@0: // now handle the general case michael@0: if (len == 0) return NULL; // FULLSTRIP michael@0: unsigned char sp = *((const unsigned char *)(word + len - 1)); michael@0: SfxEntry * sptr = sStart[sp]; michael@0: michael@0: while (sptr) { michael@0: if (isRevSubset(sptr->getKey(), word + len - 1, len)) { michael@0: if (contclasses[sptr->getFlag()]) michael@0: { michael@0: rv = sptr->check_twosfx(word,len, sfxopts, ppfx, needflag); michael@0: if (rv) { michael@0: sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless michael@0: if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless michael@0: return rv; michael@0: } michael@0: } michael@0: sptr = sptr->getNextEQ(); michael@0: } else { michael@0: sptr = sptr->getNextNE(); michael@0: } michael@0: } michael@0: michael@0: return NULL; michael@0: } michael@0: michael@0: char * AffixMgr::suffix_check_twosfx_morph(const char * word, int len, michael@0: int sfxopts, PfxEntry * ppfx, const FLAG needflag) michael@0: { michael@0: char result[MAXLNLEN]; michael@0: char result2[MAXLNLEN]; michael@0: char result3[MAXLNLEN]; michael@0: michael@0: char * st; michael@0: michael@0: result[0] = '\0'; michael@0: result2[0] = '\0'; michael@0: result3[0] = '\0'; michael@0: michael@0: // first handle the special case of 0 length suffixes michael@0: SfxEntry * se = sStart[0]; michael@0: while (se) { michael@0: if (contclasses[se->getFlag()]) michael@0: { michael@0: st = se->check_twosfx_morph(word,len, sfxopts, ppfx, needflag); michael@0: if (st) { michael@0: if (ppfx) { michael@0: if (ppfx->getMorph()) { michael@0: mystrcat(result, ppfx->getMorph(), MAXLNLEN); michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: } else debugflag(result, ppfx->getFlag()); michael@0: } michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: if (se->getMorph()) { michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: mystrcat(result, se->getMorph(), MAXLNLEN); michael@0: } else debugflag(result, se->getFlag()); michael@0: mystrcat(result, "\n", MAXLNLEN); michael@0: } michael@0: } michael@0: se = se->getNext(); michael@0: } michael@0: michael@0: // now handle the general case michael@0: if (len == 0) return NULL; // FULLSTRIP michael@0: unsigned char sp = *((const unsigned char *)(word + len - 1)); michael@0: SfxEntry * sptr = sStart[sp]; michael@0: michael@0: while (sptr) { michael@0: if (isRevSubset(sptr->getKey(), word + len - 1, len)) { michael@0: if (contclasses[sptr->getFlag()]) michael@0: { michael@0: st = sptr->check_twosfx_morph(word,len, sfxopts, ppfx, needflag); michael@0: if (st) { michael@0: sfxflag = sptr->getFlag(); // BUG: sfxflag not stateless michael@0: if (!sptr->getCont()) sfxappnd=sptr->getKey(); // BUG: sfxappnd not stateless michael@0: strcpy(result2, st); michael@0: free(st); michael@0: michael@0: result3[0] = '\0'; michael@0: michael@0: if (sptr->getMorph()) { michael@0: mystrcat(result3, " ", MAXLNLEN); michael@0: mystrcat(result3, sptr->getMorph(), MAXLNLEN); michael@0: } else debugflag(result3, sptr->getFlag()); michael@0: strlinecat(result2, result3); michael@0: mystrcat(result2, "\n", MAXLNLEN); michael@0: mystrcat(result, result2, MAXLNLEN); michael@0: } michael@0: } michael@0: sptr = sptr->getNextEQ(); michael@0: } else { michael@0: sptr = sptr->getNextNE(); michael@0: } michael@0: } michael@0: if (*result) return mystrdup(result); michael@0: return NULL; michael@0: } michael@0: michael@0: char * AffixMgr::suffix_check_morph(const char * word, int len, michael@0: int sfxopts, PfxEntry * ppfx, const FLAG cclass, const FLAG needflag, char in_compound) michael@0: { michael@0: char result[MAXLNLEN]; michael@0: michael@0: struct hentry * rv = NULL; michael@0: michael@0: result[0] = '\0'; michael@0: michael@0: PfxEntry* ep = ppfx; michael@0: michael@0: // first handle the special case of 0 length suffixes michael@0: SfxEntry * se = sStart[0]; michael@0: while (se) { michael@0: if (!cclass || se->getCont()) { michael@0: // suffixes are not allowed in beginning of compounds michael@0: if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass michael@0: // except when signed with compoundpermitflag flag michael@0: (se->getCont() && compoundpermitflag && michael@0: TESTAFF(se->getCont(),compoundpermitflag,se->getContLen()))) && (!circumfix || michael@0: // no circumfix flag in prefix and suffix michael@0: ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), michael@0: circumfix, ep->getContLen())) && michael@0: (!se->getCont() || !(TESTAFF(se->getCont(),circumfix,se->getContLen())))) || michael@0: // circumfix flag in prefix AND suffix michael@0: ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), michael@0: circumfix, ep->getContLen())) && michael@0: (se->getCont() && (TESTAFF(se->getCont(),circumfix,se->getContLen()))))) && michael@0: // fogemorpheme michael@0: (in_compound || michael@0: !((se->getCont() && (TESTAFF(se->getCont(), onlyincompound, se->getContLen()))))) && michael@0: // needaffix on prefix or first suffix michael@0: (cclass || michael@0: !(se->getCont() && TESTAFF(se->getCont(), needaffix, se->getContLen())) || michael@0: (ppfx && !((ep->getCont()) && michael@0: TESTAFF(ep->getCont(), needaffix, michael@0: ep->getContLen()))) michael@0: ) michael@0: )) michael@0: rv = se->checkword(word, len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag); michael@0: while (rv) { michael@0: if (ppfx) { michael@0: if (ppfx->getMorph()) { michael@0: mystrcat(result, ppfx->getMorph(), MAXLNLEN); michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: } else debugflag(result, ppfx->getFlag()); michael@0: } michael@0: if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); michael@0: if (! HENTRY_FIND(rv, MORPH_STEM)) { michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: mystrcat(result, MORPH_STEM, MAXLNLEN); michael@0: mystrcat(result, HENTRY_WORD(rv), MAXLNLEN); michael@0: } michael@0: // store the pointer of the hash entry michael@0: // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv); michael@0: michael@0: if (!complexprefixes && HENTRY_DATA(rv)) { michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); michael@0: } michael@0: if (se->getMorph()) { michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: mystrcat(result, se->getMorph(), MAXLNLEN); michael@0: } else debugflag(result, se->getFlag()); michael@0: mystrcat(result, "\n", MAXLNLEN); michael@0: rv = se->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag); michael@0: } michael@0: } michael@0: se = se->getNext(); michael@0: } michael@0: michael@0: // now handle the general case michael@0: if (len == 0) return NULL; // FULLSTRIP michael@0: unsigned char sp = *((const unsigned char *)(word + len - 1)); michael@0: SfxEntry * sptr = sStart[sp]; michael@0: michael@0: while (sptr) { michael@0: if (isRevSubset(sptr->getKey(), word + len - 1, len) michael@0: ) { michael@0: // suffixes are not allowed in beginning of compounds michael@0: if (((((in_compound != IN_CPD_BEGIN)) || // && !cclass michael@0: // except when signed with compoundpermitflag flag michael@0: (sptr->getCont() && compoundpermitflag && michael@0: TESTAFF(sptr->getCont(),compoundpermitflag,sptr->getContLen()))) && (!circumfix || michael@0: // no circumfix flag in prefix and suffix michael@0: ((!ppfx || !(ep->getCont()) || !TESTAFF(ep->getCont(), michael@0: circumfix, ep->getContLen())) && michael@0: (!sptr->getCont() || !(TESTAFF(sptr->getCont(),circumfix,sptr->getContLen())))) || michael@0: // circumfix flag in prefix AND suffix michael@0: ((ppfx && (ep->getCont()) && TESTAFF(ep->getCont(), michael@0: circumfix, ep->getContLen())) && michael@0: (sptr->getCont() && (TESTAFF(sptr->getCont(),circumfix,sptr->getContLen()))))) && michael@0: // fogemorpheme michael@0: (in_compound || michael@0: !((sptr->getCont() && (TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen()))))) && michael@0: // needaffix on first suffix michael@0: (cclass || !(sptr->getCont() && michael@0: TESTAFF(sptr->getCont(), needaffix, sptr->getContLen()))) michael@0: )) rv = sptr->checkword(word,len, sfxopts, ppfx, NULL, 0, 0, cclass, needflag); michael@0: while (rv) { michael@0: if (ppfx) { michael@0: if (ppfx->getMorph()) { michael@0: mystrcat(result, ppfx->getMorph(), MAXLNLEN); michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: } else debugflag(result, ppfx->getFlag()); michael@0: } michael@0: if (complexprefixes && HENTRY_DATA(rv)) mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); michael@0: if (! HENTRY_FIND(rv, MORPH_STEM)) { michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: mystrcat(result, MORPH_STEM, MAXLNLEN); michael@0: mystrcat(result, HENTRY_WORD(rv), MAXLNLEN); michael@0: } michael@0: // store the pointer of the hash entry michael@0: // sprintf(result + strlen(result), " %s%p", MORPH_HENTRY, rv); michael@0: michael@0: if (!complexprefixes && HENTRY_DATA(rv)) { michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: mystrcat(result, HENTRY_DATA2(rv), MAXLNLEN); michael@0: } michael@0: michael@0: if (sptr->getMorph()) { michael@0: mystrcat(result, " ", MAXLNLEN); michael@0: mystrcat(result, sptr->getMorph(), MAXLNLEN); michael@0: } else debugflag(result, sptr->getFlag()); michael@0: mystrcat(result, "\n", MAXLNLEN); michael@0: rv = sptr->get_next_homonym(rv, sfxopts, ppfx, cclass, needflag); michael@0: } michael@0: sptr = sptr->getNextEQ(); michael@0: } else { michael@0: sptr = sptr->getNextNE(); michael@0: } michael@0: } michael@0: michael@0: if (*result) return mystrdup(result); michael@0: return NULL; michael@0: } michael@0: michael@0: // check if word with affixes is correctly spelled michael@0: struct hentry * AffixMgr::affix_check (const char * word, int len, const FLAG needflag, char in_compound) michael@0: { michael@0: struct hentry * rv= NULL; michael@0: michael@0: // check all prefixes (also crossed with suffixes if allowed) michael@0: rv = prefix_check(word, len, in_compound, needflag); michael@0: if (rv) return rv; michael@0: michael@0: // if still not found check all suffixes michael@0: rv = suffix_check(word, len, 0, NULL, NULL, 0, NULL, FLAG_NULL, needflag, in_compound); michael@0: michael@0: if (havecontclass) { michael@0: sfx = NULL; michael@0: pfx = NULL; michael@0: michael@0: if (rv) return rv; michael@0: // if still not found check all two-level suffixes michael@0: rv = suffix_check_twosfx(word, len, 0, NULL, needflag); michael@0: michael@0: if (rv) return rv; michael@0: // if still not found check all two-level suffixes michael@0: rv = prefix_check_twosfx(word, len, IN_CPD_NOT, needflag); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // check if word with affixes is correctly spelled michael@0: char * AffixMgr::affix_check_morph(const char * word, int len, const FLAG needflag, char in_compound) michael@0: { michael@0: char result[MAXLNLEN]; michael@0: char * st = NULL; michael@0: michael@0: *result = '\0'; michael@0: michael@0: // check all prefixes (also crossed with suffixes if allowed) michael@0: st = prefix_check_morph(word, len, in_compound); michael@0: if (st) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: } michael@0: michael@0: // if still not found check all suffixes michael@0: st = suffix_check_morph(word, len, 0, NULL, '\0', needflag, in_compound); michael@0: if (st) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: } michael@0: michael@0: if (havecontclass) { michael@0: sfx = NULL; michael@0: pfx = NULL; michael@0: // if still not found check all two-level suffixes michael@0: st = suffix_check_twosfx_morph(word, len, 0, NULL, needflag); michael@0: if (st) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: } michael@0: michael@0: // if still not found check all two-level suffixes michael@0: st = prefix_check_twosfx_morph(word, len, IN_CPD_NOT, needflag); michael@0: if (st) { michael@0: mystrcat(result, st, MAXLNLEN); michael@0: free(st); michael@0: } michael@0: } michael@0: michael@0: return mystrdup(result); michael@0: } michael@0: michael@0: char * AffixMgr::morphgen(char * ts, int wl, const unsigned short * ap, michael@0: unsigned short al, char * morph, char * targetmorph, int level) michael@0: { michael@0: // handle suffixes michael@0: char * stemmorph; michael@0: char * stemmorphcatpos; michael@0: char mymorph[MAXLNLEN]; michael@0: michael@0: if (!morph) return NULL; michael@0: michael@0: // check substandard flag michael@0: if (TESTAFF(ap, substandard, al)) return NULL; michael@0: michael@0: if (morphcmp(morph, targetmorph) == 0) return mystrdup(ts); michael@0: michael@0: // int targetcount = get_sfxcount(targetmorph); michael@0: michael@0: // use input suffix fields, if exist michael@0: if (strstr(morph, MORPH_INFL_SFX) || strstr(morph, MORPH_DERI_SFX)) { michael@0: stemmorph = mymorph; michael@0: strcpy(stemmorph, morph); michael@0: mystrcat(stemmorph, " ", MAXLNLEN); michael@0: stemmorphcatpos = stemmorph + strlen(stemmorph); michael@0: } else { michael@0: stemmorph = morph; michael@0: stemmorphcatpos = NULL; michael@0: } michael@0: michael@0: for (int i = 0; i < al; i++) { michael@0: const unsigned char c = (unsigned char) (ap[i] & 0x00FF); michael@0: SfxEntry * sptr = sFlag[c]; michael@0: while (sptr) { michael@0: if (sptr->getFlag() == ap[i] && sptr->getMorph() && ((sptr->getContLen() == 0) || michael@0: // don't generate forms with substandard affixes michael@0: !TESTAFF(sptr->getCont(), substandard, sptr->getContLen()))) { michael@0: michael@0: if (stemmorphcatpos) strcpy(stemmorphcatpos, sptr->getMorph()); michael@0: else stemmorph = (char *) sptr->getMorph(); michael@0: michael@0: int cmp = morphcmp(stemmorph, targetmorph); michael@0: michael@0: if (cmp == 0) { michael@0: char * newword = sptr->add(ts, wl); michael@0: if (newword) { michael@0: hentry * check = pHMgr->lookup(newword); // XXX extra dic michael@0: if (!check || !check->astr || michael@0: !(TESTAFF(check->astr, forbiddenword, check->alen) || michael@0: TESTAFF(check->astr, ONLYUPCASEFLAG, check->alen))) { michael@0: return newword; michael@0: } michael@0: free(newword); michael@0: } michael@0: } michael@0: michael@0: // recursive call for secondary suffixes michael@0: if ((level == 0) && (cmp == 1) && (sptr->getContLen() > 0) && michael@0: // (get_sfxcount(stemmorph) < targetcount) && michael@0: !TESTAFF(sptr->getCont(), substandard, sptr->getContLen())) { michael@0: char * newword = sptr->add(ts, wl); michael@0: if (newword) { michael@0: char * newword2 = morphgen(newword, strlen(newword), sptr->getCont(), michael@0: sptr->getContLen(), stemmorph, targetmorph, 1); michael@0: michael@0: if (newword2) { michael@0: free(newword); michael@0: return newword2; michael@0: } michael@0: free(newword); michael@0: newword = NULL; michael@0: } michael@0: } michael@0: } michael@0: sptr = sptr->getFlgNxt(); michael@0: } michael@0: } michael@0: return NULL; michael@0: } michael@0: michael@0: michael@0: int AffixMgr::expand_rootword(struct guessword * wlst, int maxn, const char * ts, michael@0: int wl, const unsigned short * ap, unsigned short al, char * bad, int badl, michael@0: char * phon) michael@0: { michael@0: int nh=0; michael@0: // first add root word to list michael@0: if ((nh < maxn) && !(al && ((needaffix && TESTAFF(ap, needaffix, al)) || michael@0: (onlyincompound && TESTAFF(ap, onlyincompound, al))))) { michael@0: wlst[nh].word = mystrdup(ts); michael@0: if (!wlst[nh].word) return 0; michael@0: wlst[nh].allow = (1 == 0); michael@0: wlst[nh].orig = NULL; michael@0: nh++; michael@0: // add special phonetic version michael@0: if (phon && (nh < maxn)) { michael@0: wlst[nh].word = mystrdup(phon); michael@0: if (!wlst[nh].word) return nh - 1; michael@0: wlst[nh].allow = (1 == 0); michael@0: wlst[nh].orig = mystrdup(ts); michael@0: if (!wlst[nh].orig) return nh - 1; michael@0: nh++; michael@0: } michael@0: } michael@0: michael@0: // handle suffixes michael@0: for (int i = 0; i < al; i++) { michael@0: const unsigned char c = (unsigned char) (ap[i] & 0x00FF); michael@0: SfxEntry * sptr = sFlag[c]; michael@0: while (sptr) { michael@0: if ((sptr->getFlag() == ap[i]) && (!sptr->getKeyLen() || ((badl > sptr->getKeyLen()) && michael@0: (strcmp(sptr->getAffix(), bad + badl - sptr->getKeyLen()) == 0))) && michael@0: // check needaffix flag michael@0: !(sptr->getCont() && ((needaffix && michael@0: TESTAFF(sptr->getCont(), needaffix, sptr->getContLen())) || michael@0: (circumfix && michael@0: TESTAFF(sptr->getCont(), circumfix, sptr->getContLen())) || michael@0: (onlyincompound && michael@0: TESTAFF(sptr->getCont(), onlyincompound, sptr->getContLen())))) michael@0: ) { michael@0: char * newword = sptr->add(ts, wl); michael@0: if (newword) { michael@0: if (nh < maxn) { michael@0: wlst[nh].word = newword; michael@0: wlst[nh].allow = sptr->allowCross(); michael@0: wlst[nh].orig = NULL; michael@0: nh++; michael@0: // add special phonetic version michael@0: if (phon && (nh < maxn)) { michael@0: char st[MAXWORDUTF8LEN]; michael@0: strcpy(st, phon); michael@0: strcat(st, sptr->getKey()); michael@0: reverseword(st + strlen(phon)); michael@0: wlst[nh].word = mystrdup(st); michael@0: if (!wlst[nh].word) return nh - 1; michael@0: wlst[nh].allow = (1 == 0); michael@0: wlst[nh].orig = mystrdup(newword); michael@0: if (!wlst[nh].orig) return nh - 1; michael@0: nh++; michael@0: } michael@0: } else { michael@0: free(newword); michael@0: } michael@0: } michael@0: } michael@0: sptr = sptr->getFlgNxt(); michael@0: } michael@0: } michael@0: michael@0: int n = nh; michael@0: michael@0: // handle cross products of prefixes and suffixes michael@0: for (int j=1;jgetFlag() == ap[k]) && cptr->allowCross() && (!cptr->getKeyLen() || ((badl > cptr->getKeyLen()) && michael@0: (strncmp(cptr->getKey(), bad, cptr->getKeyLen()) == 0)))) { michael@0: int l1 = strlen(wlst[j].word); michael@0: char * newword = cptr->add(wlst[j].word, l1); michael@0: if (newword) { michael@0: if (nh < maxn) { michael@0: wlst[nh].word = newword; michael@0: wlst[nh].allow = cptr->allowCross(); michael@0: wlst[nh].orig = NULL; michael@0: nh++; michael@0: } else { michael@0: free(newword); michael@0: } michael@0: } michael@0: } michael@0: cptr = cptr->getFlgNxt(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: michael@0: // now handle pure prefixes michael@0: for (int m = 0; m < al; m ++) { michael@0: const unsigned char c = (unsigned char) (ap[m] & 0x00FF); michael@0: PfxEntry * ptr = pFlag[c]; michael@0: while (ptr) { michael@0: if ((ptr->getFlag() == ap[m]) && (!ptr->getKeyLen() || ((badl > ptr->getKeyLen()) && michael@0: (strncmp(ptr->getKey(), bad, ptr->getKeyLen()) == 0))) && michael@0: // check needaffix flag michael@0: !(ptr->getCont() && ((needaffix && michael@0: TESTAFF(ptr->getCont(), needaffix, ptr->getContLen())) || michael@0: (circumfix && michael@0: TESTAFF(ptr->getCont(), circumfix, ptr->getContLen())) || michael@0: (onlyincompound && michael@0: TESTAFF(ptr->getCont(), onlyincompound, ptr->getContLen())))) michael@0: ) { michael@0: char * newword = ptr->add(ts, wl); michael@0: if (newword) { michael@0: if (nh < maxn) { michael@0: wlst[nh].word = newword; michael@0: wlst[nh].allow = ptr->allowCross(); michael@0: wlst[nh].orig = NULL; michael@0: nh++; michael@0: } else { michael@0: free(newword); michael@0: } michael@0: } michael@0: } michael@0: ptr = ptr->getFlgNxt(); michael@0: } michael@0: } michael@0: michael@0: return nh; michael@0: } michael@0: michael@0: // return length of replacing table michael@0: int AffixMgr::get_numrep() const michael@0: { michael@0: return numrep; michael@0: } michael@0: michael@0: // return replacing table michael@0: struct replentry * AffixMgr::get_reptable() const michael@0: { michael@0: if (! reptable ) return NULL; michael@0: return reptable; michael@0: } michael@0: michael@0: // return iconv table michael@0: RepList * AffixMgr::get_iconvtable() const michael@0: { michael@0: if (! iconvtable ) return NULL; michael@0: return iconvtable; michael@0: } michael@0: michael@0: // return oconv table michael@0: RepList * AffixMgr::get_oconvtable() const michael@0: { michael@0: if (! oconvtable ) return NULL; michael@0: return oconvtable; michael@0: } michael@0: michael@0: // return replacing table michael@0: struct phonetable * AffixMgr::get_phonetable() const michael@0: { michael@0: if (! phone ) return NULL; michael@0: return phone; michael@0: } michael@0: michael@0: // return length of character map table michael@0: int AffixMgr::get_nummap() const michael@0: { michael@0: return nummap; michael@0: } michael@0: michael@0: // return character map table michael@0: struct mapentry * AffixMgr::get_maptable() const michael@0: { michael@0: if (! maptable ) return NULL; michael@0: return maptable; michael@0: } michael@0: michael@0: // return length of word break table michael@0: int AffixMgr::get_numbreak() const michael@0: { michael@0: return numbreak; michael@0: } michael@0: michael@0: // return character map table michael@0: char ** AffixMgr::get_breaktable() const michael@0: { michael@0: if (! breaktable ) return NULL; michael@0: return breaktable; michael@0: } michael@0: michael@0: // return text encoding of dictionary michael@0: char * AffixMgr::get_encoding() michael@0: { michael@0: if (! encoding ) encoding = mystrdup(SPELL_ENCODING); michael@0: return mystrdup(encoding); michael@0: } michael@0: michael@0: // return text encoding of dictionary michael@0: int AffixMgr::get_langnum() const michael@0: { michael@0: return langnum; michael@0: } michael@0: michael@0: // return double prefix option michael@0: int AffixMgr::get_complexprefixes() const michael@0: { michael@0: return complexprefixes; michael@0: } michael@0: michael@0: // return FULLSTRIP option michael@0: int AffixMgr::get_fullstrip() const michael@0: { michael@0: return fullstrip; michael@0: } michael@0: michael@0: FLAG AffixMgr::get_keepcase() const michael@0: { michael@0: return keepcase; michael@0: } michael@0: michael@0: FLAG AffixMgr::get_forceucase() const michael@0: { michael@0: return forceucase; michael@0: } michael@0: michael@0: FLAG AffixMgr::get_warn() const michael@0: { michael@0: return warn; michael@0: } michael@0: michael@0: int AffixMgr::get_forbidwarn() const michael@0: { michael@0: return forbidwarn; michael@0: } michael@0: michael@0: int AffixMgr::get_checksharps() const michael@0: { michael@0: return checksharps; michael@0: } michael@0: michael@0: char * AffixMgr::encode_flag(unsigned short aflag) const michael@0: { michael@0: return pHMgr->encode_flag(aflag); michael@0: } michael@0: michael@0: michael@0: // return the preferred ignore string for suggestions michael@0: char * AffixMgr::get_ignore() const michael@0: { michael@0: if (!ignorechars) return NULL; michael@0: return ignorechars; michael@0: } michael@0: michael@0: // return the preferred ignore string for suggestions michael@0: unsigned short * AffixMgr::get_ignore_utf16(int * len) const michael@0: { michael@0: *len = ignorechars_utf16_len; michael@0: return ignorechars_utf16; michael@0: } michael@0: michael@0: // return the keyboard string for suggestions michael@0: char * AffixMgr::get_key_string() michael@0: { michael@0: if (! keystring ) keystring = mystrdup(SPELL_KEYSTRING); michael@0: return mystrdup(keystring); michael@0: } michael@0: michael@0: // return the preferred try string for suggestions michael@0: char * AffixMgr::get_try_string() const michael@0: { michael@0: if (! trystring ) return NULL; michael@0: return mystrdup(trystring); michael@0: } michael@0: michael@0: // return the preferred try string for suggestions michael@0: const char * AffixMgr::get_wordchars() const michael@0: { michael@0: return wordchars; michael@0: } michael@0: michael@0: unsigned short * AffixMgr::get_wordchars_utf16(int * len) const michael@0: { michael@0: *len = wordchars_utf16_len; michael@0: return wordchars_utf16; michael@0: } michael@0: michael@0: // is there compounding? michael@0: int AffixMgr::get_compound() const michael@0: { michael@0: return compoundflag || compoundbegin || numdefcpd; michael@0: } michael@0: michael@0: // return the compound words control flag michael@0: FLAG AffixMgr::get_compoundflag() const michael@0: { michael@0: return compoundflag; michael@0: } michael@0: michael@0: // return the forbidden words control flag michael@0: FLAG AffixMgr::get_forbiddenword() const michael@0: { michael@0: return forbiddenword; michael@0: } michael@0: michael@0: // return the forbidden words control flag michael@0: FLAG AffixMgr::get_nosuggest() const michael@0: { michael@0: return nosuggest; michael@0: } michael@0: michael@0: // return the forbidden words control flag michael@0: FLAG AffixMgr::get_nongramsuggest() const michael@0: { michael@0: return nongramsuggest; michael@0: } michael@0: michael@0: // return the forbidden words flag modify flag michael@0: FLAG AffixMgr::get_needaffix() const michael@0: { michael@0: return needaffix; michael@0: } michael@0: michael@0: // return the onlyincompound flag michael@0: FLAG AffixMgr::get_onlyincompound() const michael@0: { michael@0: return onlyincompound; michael@0: } michael@0: michael@0: // return the compound word signal flag michael@0: FLAG AffixMgr::get_compoundroot() const michael@0: { michael@0: return compoundroot; michael@0: } michael@0: michael@0: // return the compound begin signal flag michael@0: FLAG AffixMgr::get_compoundbegin() const michael@0: { michael@0: return compoundbegin; michael@0: } michael@0: michael@0: // return the value of checknum michael@0: int AffixMgr::get_checknum() const michael@0: { michael@0: return checknum; michael@0: } michael@0: michael@0: // return the value of prefix michael@0: const char * AffixMgr::get_prefix() const michael@0: { michael@0: if (pfx) return pfx->getKey(); michael@0: return NULL; michael@0: } michael@0: michael@0: // return the value of suffix michael@0: const char * AffixMgr::get_suffix() const michael@0: { michael@0: return sfxappnd; michael@0: } michael@0: michael@0: // return the value of suffix michael@0: const char * AffixMgr::get_version() const michael@0: { michael@0: return version; michael@0: } michael@0: michael@0: // return lemma_present flag michael@0: FLAG AffixMgr::get_lemma_present() const michael@0: { michael@0: return lemma_present; michael@0: } michael@0: michael@0: // utility method to look up root words in hash table michael@0: struct hentry * AffixMgr::lookup(const char * word) michael@0: { michael@0: int i; michael@0: struct hentry * he = NULL; michael@0: for (i = 0; i < *maxdic && !he; i++) { michael@0: he = (alldic[i])->lookup(word); michael@0: } michael@0: return he; michael@0: } michael@0: michael@0: // return the value of suffix michael@0: int AffixMgr::have_contclass() const michael@0: { michael@0: return havecontclass; michael@0: } michael@0: michael@0: // return utf8 michael@0: int AffixMgr::get_utf8() const michael@0: { michael@0: return utf8; michael@0: } michael@0: michael@0: int AffixMgr::get_maxngramsugs(void) const michael@0: { michael@0: return maxngramsugs; michael@0: } michael@0: michael@0: int AffixMgr::get_maxcpdsugs(void) const michael@0: { michael@0: return maxcpdsugs; michael@0: } michael@0: michael@0: int AffixMgr::get_maxdiff(void) const michael@0: { michael@0: return maxdiff; michael@0: } michael@0: michael@0: int AffixMgr::get_onlymaxdiff(void) const michael@0: { michael@0: return onlymaxdiff; michael@0: } michael@0: michael@0: // return nosplitsugs michael@0: int AffixMgr::get_nosplitsugs(void) const michael@0: { michael@0: return nosplitsugs; michael@0: } michael@0: michael@0: // return sugswithdots michael@0: int AffixMgr::get_sugswithdots(void) const michael@0: { michael@0: return sugswithdots; michael@0: } michael@0: michael@0: /* parse flag */ michael@0: int AffixMgr::parse_flag(char * line, unsigned short * out, FileMgr * af) { michael@0: char * s = NULL; michael@0: if (*out != FLAG_NULL && !(*out >= DEFAULTFLAGS)) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: if (parse_string(line, &s, af->getlinenum())) return 1; michael@0: *out = pHMgr->decode_flag(s); michael@0: free(s); michael@0: return 0; michael@0: } michael@0: michael@0: /* parse num */ michael@0: int AffixMgr::parse_num(char * line, int * out, FileMgr * af) { michael@0: char * s = NULL; michael@0: if (*out != -1) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix file parameter\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: if (parse_string(line, &s, af->getlinenum())) return 1; michael@0: *out = atoi(s); michael@0: free(s); michael@0: return 0; michael@0: } michael@0: michael@0: /* parse in the max syllablecount of compound words and */ michael@0: int AffixMgr::parse_cpdsyllable(char * line, FileMgr * af) michael@0: { michael@0: char * tp = line; michael@0: char * piece; michael@0: int i = 0; michael@0: int np = 0; michael@0: w_char w[MAXWORDLEN]; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { np++; break; } michael@0: case 1: { cpdmaxsyllable = atoi(piece); np++; break; } michael@0: case 2: { michael@0: if (!utf8) { michael@0: cpdvowels = mystrdup(piece); michael@0: } else { michael@0: int n = u8_u16(w, MAXWORDLEN, piece); michael@0: if (n > 0) { michael@0: flag_qsort((unsigned short *) w, 0, n); michael@0: cpdvowels_utf16 = (w_char *) malloc(n * sizeof(w_char)); michael@0: if (!cpdvowels_utf16) return 1; michael@0: memcpy(cpdvowels_utf16, w, n * sizeof(w_char)); michael@0: } michael@0: cpdvowels_utf16_len = n; michael@0: } michael@0: np++; michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (np < 2) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing compoundsyllable information\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: if (np == 2) cpdvowels = mystrdup("aeiouAEIOU"); michael@0: return 0; michael@0: } michael@0: michael@0: /* parse in the typical fault correcting table */ michael@0: int AffixMgr::parse_reptable(char * line, FileMgr * af) michael@0: { michael@0: if (numrep != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: char * tp = line; michael@0: char * piece; michael@0: int i = 0; michael@0: int np = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { np++; break; } michael@0: case 1: { michael@0: numrep = atoi(piece); michael@0: if (numrep < 1) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: reptable = (replentry *) malloc(numrep * sizeof(struct replentry)); michael@0: if (!reptable) return 1; michael@0: np++; michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (np != 2) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: michael@0: /* now parse the numrep lines to read in the remainder of the table */ michael@0: char * nl; michael@0: for (int j=0; j < numrep; j++) { michael@0: if (!(nl = af->getline())) return 1; michael@0: mychomp(nl); michael@0: tp = nl; michael@0: i = 0; michael@0: reptable[j].pattern = NULL; michael@0: reptable[j].pattern2 = NULL; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { michael@0: if (strncmp(piece,"REP",3) != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: numrep = 0; michael@0: return 1; michael@0: } michael@0: break; michael@0: } michael@0: case 1: { michael@0: if (*piece == '^') reptable[j].start = true; else reptable[j].start = false; michael@0: reptable[j].pattern = mystrrep(mystrdup(piece + int(reptable[j].start)),"_"," "); michael@0: int lr = strlen(reptable[j].pattern) - 1; michael@0: if (reptable[j].pattern[lr] == '$') { michael@0: reptable[j].end = true; michael@0: reptable[j].pattern[lr] = '\0'; michael@0: } else reptable[j].end = false; michael@0: break; michael@0: } michael@0: case 2: { reptable[j].pattern2 = mystrrep(mystrdup(piece),"_"," "); break; } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if ((!(reptable[j].pattern)) || (!(reptable[j].pattern2))) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: numrep = 0; michael@0: return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /* parse in the typical fault correcting table */ michael@0: int AffixMgr::parse_convtable(char * line, FileMgr * af, RepList ** rl, const char * keyword) michael@0: { michael@0: if (*rl) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: char * tp = line; michael@0: char * piece; michael@0: int i = 0; michael@0: int np = 0; michael@0: int numrl = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { np++; break; } michael@0: case 1: { michael@0: numrl = atoi(piece); michael@0: if (numrl < 1) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: incorrect entry number\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: *rl = new RepList(numrl); michael@0: if (!*rl) return 1; michael@0: np++; michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (np != 2) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: michael@0: /* now parse the num lines to read in the remainder of the table */ michael@0: char * nl; michael@0: for (int j=0; j < numrl; j++) { michael@0: if (!(nl = af->getline())) return 1; michael@0: mychomp(nl); michael@0: tp = nl; michael@0: i = 0; michael@0: char * pattern = NULL; michael@0: char * pattern2 = NULL; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { michael@0: if (strncmp(piece, keyword, strlen(keyword)) != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: delete *rl; michael@0: *rl = NULL; michael@0: return 1; michael@0: } michael@0: break; michael@0: } michael@0: case 1: { pattern = mystrrep(mystrdup(piece),"_"," "); break; } michael@0: case 2: { michael@0: pattern2 = mystrrep(mystrdup(piece),"_"," "); michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (!pattern || !pattern2) { michael@0: if (pattern) michael@0: free(pattern); michael@0: if (pattern2) michael@0: free(pattern2); michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: (*rl)->add(pattern, pattern2); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: /* parse in the typical fault correcting table */ michael@0: int AffixMgr::parse_phonetable(char * line, FileMgr * af) michael@0: { michael@0: if (phone) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: char * tp = line; michael@0: char * piece; michael@0: int i = 0; michael@0: int np = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { np++; break; } michael@0: case 1: { michael@0: phone = (phonetable *) malloc(sizeof(struct phonetable)); michael@0: if (!phone) return 1; michael@0: phone->num = atoi(piece); michael@0: phone->rules = NULL; michael@0: phone->utf8 = (char) utf8; michael@0: if (phone->num < 1) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: phone->rules = (char * *) malloc(2 * (phone->num + 1) * sizeof(char *)); michael@0: if (!phone->rules) { michael@0: free(phone); michael@0: phone = NULL; michael@0: return 1; michael@0: } michael@0: np++; michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (np != 2) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: michael@0: /* now parse the phone->num lines to read in the remainder of the table */ michael@0: char * nl; michael@0: for (int j=0; j < phone->num; j++) { michael@0: if (!(nl = af->getline())) return 1; michael@0: mychomp(nl); michael@0: tp = nl; michael@0: i = 0; michael@0: phone->rules[j * 2] = NULL; michael@0: phone->rules[j * 2 + 1] = NULL; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { michael@0: if (strncmp(piece,"PHONE",5) != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: phone->num = 0; michael@0: return 1; michael@0: } michael@0: break; michael@0: } michael@0: case 1: { phone->rules[j * 2] = mystrrep(mystrdup(piece),"_",""); break; } michael@0: case 2: { phone->rules[j * 2 + 1] = mystrrep(mystrdup(piece),"_",""); break; } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if ((!(phone->rules[j * 2])) || (!(phone->rules[j * 2 + 1]))) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: phone->num = 0; michael@0: return 1; michael@0: } michael@0: } michael@0: phone->rules[phone->num * 2] = mystrdup(""); michael@0: phone->rules[phone->num * 2 + 1] = mystrdup(""); michael@0: init_phonet_hash(*phone); michael@0: return 0; michael@0: } michael@0: michael@0: /* parse in the checkcompoundpattern table */ michael@0: int AffixMgr::parse_checkcpdtable(char * line, FileMgr * af) michael@0: { michael@0: if (numcheckcpd != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: char * tp = line; michael@0: char * piece; michael@0: int i = 0; michael@0: int np = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { np++; break; } michael@0: case 1: { michael@0: numcheckcpd = atoi(piece); michael@0: if (numcheckcpd < 1) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: checkcpdtable = (patentry *) malloc(numcheckcpd * sizeof(struct patentry)); michael@0: if (!checkcpdtable) return 1; michael@0: np++; michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (np != 2) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: michael@0: /* now parse the numcheckcpd lines to read in the remainder of the table */ michael@0: char * nl; michael@0: for (int j=0; j < numcheckcpd; j++) { michael@0: if (!(nl = af->getline())) return 1; michael@0: mychomp(nl); michael@0: tp = nl; michael@0: i = 0; michael@0: checkcpdtable[j].pattern = NULL; michael@0: checkcpdtable[j].pattern2 = NULL; michael@0: checkcpdtable[j].pattern3 = NULL; michael@0: checkcpdtable[j].cond = FLAG_NULL; michael@0: checkcpdtable[j].cond2 = FLAG_NULL; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { michael@0: if (strncmp(piece,"CHECKCOMPOUNDPATTERN",20) != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: numcheckcpd = 0; michael@0: return 1; michael@0: } michael@0: break; michael@0: } michael@0: case 1: { michael@0: checkcpdtable[j].pattern = mystrdup(piece); michael@0: char * p = strchr(checkcpdtable[j].pattern, '/'); michael@0: if (p) { michael@0: *p = '\0'; michael@0: checkcpdtable[j].cond = pHMgr->decode_flag(p + 1); michael@0: } michael@0: break; } michael@0: case 2: { michael@0: checkcpdtable[j].pattern2 = mystrdup(piece); michael@0: char * p = strchr(checkcpdtable[j].pattern2, '/'); michael@0: if (p) { michael@0: *p = '\0'; michael@0: checkcpdtable[j].cond2 = pHMgr->decode_flag(p + 1); michael@0: } michael@0: break; michael@0: } michael@0: case 3: { checkcpdtable[j].pattern3 = mystrdup(piece); simplifiedcpd = 1; break; } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if ((!(checkcpdtable[j].pattern)) || (!(checkcpdtable[j].pattern2))) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: numcheckcpd = 0; michael@0: return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /* parse in the compound rule table */ michael@0: int AffixMgr::parse_defcpdtable(char * line, FileMgr * af) michael@0: { michael@0: if (numdefcpd != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: char * tp = line; michael@0: char * piece; michael@0: int i = 0; michael@0: int np = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { np++; break; } michael@0: case 1: { michael@0: numdefcpd = atoi(piece); michael@0: if (numdefcpd < 1) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: defcpdtable = (flagentry *) malloc(numdefcpd * sizeof(flagentry)); michael@0: if (!defcpdtable) return 1; michael@0: np++; michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (np != 2) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: michael@0: /* now parse the numdefcpd lines to read in the remainder of the table */ michael@0: char * nl; michael@0: for (int j=0; j < numdefcpd; j++) { michael@0: if (!(nl = af->getline())) return 1; michael@0: mychomp(nl); michael@0: tp = nl; michael@0: i = 0; michael@0: defcpdtable[j].def = NULL; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { michael@0: if (strncmp(piece, "COMPOUNDRULE", 12) != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: numdefcpd = 0; michael@0: return 1; michael@0: } michael@0: break; michael@0: } michael@0: case 1: { // handle parenthesized flags michael@0: if (strchr(piece, '(')) { michael@0: defcpdtable[j].def = (FLAG *) malloc(strlen(piece) * sizeof(FLAG)); michael@0: defcpdtable[j].len = 0; michael@0: int end = 0; michael@0: FLAG * conv; michael@0: while (!end) { michael@0: char * par = piece + 1; michael@0: while (*par != '(' && *par != ')' && *par != '\0') par++; michael@0: if (*par == '\0') end = 1; else *par = '\0'; michael@0: if (*piece == '(') piece++; michael@0: if (*piece == '*' || *piece == '?') { michael@0: defcpdtable[j].def[defcpdtable[j].len++] = (FLAG) *piece; michael@0: } else if (*piece != '\0') { michael@0: int l = pHMgr->decode_flags(&conv, piece, af); michael@0: for (int k = 0; k < l; k++) defcpdtable[j].def[defcpdtable[j].len++] = conv[k]; michael@0: free(conv); michael@0: } michael@0: piece = par + 1; michael@0: } michael@0: } else { michael@0: defcpdtable[j].len = pHMgr->decode_flags(&(defcpdtable[j].def), piece, af); michael@0: } michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (!defcpdtable[j].len) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: numdefcpd = 0; michael@0: return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: michael@0: /* parse in the character map table */ michael@0: int AffixMgr::parse_maptable(char * line, FileMgr * af) michael@0: { michael@0: if (nummap != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: char * tp = line; michael@0: char * piece; michael@0: int i = 0; michael@0: int np = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { np++; break; } michael@0: case 1: { michael@0: nummap = atoi(piece); michael@0: if (nummap < 1) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: maptable = (mapentry *) malloc(nummap * sizeof(struct mapentry)); michael@0: if (!maptable) return 1; michael@0: np++; michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (np != 2) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: michael@0: /* now parse the nummap lines to read in the remainder of the table */ michael@0: char * nl; michael@0: for (int j=0; j < nummap; j++) { michael@0: if (!(nl = af->getline())) return 1; michael@0: mychomp(nl); michael@0: tp = nl; michael@0: i = 0; michael@0: maptable[j].set = NULL; michael@0: maptable[j].len = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { michael@0: if (strncmp(piece,"MAP",3) != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: nummap = 0; michael@0: return 1; michael@0: } michael@0: break; michael@0: } michael@0: case 1: { michael@0: int setn = 0; michael@0: maptable[j].len = strlen(piece); michael@0: maptable[j].set = (char **) malloc(maptable[j].len * sizeof(char*)); michael@0: if (!maptable[j].set) return 1; michael@0: for (int k = 0; k < maptable[j].len; k++) { michael@0: int chl = 1; michael@0: int chb = k; michael@0: if (piece[k] == '(') { michael@0: char * parpos = strchr(piece + k, ')'); michael@0: if (parpos != NULL) { michael@0: chb = k + 1; michael@0: chl = (int)(parpos - piece) - k - 1; michael@0: k = k + chl + 1; michael@0: } michael@0: } else { michael@0: if (utf8 && (piece[k] & 0xc0) == 0xc0) { michael@0: for (k++; utf8 && (piece[k] & 0xc0) == 0x80; k++); michael@0: chl = k - chb; michael@0: k--; michael@0: } michael@0: } michael@0: maptable[j].set[setn] = (char *) malloc(chl + 1); michael@0: if (!maptable[j].set[setn]) return 1; michael@0: strncpy(maptable[j].set[setn], piece + chb, chl); michael@0: maptable[j].set[setn][chl] = '\0'; michael@0: setn++; michael@0: } michael@0: maptable[j].len = setn; michael@0: break; } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (!maptable[j].set || !maptable[j].len) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: nummap = 0; michael@0: return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: /* parse in the word breakpoint table */ michael@0: int AffixMgr::parse_breaktable(char * line, FileMgr * af) michael@0: { michael@0: if (numbreak > -1) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple table definitions\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: char * tp = line; michael@0: char * piece; michael@0: int i = 0; michael@0: int np = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { np++; break; } michael@0: case 1: { michael@0: numbreak = atoi(piece); michael@0: if (numbreak < 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: if (numbreak == 0) return 0; michael@0: breaktable = (char **) malloc(numbreak * sizeof(char *)); michael@0: if (!breaktable) return 1; michael@0: np++; michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (np != 2) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); michael@0: return 1; michael@0: } michael@0: michael@0: /* now parse the numbreak lines to read in the remainder of the table */ michael@0: char * nl; michael@0: for (int j=0; j < numbreak; j++) { michael@0: if (!(nl = af->getline())) return 1; michael@0: mychomp(nl); michael@0: tp = nl; michael@0: i = 0; michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: case 0: { michael@0: if (strncmp(piece,"BREAK",5) != 0) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: numbreak = 0; michael@0: return 1; michael@0: } michael@0: break; michael@0: } michael@0: case 1: { michael@0: breaktable[j] = mystrdup(piece); michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: if (!breaktable) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: table is corrupt\n", af->getlinenum()); michael@0: numbreak = 0; michael@0: return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: void AffixMgr::reverse_condition(char * piece) { michael@0: int neg = 0; michael@0: for (char * k = piece + strlen(piece) - 1; k >= piece; k--) { michael@0: switch(*k) { michael@0: case '[': { michael@0: if (neg) *(k+1) = '['; else *k = ']'; michael@0: break; michael@0: } michael@0: case ']': { michael@0: *k = '['; michael@0: if (neg) *(k+1) = '^'; michael@0: neg = 0; michael@0: break; michael@0: } michael@0: case '^': { michael@0: if (*(k+1) == ']') neg = 1; else *(k+1) = *k; michael@0: break; michael@0: } michael@0: default: { michael@0: if (neg) *(k+1) = *k; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: int AffixMgr::parse_affix(char * line, const char at, FileMgr * af, char * dupflags) michael@0: { michael@0: int numents = 0; // number of affentry structures to parse michael@0: michael@0: unsigned short aflag = 0; // affix char identifier michael@0: michael@0: char ff=0; michael@0: std::vector affentries; michael@0: michael@0: char * tp = line; michael@0: char * nl = line; michael@0: char * piece; michael@0: int i = 0; michael@0: michael@0: // checking lines with bad syntax michael@0: #ifdef DEBUG michael@0: int basefieldnum = 0; michael@0: #endif michael@0: michael@0: // split affix header line into pieces michael@0: michael@0: int np = 0; michael@0: michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: // piece 1 - is type of affix michael@0: case 0: { np++; break; } michael@0: michael@0: // piece 2 - is affix char michael@0: case 1: { michael@0: np++; michael@0: aflag = pHMgr->decode_flag(piece); michael@0: if (((at == 'S') && (dupflags[aflag] & dupSFX)) || michael@0: ((at == 'P') && (dupflags[aflag] & dupPFX))) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: multiple definitions of an affix flag\n", michael@0: af->getlinenum()); michael@0: // return 1; XXX permissive mode for bad dictionaries michael@0: } michael@0: dupflags[aflag] += (char) ((at == 'S') ? dupSFX : dupPFX); michael@0: break; michael@0: } michael@0: // piece 3 - is cross product indicator michael@0: case 2: { np++; if (*piece == 'Y') ff = aeXPRODUCT; break; } michael@0: michael@0: // piece 4 - is number of affentries michael@0: case 3: { michael@0: np++; michael@0: numents = atoi(piece); michael@0: if (numents == 0) { michael@0: char * err = pHMgr->encode_flag(aflag); michael@0: if (err) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: bad entry number\n", michael@0: af->getlinenum()); michael@0: free(err); michael@0: } michael@0: return 1; michael@0: } michael@0: affentries.resize(numents); michael@0: affentries[0].opts = ff; michael@0: if (utf8) affentries[0].opts += aeUTF8; michael@0: if (pHMgr->is_aliasf()) affentries[0].opts += aeALIASF; michael@0: if (pHMgr->is_aliasm()) affentries[0].opts += aeALIASM; michael@0: affentries[0].aflag = aflag; michael@0: } michael@0: michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: // check to make sure we parsed enough pieces michael@0: if (np != 4) { michael@0: char * err = pHMgr->encode_flag(aflag); michael@0: if (err) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing data\n", af->getlinenum()); michael@0: free(err); michael@0: } michael@0: return 1; michael@0: } michael@0: michael@0: // now parse numents affentries for this affix michael@0: std::vector::iterator start = affentries.begin(); michael@0: std::vector::iterator end = affentries.end(); michael@0: for (std::vector::iterator entry = start; entry != end; ++entry) { michael@0: if (!(nl = af->getline())) return 1; michael@0: mychomp(nl); michael@0: tp = nl; michael@0: i = 0; michael@0: np = 0; michael@0: michael@0: // split line into pieces michael@0: piece = mystrsep(&tp, 0); michael@0: while (piece) { michael@0: if (*piece != '\0') { michael@0: switch(i) { michael@0: // piece 1 - is type michael@0: case 0: { michael@0: np++; michael@0: if (entry != start) entry->opts = start->opts & michael@0: (char) (aeXPRODUCT + aeUTF8 + aeALIASF + aeALIASM); michael@0: break; michael@0: } michael@0: michael@0: // piece 2 - is affix char michael@0: case 1: { michael@0: np++; michael@0: if (pHMgr->decode_flag(piece) != aflag) { michael@0: char * err = pHMgr->encode_flag(aflag); michael@0: if (err) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n", michael@0: af->getlinenum(), err); michael@0: free(err); michael@0: } michael@0: return 1; michael@0: } michael@0: michael@0: if (entry != start) entry->aflag = start->aflag; michael@0: break; michael@0: } michael@0: michael@0: // piece 3 - is string to strip or 0 for null michael@0: case 2: { michael@0: np++; michael@0: if (complexprefixes) { michael@0: if (utf8) reverseword_utf(piece); else reverseword(piece); michael@0: } michael@0: entry->strip = mystrdup(piece); michael@0: entry->stripl = (unsigned char) strlen(entry->strip); michael@0: if (strcmp(entry->strip,"0") == 0) { michael@0: free(entry->strip); michael@0: entry->strip=mystrdup(""); michael@0: entry->stripl = 0; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: // piece 4 - is affix string or 0 for null michael@0: case 3: { michael@0: char * dash; michael@0: entry->morphcode = NULL; michael@0: entry->contclass = NULL; michael@0: entry->contclasslen = 0; michael@0: np++; michael@0: dash = strchr(piece, '/'); michael@0: if (dash) { michael@0: *dash = '\0'; michael@0: michael@0: if (ignorechars) { michael@0: if (utf8) { michael@0: remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len); michael@0: } else { michael@0: remove_ignored_chars(piece,ignorechars); michael@0: } michael@0: } michael@0: michael@0: if (complexprefixes) { michael@0: if (utf8) reverseword_utf(piece); else reverseword(piece); michael@0: } michael@0: entry->appnd = mystrdup(piece); michael@0: michael@0: if (pHMgr->is_aliasf()) { michael@0: int index = atoi(dash + 1); michael@0: entry->contclasslen = (unsigned short) pHMgr->get_aliasf(index, &(entry->contclass), af); michael@0: if (!entry->contclasslen) HUNSPELL_WARNING(stderr, "error: bad affix flag alias: \"%s\"\n", dash+1); michael@0: } else { michael@0: entry->contclasslen = (unsigned short) pHMgr->decode_flags(&(entry->contclass), dash + 1, af); michael@0: flag_qsort(entry->contclass, 0, entry->contclasslen); michael@0: } michael@0: *dash = '/'; michael@0: michael@0: havecontclass = 1; michael@0: for (unsigned short _i = 0; _i < entry->contclasslen; _i++) { michael@0: contclasses[(entry->contclass)[_i]] = 1; michael@0: } michael@0: } else { michael@0: if (ignorechars) { michael@0: if (utf8) { michael@0: remove_ignored_chars_utf(piece, ignorechars_utf16, ignorechars_utf16_len); michael@0: } else { michael@0: remove_ignored_chars(piece,ignorechars); michael@0: } michael@0: } michael@0: michael@0: if (complexprefixes) { michael@0: if (utf8) reverseword_utf(piece); else reverseword(piece); michael@0: } michael@0: entry->appnd = mystrdup(piece); michael@0: } michael@0: michael@0: entry->appndl = (unsigned char) strlen(entry->appnd); michael@0: if (strcmp(entry->appnd,"0") == 0) { michael@0: free(entry->appnd); michael@0: entry->appnd=mystrdup(""); michael@0: entry->appndl = 0; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: // piece 5 - is the conditions descriptions michael@0: case 4: { michael@0: np++; michael@0: if (complexprefixes) { michael@0: if (utf8) reverseword_utf(piece); else reverseword(piece); michael@0: reverse_condition(piece); michael@0: } michael@0: if (entry->stripl && (strcmp(piece, ".") != 0) && michael@0: redundant_condition(at, entry->strip, entry->stripl, piece, af->getlinenum())) michael@0: strcpy(piece, "."); michael@0: if (at == 'S') { michael@0: reverseword(piece); michael@0: reverse_condition(piece); michael@0: } michael@0: if (encodeit(*entry, piece)) return 1; michael@0: break; michael@0: } michael@0: michael@0: case 5: { michael@0: np++; michael@0: if (pHMgr->is_aliasm()) { michael@0: int index = atoi(piece); michael@0: entry->morphcode = pHMgr->get_aliasm(index); michael@0: } else { michael@0: if (complexprefixes) { // XXX - fix me for morph. gen. michael@0: if (utf8) reverseword_utf(piece); else reverseword(piece); michael@0: } michael@0: // add the remaining of the line michael@0: if (*tp) { michael@0: *(tp - 1) = ' '; michael@0: tp = tp + strlen(tp); michael@0: } michael@0: entry->morphcode = mystrdup(piece); michael@0: if (!entry->morphcode) return 1; michael@0: } michael@0: break; michael@0: } michael@0: default: break; michael@0: } michael@0: i++; michael@0: } michael@0: piece = mystrsep(&tp, 0); michael@0: } michael@0: // check to make sure we parsed enough pieces michael@0: if (np < 4) { michael@0: char * err = pHMgr->encode_flag(aflag); michael@0: if (err) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: affix %s is corrupt\n", michael@0: af->getlinenum(), err); michael@0: free(err); michael@0: } michael@0: return 1; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: // detect unnecessary fields, excepting comments michael@0: if (basefieldnum) { michael@0: int fieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6); michael@0: if (fieldnum != basefieldnum) michael@0: HUNSPELL_WARNING(stderr, "warning: line %d: bad field number\n", af->getlinenum()); michael@0: } else { michael@0: basefieldnum = !(entry->morphcode) ? 5 : ((*(entry->morphcode)=='#') ? 5 : 6); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: // now create SfxEntry or PfxEntry objects and use links to michael@0: // build an ordered (sorted by affix string) list michael@0: for (std::vector::iterator entry = start; entry != end; ++entry) { michael@0: if (at == 'P') { michael@0: PfxEntry * pfxptr = new PfxEntry(this,&(*entry)); michael@0: build_pfxtree(pfxptr); michael@0: } else { michael@0: SfxEntry * sfxptr = new SfxEntry(this,&(*entry)); michael@0: build_sfxtree(sfxptr); michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: int AffixMgr::redundant_condition(char ft, char * strip, int stripl, const char * cond, int linenum) { michael@0: int condl = strlen(cond); michael@0: int i; michael@0: int j; michael@0: int neg; michael@0: int in; michael@0: if (ft == 'P') { // prefix michael@0: if (strncmp(strip, cond, condl) == 0) return 1; michael@0: if (utf8) { michael@0: } else { michael@0: for (i = 0, j = 0; (i < stripl) && (j < condl); i++, j++) { michael@0: if (cond[j] != '[') { michael@0: if (cond[j] != strip[i]) { michael@0: HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); michael@0: return 0; michael@0: } michael@0: } else { michael@0: neg = (cond[j+1] == '^') ? 1 : 0; michael@0: in = 0; michael@0: do { michael@0: j++; michael@0: if (strip[i] == cond[j]) in = 1; michael@0: } while ((j < (condl - 1)) && (cond[j] != ']')); michael@0: if (j == (condl - 1) && (cond[j] != ']')) { michael@0: HUNSPELL_WARNING(stderr, "error: line %d: missing ] in condition:\n%s\n", linenum, cond); michael@0: return 0; michael@0: } michael@0: if ((!neg && !in) || (neg && in)) { michael@0: HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); michael@0: return 0; michael@0: } michael@0: } michael@0: } michael@0: if (j >= condl) return 1; michael@0: } michael@0: } else { // suffix michael@0: if ((stripl >= condl) && strcmp(strip + stripl - condl, cond) == 0) return 1; michael@0: if (utf8) { michael@0: } else { michael@0: for (i = stripl - 1, j = condl - 1; (i >= 0) && (j >= 0); i--, j--) { michael@0: if (cond[j] != ']') { michael@0: if (cond[j] != strip[i]) { michael@0: HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); michael@0: return 0; michael@0: } michael@0: } else { michael@0: in = 0; michael@0: do { michael@0: j--; michael@0: if (strip[i] == cond[j]) in = 1; michael@0: } while ((j > 0) && (cond[j] != '[')); michael@0: if ((j == 0) && (cond[j] != '[')) { michael@0: HUNSPELL_WARNING(stderr, "error: line: %d: missing ] in condition:\n%s\n", linenum, cond); michael@0: return 0; michael@0: } michael@0: neg = (cond[j+1] == '^') ? 1 : 0; michael@0: if ((!neg && !in) || (neg && in)) { michael@0: HUNSPELL_WARNING(stderr, "warning: line %d: incompatible stripping characters and condition\n", linenum); michael@0: return 0; michael@0: } michael@0: } michael@0: } michael@0: if (j < 0) return 1; michael@0: } michael@0: } michael@0: return 0; michael@0: }