~ubuntu-branches/debian/sid/calibre/sid

« back to all changes in this revision

Viewing changes to src/hunspell/affentry.cxx

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2014-02-02 10:46:11 UTC
  • mfrom: (1.5.7)
  • Revision ID: package-import@ubuntu.com-20140202104611-c2tlt43ldklc28k4
Tags: 1.22.0+dfsg-1
* New upstream release.
* debian/copyright: Update to 1.0 copyright standard. Thanks Felix Gruber!
  (LP: #737343)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "license.hunspell"
 
2
#include "license.myspell"
 
3
 
 
4
#include <stdlib.h>
 
5
#include <string.h>
 
6
#include <stdio.h>
 
7
#include <ctype.h>
 
8
 
 
9
#include "affentry.hxx"
 
10
#include "csutil.hxx"
 
11
 
 
12
PfxEntry::PfxEntry(AffixMgr* pmgr, affentry* dp)
 
13
{
 
14
  // register affix manager
 
15
  pmyMgr = pmgr;
 
16
 
 
17
  // set up its initial values
 
18
 
 
19
  aflag = dp->aflag;         // flag
 
20
  strip = dp->strip;         // string to strip
 
21
  appnd = dp->appnd;         // string to append
 
22
  stripl = dp->stripl;       // length of strip string
 
23
  appndl = dp->appndl;       // length of append string
 
24
  numconds = dp->numconds;   // length of the condition
 
25
  opts = dp->opts;           // cross product flag
 
26
  // then copy over all of the conditions
 
27
  if (opts & aeLONGCOND) {
 
28
    memcpy(c.conds, dp->c.l.conds1, MAXCONDLEN_1);
 
29
    c.l.conds2 = dp->c.l.conds2;
 
30
  } else memcpy(c.conds, dp->c.conds, MAXCONDLEN);
 
31
  next = NULL;
 
32
  nextne = NULL;
 
33
  nexteq = NULL;
 
34
  morphcode = dp->morphcode;
 
35
  contclass = dp->contclass;
 
36
  contclasslen = dp->contclasslen;
 
37
}
 
38
 
 
39
 
 
40
PfxEntry::~PfxEntry()
 
41
{
 
42
    aflag = 0;
 
43
    if (appnd) free(appnd);
 
44
    if (strip) free(strip);
 
45
    pmyMgr = NULL;
 
46
    appnd = NULL;
 
47
    strip = NULL;
 
48
    if (opts & aeLONGCOND) free(c.l.conds2);
 
49
    if (morphcode && !(opts & aeALIASM)) free(morphcode);
 
50
    if (contclass && !(opts & aeALIASF)) free(contclass);
 
51
}
 
52
 
 
53
// add prefix to this word assuming conditions hold
 
54
char * PfxEntry::add(const char * word, int len)
 
55
{
 
56
    char tword[MAXWORDUTF8LEN + 4];
 
57
 
 
58
    if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) && 
 
59
       (len >= numconds) && test_condition(word) &&
 
60
       (!stripl || (strncmp(word, strip, stripl) == 0)) &&
 
61
       ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) {
 
62
    /* we have a match so add prefix */
 
63
              char * pp = tword;
 
64
              if (appndl) {
 
65
                  strcpy(tword,appnd);
 
66
                  pp += appndl;
 
67
               }
 
68
               strcpy(pp, (word + stripl));
 
69
               return mystrdup(tword);
 
70
     }
 
71
     return NULL;
 
72
}
 
73
 
 
74
inline char * PfxEntry::nextchar(char * p) {
 
75
    if (p) {
 
76
        p++;
 
77
        if (opts & aeLONGCOND) {
 
78
            // jump to the 2nd part of the condition
 
79
            if (p == c.conds + MAXCONDLEN_1) return c.l.conds2;
 
80
        // end of the MAXCONDLEN length condition
 
81
        } else if (p == c.conds + MAXCONDLEN) return NULL;
 
82
        return *p ? p : NULL;
 
83
    }
 
84
    return NULL;
 
85
}
 
86
 
 
87
inline int PfxEntry::test_condition(const char * st)
 
88
{
 
89
    const char * pos = NULL; // group with pos input position
 
90
    bool neg = false;        // complementer
 
91
    bool ingroup = false;    // character in the group
 
92
    if (numconds == 0) return 1;
 
93
    char * p = c.conds;
 
94
    while (1) {
 
95
      switch (*p) {
 
96
        case '\0': return 1;
 
97
        case '[': { 
 
98
                neg = false;
 
99
                ingroup = false;
 
100
                p = nextchar(p);
 
101
                pos = st; break;
 
102
            }
 
103
        case '^': { p = nextchar(p); neg = true; break; }
 
104
        case ']': { 
 
105
                if ((neg && ingroup) || (!neg && !ingroup)) return 0;
 
106
                pos = NULL;
 
107
                p = nextchar(p);
 
108
                // skip the next character
 
109
                if (!ingroup && *st) for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++);
 
110
                if (*st == '\0' && p) return 0; // word <= condition
 
111
                break;
 
112
            }
 
113
         case '.': if (!pos) { // dots are not metacharacters in groups: [.]
 
114
                p = nextchar(p);
 
115
                // skip the next character
 
116
                for (st++; (opts & aeUTF8) && (*st & 0xc0) == 0x80; st++);
 
117
                if (*st == '\0' && p) return 0; // word <= condition
 
118
                break;
 
119
            }
 
120
    default: {
 
121
                if (*st == *p) {
 
122
                    st++;
 
123
                    p = nextchar(p);
 
124
                    if ((opts & aeUTF8) && (*(st - 1) & 0x80)) { // multibyte
 
125
                        while (p && (*p & 0xc0) == 0x80) {       // character
 
126
                            if (*p != *st) {
 
127
                                if (!pos) return 0;
 
128
                                st = pos;
 
129
                                break;
 
130
                            }
 
131
                            p = nextchar(p);
 
132
                            st++;
 
133
                        }
 
134
                        if (pos && st != pos) {
 
135
                            ingroup = true;
 
136
                            while (p && *p != ']' && (p = nextchar(p)));
 
137
                        }
 
138
                    } else if (pos) {
 
139
                        ingroup = true;
 
140
                        while (p && *p != ']' && (p = nextchar(p)));
 
141
                    }
 
142
                } else if (pos) { // group
 
143
                    p = nextchar(p);
 
144
                } else return 0;
 
145
            }
 
146
      }
 
147
      if (!p) return 1;
 
148
    }
 
