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