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