149
}
 
150
 
 
151
// check if this prefix entry matches
 
152
struct hentry * PfxEntry::checkword(const char * word, int len, char in_compound, const FLAG needflag)
 
153
{
 
154
    int                 tmpl;   // length of tmpword
 
155
    struct hentry *     he;     // hash entry of root word or NULL
 
156
    char                tmpword[MAXWORDUTF8LEN + 4];
 
157
 
 
158
    // on entry prefix is 0 length or already matches the beginning of the word.
 
159
    // So if the remaining root word has positive length
 
160
    // and if there are enough chars in root word and added back strip chars
 
161
    // to meet the number of characters conditions, then test it
 
162
 
 
163
     tmpl = len - appndl;
 
164
 
 
165
     if (tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) {
 
166
 
 
167
            // generate new root word by removing prefix and adding
 
168
            // back any characters that would have been stripped
 
169
 
 
170
            if (stripl) strcpy (tmpword, strip);
 
171
            strcpy ((tmpword + stripl), (word + appndl));
 
172
 
 
173
            // now make sure all of the conditions on characters
 
174
            // are met.  Please see the appendix at the end of
 
175
            // this file for more info on exactly what is being
 
176
            // tested
 
177
 
 
178
            // if all conditions are met then check if resulting
 
179
            // root word in the dictionary
 
180
 
 
181
            if (test_condition(tmpword)) {
 
182
                tmpl += stripl;
 
183
                if ((he = pmyMgr->lookup(tmpword)) != NULL) {
 
184
                   do {
 
185
                      if (TESTAFF(he->astr, aflag, he->alen) &&
 
186
                        // forbid single prefixes with needaffix flag
 
187
                        ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) &&
 
188
                        // needflag
 
189
                        ((!needflag) || TESTAFF(he->astr, needflag, he->alen) ||
 
190
                         (contclass && TESTAFF(contclass, needflag, contclasslen))))
 
191
                            return he;
 
192
                      he = he->next_homonym; // check homonyms
 
193
                   } while (he);
 
194
                }
 
195
 
 
196
                // prefix matched but no root word was found
 
197
                // if aeXPRODUCT is allowed, try again but now
 
198
                // ross checked combined with a suffix
 
199
 
 
200
                //if ((opts & aeXPRODUCT) && in_compound) {
 
201
                if ((opts & aeXPRODUCT)) {
 
202
                   he = pmyMgr->suffix_check(tmpword, tmpl, aeXPRODUCT, this, NULL,
 
203
                        0, NULL, FLAG_NULL, needflag, in_compound);
 
204
                   if (he) return he;
 
205
                }
 
206
            }
 
207
     }
 
208
    return NULL;
 
209
}
 
210
 
 
211
// check if this prefix entry matches
 
212
struct hentry * PfxEntry::check_twosfx(const char * word, int len,
 
213
    char in_compound, const FLAG needflag)
 
214
{
 
215
    int                 tmpl;   // length of tmpword
 
216
    struct hentry *     he;     // hash entry of root word or NULL
 
217
    char                tmpword[MAXWORDUTF8LEN + 4];
 
218
 
 
219
    // on entry prefix is 0 length or already matches the beginning of the word.
 
220
    // So if the remaining root word has positive length
 
221
    // and if there are enough chars in root word and added back strip chars
 
222
    // to meet the number of characters conditions, then test it
 
223
 
 
224
     tmpl = len - appndl;
 
225
 
 
226
     if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
 
227
        (tmpl + stripl >= numconds)) {
 
228
 
 
229
            // generate new root word by removing prefix and adding
 
230
            // back any characters that would have been stripped
 
231
 
 
232
            if (stripl) strcpy (tmpword, strip);
 
233
            strcpy ((tmpword + stripl), (word + appndl));
 
234
 
 
235
            // now make sure all of the conditions on characters
 
236
            // are met.  Please see the appendix at the end of
 
237
            // this file for more info on exactly what is being
 
238
            // tested
 
239
 
 
240
            // if all conditions are met then check if resulting
 
241
            // root word in the dictionary
 
242
 
 
243
            if (test_condition(tmpword)) {
 
244
                tmpl += stripl;
 
245
 
 
246
                // prefix matched but no root word was found
 
247
                // if aeXPRODUCT is allowed, try again but now
 
248
                // cross checked combined with a suffix
 
249
 
 
250
                if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
 
251
                   he = pmyMgr->suffix_check_twosfx(tmpword, tmpl, aeXPRODUCT, this, needflag);
 
252
                   if (he) return he;
 
253
                }
 
254
            }
 
255
     }
 
256
    return NULL;
 
257
}
 
258
 
 
259
// check if this prefix entry matches
 
260
char * PfxEntry::check_twosfx_morph(const char * word, int len,
 
261
         char in_compound, const FLAG needflag)
 
262
{
 
263
    int                 tmpl;   // length of tmpword
 
264
    char                tmpword[MAXWORDUTF8LEN + 4];
 
265
 
 
266
    // on entry prefix is 0 length or already matches the beginning of the word.
 
267
    // So if the remaining root word has positive length
 
268
    // and if there are enough chars in root word and added back strip chars
 
269
    // to meet the number of characters conditions, then test it
 
270
 
 
271
     tmpl = len - appndl;
 
272
 
 
273
     if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
 
274
        (tmpl + stripl >= numconds)) {
 
275
 
 
276
            // generate new root word by removing prefix and adding
 
277
            // back any characters that would have been stripped
 
278
 
 
279
            if (stripl) strcpy (tmpword, strip);
 
280
            strcpy ((tmpword + stripl), (word + appndl));
 
281
 
 
282
            // now make sure all of the conditions on characters
 
283
            // are met.  Please see the appendix at the end of
 
284
            // this file for more info on exactly what is being
 
285
            // tested
 
286
 
 
287
            // if all conditions are met then check if resulting
 
288
            // root word in the dictionary
 
289
 
 
290
            if (test_condition(tmpword)) {
 
291
                tmpl += stripl;
 
292
 
 
293
                // prefix matched but no root word was found
 
294
                // if aeXPRODUCT is allowed, try again but now
 
295
                // ross checked combined with a suffix
 
296
 
 
297
                if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
 
298
                    return pmyMgr->suffix_check_twosfx_morph(tmpword, tmpl,
 
299
                             aeXPRODUCT, this, needflag);
 
300
                }
 
301
            }
 
302
     }
 
303
    return NULL;
 
304
}
 
305
 
 
306
// check if this prefix entry matches
 
307
char * PfxEntry::check_morph(const char * word, int len, char in_compound, const FLAG needflag)
 
308
{
 
309
    int                 tmpl;   // length of tmpword
 
310
    struct hentry *     he;     // hash entry of root word or NULL
 
311
    char                tmpword[MAXWORDUTF8LEN + 4];
 
312
    char                result[MAXLNLEN];
 
313
    char * st;
 
314
 
 
315
    *result = '\0';
 
316
 
 
317
    // on entry prefix is 0 length or already matches the beginning of the word.
 
318
    // So if the remaining root word has positive length
 
319
    // and if there are enough chars in root word and added back strip chars
 
320
    // to meet the number of characters conditions, then test it
 
321
 
 
322
     tmpl = len - appndl;
 
323
 
 
324
     if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
 
325
        (tmpl + stripl >= numconds)) {
 
326
 
 
327
            // generate new root word by removing prefix and adding
 
328
            // back any characters that would have been stripped
 
329
 
 
330
            if (stripl) strcpy (tmpword, strip);
 
331
            strcpy ((tmpword + stripl), (word + appndl));
 
332
 
 
333
            // now make sure all of the conditions on characters
 
334
            // are met.  Please see the appendix at the end of
 
335
            // this file for more info on exactly what is being
 
336
            // tested
 
337
 
 
338
            // if all conditions are met then check if resulting
 
339
            // root word in the dictionary
 
340
 
 
341
            if (test_condition(tmpword)) {
 
342
                tmpl += stripl;
 
343
                if ((he = pmyMgr->lookup(tmpword)) != NULL) {
 
344
                    do {
 
345
                      if (TESTAFF(he->astr, aflag, he->alen) &&
 
346
                        // forbid single prefixes with needaffix flag
 
347
                        ! TESTAFF(contclass, pmyMgr->get_needaffix(), contclasslen) &&
 
348
                        // needflag
 
349
                        ((!needflag) || TESTAFF(he->astr, needflag, he->alen) ||
 
350
                         (contclass && TESTAFF(contclass, needflag, contclasslen)))) {
 
351
                            if (morphcode) {
 
352
                                mystrcat(result, " ", MAXLNLEN);
 
353
                                mystrcat(result, morphcode, MAXLNLEN);
 
354
                            } else mystrcat(result,getKey(), MAXLNLEN);
 
355
                            if (!HENTRY_FIND(he, MORPH_STEM)) {
 
356
                                mystrcat(result, " ", MAXLNLEN);
 
357
                                mystrcat(result, MORPH_STEM, MAXLNLEN);
 
358
                                mystrcat(result, HENTRY_WORD(he), MAXLNLEN);
 
359
                            }
 
360
                            // store the pointer of the hash entry
 
361
                            if (HENTRY_DATA(he)) {
 
362
                                mystrcat(result, " ", MAXLNLEN);
 
363
                                mystrcat(result, HENTRY_DATA2(he), MAXLNLEN);
 
364
                            } else {
 
365
                                // return with debug information
 
366
                                char * flag = pmyMgr->encode_flag(getFlag());
 
367
                                mystrcat(result, " ", MAXLNLEN);
 
368
                                mystrcat(result, MORPH_FLAG, MAXLNLEN);
 
369
                                mystrcat(result, flag, MAXLNLEN);
 
370
                                free(flag);
 
371
                            }
 
372
                            mystrcat(result, "\n", MAXLNLEN);
 
373
                      }
 
374
                      he = he->next_homonym;
 
375
                    } while (he);
 
376
                }
 
377
 
 
378
                // prefix matched but no root word was found
 
379
                // if aeXPRODUCT is allowed, try again but now
 
380
                // ross checked combined with a suffix
 
381
 
 
382
                if ((opts & aeXPRODUCT) && (in_compound != IN_CPD_BEGIN)) {
 
383
                   st = pmyMgr->suffix_check_morph(tmpword, tmpl, aeXPRODUCT, this,
 
384
                     FLAG_NULL, needflag);
 
385
                   if (st) {
 
386
                        mystrcat(result, st, MAXLNLEN);
 
387
                        free(st);
 
388
                   }
 
389
                }
 
390
            }
 
391
     }
 
392
    
 
393
    if (*result) return mystrdup(result);
 
394
    return NULL;
 
395
}
 
396
 
 
397
SfxEntry::SfxEntry(AffixMgr * pmgr, affentry* dp)
 
398
{
 
399
  // register affix manager
 
400
  pmyMgr = pmgr;
 
401
 
 
402
  // set up its initial values
 
403
  aflag = dp->aflag;         // char flag
 
404
  strip = dp->strip;         // string to strip
 
405
  appnd = dp->appnd;         // string to append
 
406
  stripl = dp->stripl;       // length of strip string
 
407
  appndl = dp->appndl;       // length of append string
 
408
  numconds = dp->numconds;   // length of the condition
 
409
  opts = dp->opts;           // cross product flag
 
410
 
 
411
  // then copy over all of the conditions
 
412
  if (opts & aeLONGCOND) {
 
413
    memcpy(c.l.conds1, dp->c.l.conds1, MAXCONDLEN_1);
 
414
    c.l.conds2 = dp->c.l.conds2;
 
415
  } else memcpy(c.conds, dp->c.conds, MAXCONDLEN);
 
416
 
 
417
  rappnd = myrevstrdup(appnd);
 
418
  morphcode = dp->morphcode;
 
419
  contclass = dp->contclass;
 
420
  contclasslen = dp->contclasslen;
 
421
}
 
422
 
 
423
 
 
424
SfxEntry::~SfxEntry()
 
425
{
 
426
    aflag = 0;
 
427
    if (appnd) free(appnd);
 
428
    if (rappnd) free(rappnd);
 
429
    if (strip) free(strip);
 
430
    pmyMgr = NULL;
 
431
    appnd = NULL;
 
432
    strip = NULL;
 
433
    if (opts & aeLONGCOND) free(c.l.conds2);
 
434
    if (morphcode && !(opts & aeALIASM)) free(morphcode);
 
435
    if (contclass && !(opts & aeALIASF)) free(contclass);
 
436
}
 
437
 
 
438
// add suffix to this word assuming conditions hold
 
439
char * SfxEntry::add(const char * word, int len)
 
440
{
 
441
    char                tword[MAXWORDUTF8LEN + 4];
 
442
 
 
443
     /* make sure all conditions match */
 
444
     if ((len > stripl || (len == 0 && pmyMgr->get_fullstrip())) &&
 
445
        (len >= numconds) && test_condition(word + len, word) &&
 
446
        (!stripl || (strcmp(word + len - stripl, strip) == 0)) &&
 
447
        ((MAXWORDUTF8LEN + 4) > (len + appndl - stripl))) {
 
448
              /* we have a match so add suffix */
 
449
              strcpy(tword,word);
 
450
              if (appndl) {
 
451
                  strcpy(tword + len - stripl, appnd);
 
452
              } else {
 
453
                  *(tword + len - stripl) = '\0';
 
454
              }
 
455
              return mystrdup(tword);
 
456
     }
 
457
     return NULL;
 
458
}
 
459
 
 
460
inline char * SfxEntry::nextchar(char * p) {
 
461
    if (p) {
 
462
        p++;
 
463
        if (opts & aeLONGCOND) {
 
464
            // jump to the 2nd part of the condition
 
465
            if (p == c.l.conds1 + MAXCONDLEN_1) return c.l.conds2;
 
466
        // end of the MAXCONDLEN length condition
 
467
        } else if (p == c.conds + MAXCONDLEN) return NULL;
 
468
        return *p ? p : NULL;
 
469
    }
 
470
    return NULL;
 
471
}
 
472
 
 
473
inline int SfxEntry::test_condition(const char * st, const char * beg)
 
474
{
 
475
    const char * pos = NULL;    // group with pos input position
 
476
    bool neg = false;           // complementer
 
477
    bool ingroup = false;       // character in the group
 
478
    if (numconds == 0) return 1;
 
479
    char * p = c.conds;
 
480
    st--;
 
481
    int i = 1;
 
482
    while (1) {
 
483
      switch (*p) {
 
484
        case '\0': return 1;
 
485
        case '[': { p = nextchar(p); pos = st; break; }
 
486
        case '^': { p = nextchar(p); neg = true; break; }
 
487
        case ']': { if (!neg && !ingroup) return 0;
 
488
                i++;
 
489
                // skip the next character
 
490
                if (!ingroup) {
 
491
                    for (; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--);
 
492
                    st--;
 
493
                }                    
 
494
                pos = NULL;
 
495
                neg = false;
 
496
                ingroup = false;
 
497
                p = nextchar(p);
 
498
                if (st < beg && p) return 0; // word <= condition
 
499
                break;
 
500
            }
 
501
        case '.': if (!pos) { // dots are not metacharacters in groups: [.]
 
502
                p = nextchar(p);
 
503
                // skip the next character
 
504
                for (st--; (opts & aeUTF8) && (st >= beg) && (*st & 0xc0) == 0x80; st--);
 
505
                if (st < beg) { // word <= condition
 
506
                    if (p) return 0; else return 1;
 
507
                }
 
508
                if ((opts & aeUTF8) && (*st & 0x80)) { // head of the UTF-8 character
 
509
                    st--;
 
510
                    if (st < beg) { // word <= condition
 
511
                        if (p) return 0; else return 1;
 
512
                    }
 
513
                }
 
514
                break;
 
515
            }
 
516
    default: {
 
517
                if (*st == *p) {
 
518
                    p = nextchar(p);
 
519
                    if ((opts & aeUTF8) && (*st & 0x80)) {
 
520
                        st--;
 
521
                        while (p && (st >= beg)) {
 
522
                            if (*p != *st) {
 
523
                                if (!pos) return 0;
 
524
                                st = pos;
 
525
                                break;
 
526
                            }
 
527
                            // first byte of the UTF-8 multibyte character
 
528
                            if ((*p & 0xc0) != 0x80) break;
 
529
                            p = nextchar(p);
 
530
                            st--;
 
531
                        }
 
532
                        if (pos && st != pos) {
 
533
                            if (neg) return 0;
 
534
                            else if (i == numconds) return 1;
 
535
                            ingroup = true;
 
536
                            while (p && *p != ']' && (p = nextchar(p)));
 
537
                            st--;
 
538
                        }
 
539
                        if (p && *p != ']') p = nextchar(p);
 
540
                    } else if (pos) {
 
541
                        if (neg) return 0;
 
542
                        else if (i == numconds) return 1;
 
543
                        ingroup = true;
 
544
                        while (p && *p != ']' && (p = nextchar(p)));
 
545
//                      if (p && *p != ']') p = nextchar(p);
 
546
                        st--;
 
547
                    }
 
548
                    if (!pos) {
 
549
                        i++;
 
550
                        st--;
 
551
                    }
 
552
                    if (st < beg && p && *p != ']') return 0; // word <= condition
 
553
                } else if (pos) { // group
 
554
                    p = nextchar(p);
 
555
                } else return 0;
 
556
            }
 
557
      }
 
558
      if (!p) return 1;
 
559
    }
 
560
}
 
561
 
 
562
// see if this suffix is present in the word
 
563
struct hentry * SfxEntry::checkword(const char * word, int len, int optflags,
 
564
    PfxEntry* ppfx, char ** wlst, int maxSug, int * ns, const FLAG cclass, const FLAG needflag,
 
565
    const FLAG badflag)
 
566
{
 
567
    int                 tmpl;            // length of tmpword
 
568
    struct hentry *     he;              // hash entry pointer
 
569
    unsigned char *     cp;
 
570
    char                tmpword[MAXWORDUTF8LEN + 4];
 
571
    PfxEntry* ep = ppfx;
 
572
 
 
573
    // if this suffix is being cross checked with a prefix
 
574
    // but it does not support cross products skip it
 
575
 
 
576
    if (((optflags & aeXPRODUCT) != 0) && ((opts & aeXPRODUCT) == 0))
 
577
        return NULL;
 
578
 
 
579
    // upon entry suffix is 0 length or already matches the end of the word.
 
580
    // So if the remaining root word has positive length
 
581
    // and if there are enough chars in root word and added back strip chars
 
582
    // to meet the number of characters conditions, then test it
 
583
 
 
584
    tmpl = len - appndl;
 
585
    // the second condition is not enough for UTF-8 strings
 
586
    // it checked in test_condition()
 
587
 
 
588
    if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
 
589
        (tmpl + stripl >= numconds)) {
 
590
 
 
591
            // generate new root word by removing suffix and adding
 
592
            // back any characters that would have been stripped or
 
593
            // or null terminating the shorter string
 
594
 
 
595
            strcpy (tmpword, word);
 
596
            cp = (unsigned char *)(tmpword + tmpl);
 
597
            if (stripl) {
 
598
                strcpy ((char *)cp, strip);
 
599
                tmpl += stripl;
 
600
                cp = (unsigned char *)(tmpword + tmpl);
 
601
            } else *cp = '\0';
 
602
 
 
603
            // now make sure all of the conditions on characters
 
604
            // are met.  Please see the appendix at the end of
 
605
            // this file for more info on exactly what is being
 
606
            // tested
 
607
 
 
608
            // if all conditions are met then check if resulting
 
609
            // root word in the dictionary
 
610
 
 
611
            if (test_condition((char *) cp, (char *) tmpword)) {
 
612
 
 
613
#ifdef SZOSZABLYA_POSSIBLE_ROOTS
 
614
                fprintf(stdout,"%s %s %c\n", word, tmpword, aflag);
 
615
#endif
 
616
                if ((he = pmyMgr->lookup(tmpword)) != NULL) {
 
617
                    do {
 
618
                        // check conditional suffix (enabled by prefix)
 
619
                        if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() &&
 
620
                                    TESTAFF(ep->getCont(), aflag, ep->getContLen()))) &&
 
621
                            (((optflags & aeXPRODUCT) == 0) ||
 
622
                            (ep && TESTAFF(he->astr, ep->getFlag(), he->alen)) ||
 
623
                             // enabled by prefix
 
624
                            ((contclass) && (ep && TESTAFF(contclass, ep->getFlag(), contclasslen)))
 
625
                            ) &&
 
626
                            // handle cont. class
 
627
                            ((!cclass) ||
 
628
                                ((contclass) && TESTAFF(contclass, cclass, contclasslen))
 
629
                            ) &&
 
630
                            // check only in compound homonyms (bad flags)
 
631
                            (!badflag || !TESTAFF(he->astr, badflag, he->alen)
 
632
                            ) &&
 
633
                            // handle required flag
 
634
                            ((!needflag) ||
 
635
                              (TESTAFF(he->astr, needflag, he->alen) ||
 
636
                              ((contclass) && TESTAFF(contclass, needflag, contclasslen)))
 
637
                            )
 
638
                        ) return he;
 
639
                        he = he->next_homonym; // check homonyms
 
640
                    } while (he);
 
641
 
 
642
                // obsolote stemming code (used only by the
 
643
                // experimental SuffixMgr:suggest_pos_stems)
 
644
                // store resulting root in wlst
 
645
                } else if (wlst && (*ns < maxSug)) {
 
646
                    int cwrd = 1;
 
647
                    for (int k=0; k < *ns; k++)
 
648
                        if (strcmp(tmpword, wlst[k]) == 0) cwrd = 0;
 
649
                    if (cwrd) {
 
650
                        wlst[*ns] = mystrdup(tmpword);
 
651
                        if (wlst[*ns] == NULL) {
 
652
                            for (int j=0; j<*ns; j++) free(wlst[j]);
 
653
                            *ns = -1;
 
654
                            return NULL;
 
655
                        }
 
656
                        (*ns)++;
 
657
                    }
 
658
                }
 
659
            }
 
660
    }
 
661
    return NULL;
 
662
}
 
663
 
 
664
// see if two-level suffix is present in the word
 
665
struct hentry * SfxEntry::check_twosfx(const char * word, int len, int optflags,
 
666
    PfxEntry* ppfx, const FLAG needflag)
 
667
{
 
668
    int                 tmpl;            // length of tmpword
 
669
    struct hentry *     he;              // hash entry pointer
 
670
    unsigned char *     cp;
 
671
    char                tmpword[MAXWORDUTF8LEN + 4];
 
672
    PfxEntry* ep = ppfx;
 
673
 
 
674
 
 
675
    // if this suffix is being cross checked with a prefix
 
676
    // but it does not support cross products skip it
 
677
 
 
678
    if ((optflags & aeXPRODUCT) != 0 &&  (opts & aeXPRODUCT) == 0)
 
679
        return NULL;
 
680
 
 
681
    // upon entry suffix is 0 length or already matches the end of the word.
 
682
    // So if the remaining root word has positive length
 
683
    // and if there are enough chars in root word and added back strip chars
 
684
    // to meet the number of characters conditions, then test it
 
685
 
 
686
    tmpl = len - appndl;
 
687
 
 
688
    if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
 
689
       (tmpl + stripl >= numconds)) {
 
690
 
 
691
            // generate new root word by removing suffix and adding
 
692
            // back any characters that would have been stripped or
 
693
            // or null terminating the shorter string
 
694
 
 
695
            strcpy (tmpword, word);
 
696
            cp = (unsigned char *)(tmpword + tmpl);
 
697
            if (stripl) {
 
698
                strcpy ((char *)cp, strip);
 
699
                tmpl += stripl;
 
700
                cp = (unsigned char *)(tmpword + tmpl);
 
701
            } else *cp = '\0';
 
702
 
 
703
            // now make sure all of the conditions on characters
 
704
            // are met.  Please see the appendix at the end of
 
705
            // this file for more info on exactly what is being
 
706
            // tested
 
707
 
 
708
            // if all conditions are met then recall suffix_check
 
709
 
 
710
            if (test_condition((char *) cp, (char *) tmpword)) {
 
711
                if (ppfx) {
 
712
                    // handle conditional suffix
 
713
                    if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen))
 
714
                        he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag);
 
715
                    else
 
716
                        he = pmyMgr->suffix_check(tmpword, tmpl, optflags, ppfx, NULL, 0, NULL, (FLAG) aflag, needflag);
 
717
                } else {
 
718
                    he = pmyMgr->suffix_check(tmpword, tmpl, 0, NULL, NULL, 0, NULL, (FLAG) aflag, needflag);
 
719
                }
 
720
                if (he) return he;
 
721
            }
 
722
    }
 
723
    return NULL;
 
724
}
 
725
 
 
726
// see if two-level suffix is present in the word
 
727
char * SfxEntry::check_twosfx_morph(const char * word, int len, int optflags,
 
728
    PfxEntry* ppfx, const FLAG needflag)
 
729
{
 
730
    int                 tmpl;            // length of tmpword
 
731
    unsigned char *     cp;
 
732
    char                tmpword[MAXWORDUTF8LEN + 4];
 
733
    PfxEntry* ep = ppfx;
 
734
    char * st;
 
735
 
 
736
    char result[MAXLNLEN];
 
737
 
 
738
    *result = '\0';
 
739
 
 
740
    // if this suffix is being cross checked with a prefix
 
741
    // but it does not support cross products skip it
 
742
 
 
743
    if ((optflags & aeXPRODUCT) != 0 &&  (opts & aeXPRODUCT) == 0)
 
744
        return NULL;
 
745
 
 
746
    // upon entry suffix is 0 length or already matches the end of the word.
 
747
    // So if the remaining root word has positive length
 
748
    // and if there are enough chars in root word and added back strip chars
 
749
    // to meet the number of characters conditions, then test it
 
750
 
 
751
    tmpl = len - appndl;
 
752
 
 
753
    if ((tmpl > 0 || (tmpl == 0 && pmyMgr->get_fullstrip())) &&
 
754
       (tmpl + stripl >= numconds)) {
 
755
 
 
756
            // generate new root word by removing suffix and adding
 
757
            // back any characters that would have been stripped or
 
758
            // or null terminating the shorter string
 
759
 
 
760
            strcpy (tmpword, word);
 
761
            cp = (unsigned char *)(tmpword + tmpl);
 
762
            if (stripl) {
 
763
                strcpy ((char *)cp, strip);
 
764
                tmpl += stripl;
 
765
                cp = (unsigned char *)(tmpword + tmpl);
 
766
            } else *cp = '\0';
 
767
 
 
768
            // now make sure all of the conditions on characters
 
769
            // are met.  Please see the appendix at the end of
 
770
            // this file for more info on exactly what is being
 
771
            // tested
 
772
 
 
773
            // if all conditions are met then recall suffix_check
 
774
 
 
775
            if (test_condition((char *) cp, (char *) tmpword)) {
 
776
                if (ppfx) {
 
777
                    // handle conditional suffix
 
778
                    if ((contclass) && TESTAFF(contclass, ep->getFlag(), contclasslen)) {
 
779
                        st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag);
 
780
                        if (st) {
 
781
                            if (ppfx->getMorph()) {
 
782
                                mystrcat(result, ppfx->getMorph(), MAXLNLEN);
 
783
                                mystrcat(result, " ", MAXLNLEN);
 
784
                            }
 
785
                            mystrcat(result,st, MAXLNLEN);
 
786
                            free(st);
 
787
                            mychomp(result);
 
788
                        }
 
789
                    } else {
 
790
                        st = pmyMgr->suffix_check_morph(tmpword, tmpl, optflags, ppfx, aflag, needflag);
 
791
                        if (st) {
 
792
                            mystrcat(result, st, MAXLNLEN);
 
793
                            free(st);
 
794
                            mychomp(result);
 
795
                        }
 
796
                    }
 
797
                } else {
 
798
                        st = pmyMgr->suffix_check_morph(tmpword, tmpl, 0, NULL, aflag, needflag);
 
799
                        if (st) {
 
800
                            mystrcat(result, st, MAXLNLEN);
 
801
                            free(st);
 
802
                            mychomp(result);
 
803
                        }
 
804
                }
 
805
                if (*result) return mystrdup(result);
 
806
            }
 
807
    }
 
808
    return NULL;
 
809
}
 
810
 
 
811
// get next homonym with same affix
 
812
struct hentry * SfxEntry::get_next_homonym(struct hentry * he, int optflags, PfxEntry* ppfx,
 
813
    const FLAG cclass, const FLAG needflag)
 
814
{
 
815
    PfxEntry* ep = ppfx;
 
816
    FLAG eFlag = ep ? ep->getFlag() : FLAG_NULL;
 
817
 
 
818
    while (he->next_homonym) {
 
819
        he = he->next_homonym;
 
820
        if ((TESTAFF(he->astr, aflag, he->alen) || (ep && ep->getCont() && TESTAFF(ep->getCont(), aflag, ep->getContLen()))) &&
 
821
                            ((optflags & aeXPRODUCT) == 0 ||
 
822
                            TESTAFF(he->astr, eFlag, he->alen) ||
 
823
                             // handle conditional suffix
 
824
                            ((contclass) && TESTAFF(contclass, eFlag, contclasslen))
 
825
                            ) &&
 
826
                            // handle cont. class
 
827
                            ((!cclass) ||
 
828
                                ((contclass) && TESTAFF(contclass, cclass, contclasslen))
 
829
                            ) &&
 
830
                            // handle required flag
 
831
                            ((!needflag) ||
 
832
                              (TESTAFF(he->astr, needflag, he->alen) ||
 
833
                              ((contclass) && TESTAFF(contclass, needflag, contclasslen)))
 
834
                            )
 
835
                        ) return he;
 
836
    }
 
837
    return NULL;
 
838
}
 
839
 
 
840
 
 
841
#if 0
 
842
 
 
843
Appendix:  Understanding Affix Code
 
844
 
 
845
 
 
846
An affix is either a  prefix or a suffix attached to root words to make 
 
847
other words.
 
848
 
 
849
Basically a Prefix or a Suffix is set of AffEntry objects
 
850
which store information about the prefix or suffix along 
 
851
with supporting routines to check if a word has a particular 
 
852
prefix or suffix or a combination.
 
853
 
 
854
The structure affentry is defined as follows:
 
855
 
 
856
struct affentry
 
857
{
 
858
   unsigned short aflag;    // ID used to represent the affix
 
859
   char * strip;            // string to strip before adding affix
 
860
   char * appnd;            // the affix string to add
 
861
   unsigned char stripl;    // length of the strip string
 
862
   unsigned char appndl;    // length of the affix string
 
863
   char numconds;           // the number of conditions that must be met
 
864
   char opts;               // flag: aeXPRODUCT- combine both prefix and suffix 
 
865
   char   conds[SETSIZE];   // array which encodes the conditions to be met
 
866
};
 
867
 
 
868
 
 
869
Here is a suffix borrowed from the en_US.aff file.  This file 
 
870
is whitespace delimited.
 
871
 
 
872
SFX D Y 4 
 
873
SFX D   0     e          d
 
874
SFX D   y     ied        [^aeiou]y
 
875
SFX D   0     ed         [^ey]
 
876
SFX D   0     ed         [aeiou]y
 
877
 
 
878
This information can be interpreted as follows:
 
879
 
 
880
In the first line has 4 fields
 
881
 
 
882
Field
 
883
-----
 
884
1     SFX - indicates this is a suffix
 
885
2     D   - is the name of the character flag which represents this suffix
 
886
3     Y   - indicates it can be combined with prefixes (cross product)
 
887
4     4   - indicates that sequence of 4 affentry structures are needed to
 
888
               properly store the affix information
 
889
 
 
890
The remaining lines describe the unique information for the 4 SfxEntry 
 
891
objects that make up this affix.  Each line can be interpreted
 
892
as follows: (note fields 1 and 2 are as a check against line 1 info)
 
893
 
 
894
Field
 
895
-----
 
896
1     SFX         - indicates this is a suffix
 
897
2     D           - is the name of the character flag for this affix
 
898
3     y           - the string of chars to strip off before adding affix
 
899
                         (a 0 here indicates the NULL string)
 
900
4     ied         - the string of affix characters to add
 
901
5     [^aeiou]y   - the conditions which must be met before the affix
 
902
                    can be applied
 
903
 
 
904
Field 5 is interesting.  Since this is a suffix, field 5 tells us that
 
905
there are 2 conditions that must be met.  The first condition is that 
 
906
the next to the last character in the word must *NOT* be any of the 
 
907
following "a", "e", "i", "o" or "u".  The second condition is that
 
908
the last character of the word must end in "y".
 
909
 
 
910
So how can we encode this information concisely and be able to 
 
911
test for both conditions in a fast manner?  The answer is found
 
912
but studying the wonderful ispell code of Geoff Kuenning, et.al. 
 
913
(now available under a normal BSD license).
 
914
 
 
915
If we set up a conds array of 256 bytes indexed (0 to 255) and access it
 
916
using a character (cast to an unsigned char) of a string, we have 8 bits
 
917
of information we can store about that character.  Specifically we
 
918
could use each bit to say if that character is allowed in any of the 
 
919
last (or first for prefixes) 8 characters of the word.
 
920
 
 
921
Basically, each character at one end of the word (up to the number 
 
922
of conditions) is used to index into the conds array and the resulting 
 
923
value found there says whether the that character is valid for a 
 
924
specific character position in the word.  
 
925
 
 
926
For prefixes, it does this by setting bit 0 if that char is valid 
 
927
in the first position, bit 1 if valid in the second position, and so on. 
 
928
 
 
929
If a bit is not set, then that char is not valid for that postion in the
 
930
word.
 
931
 
 
932
If working with suffixes bit 0 is used for the character closest 
 
933
to the front, bit 1 for the next character towards the end, ..., 
 
934
with bit numconds-1 representing the last char at the end of the string. 
 
935
 
 
936
Note: since entries in the conds[] are 8 bits, only 8 conditions 
 
937
(read that only 8 character positions) can be examined at one
 
938
end of a word (the beginning for prefixes and the end for suffixes.
 
939
 
 
940
So to make this clearer, lets encode the conds array values for the 
 
941
first two affentries for the suffix D described earlier.
 
942
 
 
943
 
 
944
  For the first affentry:    
 
945
     numconds = 1             (only examine the last character)
 
946
 
 
947
     conds['e'] =  (1 << 0)   (the word must end in an E)
 
948
     all others are all 0
 
949
 
 
950
  For the second affentry:
 
951
     numconds = 2             (only examine the last two characters)     
 
952
 
 
953
     conds[X] = conds[X] | (1 << 0)     (aeiou are not allowed)
 
954
         where X is all characters *but* a, e, i, o, or u
 
955
         
 
956
 
 
957
     conds['y'] = (1 << 1)     (the last char must be a y)
 
958
     all other bits for all other entries in the conds array are zero
 
959
 
 
960
 
 
961
#endif
 
962