PostgreSQL Source Code git master
Loading...
Searching...
No Matches
spell.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * spell.c
4 * Normalizing word with ISpell
5 *
6 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7 *
8 * Ispell dictionary
9 * -----------------
10 *
11 * Rules of dictionaries are defined in two files with .affix and .dict
12 * extensions. They are used by spell checker programs Ispell and Hunspell.
13 *
14 * An .affix file declares morphological rules to get a basic form of words.
15 * The format of an .affix file has different structure for Ispell and Hunspell
16 * dictionaries. The Hunspell format is more complicated. But when an .affix
17 * file is imported and compiled, it is stored in the same structure AffixNode.
18 *
19 * A .dict file stores a list of basic forms of words with references to
20 * affix rules. The format of a .dict file has the same structure for Ispell
21 * and Hunspell dictionaries.
22 *
23 * Compilation of a dictionary
24 * ---------------------------
25 *
26 * A compiled dictionary is stored in the IspellDict structure. Compilation of
27 * a dictionary is divided into the several steps:
28 * - NIImportDictionary() - stores each word of a .dict file in the
29 * temporary Spell field.
30 * - NIImportAffixes() - stores affix rules of an .affix file in the
31 * Affix field (not temporary) if an .affix file has the Ispell format.
32 * -> NIImportOOAffixes() - stores affix rules if an .affix file has the
33 * Hunspell format. The AffixData field is initialized if AF parameter
34 * is defined.
35 * - NISortDictionary() - builds a prefix tree (Trie) from the words list
36 * and stores it in the Dictionary field. The words list is got from the
37 * Spell field. The AffixData field is initialized if AF parameter is not
38 * defined.
39 * - NISortAffixes():
40 * - builds a list of compound affixes from the affix list and stores it
41 * in the CompoundAffix.
42 * - builds prefix trees (Trie) from the affix list for prefixes and suffixes
43 * and stores them in Suffix and Prefix fields.
44 * The affix list is got from the Affix field.
45 *
46 * Memory management
47 * -----------------
48 *
49 * The IspellDict structure has the Spell field which is used only in compile
50 * time. The Spell field stores a words list. It can take a lot of memory.
51 * Therefore when a dictionary is compiled this field is cleared by
52 * NIFinishBuild().
53 *
54 * All resources which should cleared by NIFinishBuild() is initialized using
55 * tmpalloc() and tmpalloc0().
56 *
57 * IDENTIFICATION
58 * src/backend/tsearch/spell.c
59 *
60 *-------------------------------------------------------------------------
61 */
62
63#include "postgres.h"
64
66#include "miscadmin.h"
67#include "tsearch/dicts/spell.h"
68#include "tsearch/ts_locale.h"
69#include "utils/formatting.h"
70#include "utils/memutils.h"
71
72
73/*
74 * Initialization requires a lot of memory that's not needed
75 * after the initialization is done. During initialization,
76 * CurrentMemoryContext is the long-lived memory context associated
77 * with the dictionary cache entry. We keep the short-lived stuff
78 * in the Conf->buildCxt context.
79 */
80#define tmpalloc(sz) MemoryContextAlloc(Conf->buildCxt, (sz))
81#define tmpalloc0(sz) MemoryContextAllocZero(Conf->buildCxt, (sz))
82
83/*
84 * Prepare for constructing an ISpell dictionary.
85 *
86 * The IspellDict struct is assumed to be zeroed when allocated.
87 */
88void
90{
91 /*
92 * The temp context is a child of CurTransactionContext, so that it will
93 * go away automatically on error.
94 */
96 "Ispell dictionary init context",
98}
99
100/*
101 * Clean up when dictionary construction is complete.
102 */
103void
105{
106 /* Release no-longer-needed temp memory */
107 MemoryContextDelete(Conf->buildCxt);
108 /* Just for cleanliness, zero the now-dangling pointers */
109 Conf->buildCxt = NULL;
110 Conf->Spell = NULL;
111 Conf->firstfree = NULL;
112 Conf->CompoundAffixFlags = NULL;
113}
114
115
116/*
117 * "Compact" palloc: allocate without extra palloc overhead.
118 *
119 * Since we have no need to free the ispell data items individually, there's
120 * not much value in the per-chunk overhead normally consumed by palloc.
121 * Getting rid of it is helpful since ispell can allocate a lot of small nodes.
122 *
123 * We currently pre-zero all data allocated this way, even though some of it
124 * doesn't need that. The cpalloc and cpalloc0 macros are just documentation
125 * to indicate which allocations actually require zeroing.
126 */
127#define COMPACT_ALLOC_CHUNK 8192 /* amount to get from palloc at once */
128#define COMPACT_MAX_REQ 1024 /* must be < COMPACT_ALLOC_CHUNK */
129
130static void *
132{
133 void *result;
134
135 /* Should only be called during init */
136 Assert(Conf->buildCxt != NULL);
137
138 /* No point in this for large chunks */
139 if (size > COMPACT_MAX_REQ)
140 return palloc0(size);
141
142 /* Keep everything maxaligned */
143 size = MAXALIGN(size);
144
145 /* Need more space? */
146 if (size > Conf->avail)
147 {
148 Conf->firstfree = palloc0(COMPACT_ALLOC_CHUNK);
149 Conf->avail = COMPACT_ALLOC_CHUNK;
150 }
151
152 result = Conf->firstfree;
153 Conf->firstfree += size;
154 Conf->avail -= size;
155
156 return result;
157}
158
159#define cpalloc(size) compact_palloc0(Conf, size)
160#define cpalloc0(size) compact_palloc0(Conf, size)
161
162static char *
164{
165 char *res = cpalloc(strlen(str) + 1);
166
167 strcpy(res, str);
168 return res;
169}
170
171
172/*
173 * Apply str_tolower(), producing a temporary result (in the buildCxt).
174 */
175static char *
176lowerstr_ctx(IspellDict *Conf, const char *src)
177{
179 char *dst;
180
184
185 return dst;
186}
187
188#define MAX_NORM 1024
189#define MAXNORMLEN 256
190
191#define STRNCMP(s,p) strncmp( (s), (p), strlen(p) )
192#define GETWCHAR(W,L,N,T) ( ((const uint8*)(W))[ ((T)==FF_PREFIX) ? (N) : ( (L) - 1 - (N) ) ] )
193#define GETCHAR(A,N,T) GETWCHAR( (A)->repl, (A)->replen, N, T )
194
195static const char *VoidString = "";
196
197static int
198cmpspell(const void *s1, const void *s2)
199{
200 return strcmp((*(SPELL *const *) s1)->word, (*(SPELL *const *) s2)->word);
201}
202
203static int
204cmpspellaffix(const void *s1, const void *s2)
205{
206 return strcmp((*(SPELL *const *) s1)->p.flag,
207 (*(SPELL *const *) s2)->p.flag);
208}
209
210static int
211cmpcmdflag(const void *f1, const void *f2)
212{
213 const CompoundAffixFlag *fv1 = f1;
214 const CompoundAffixFlag *fv2 = f2;
215
216 Assert(fv1->flagMode == fv2->flagMode);
217
218 if (fv1->flagMode == FM_NUM)
219 {
220 if (fv1->flag.i == fv2->flag.i)
221 return 0;
222
223 return (fv1->flag.i > fv2->flag.i) ? 1 : -1;
224 }
225
226 return strcmp(fv1->flag.s, fv2->flag.s);
227}
228
229static char *
230findchar(char *str, int c)
231{
232 while (*str)
233 {
234 if (t_iseq(str, c))
235 return str;
237 }
238
239 return NULL;
240}
241
242static char *
243findchar2(char *str, int c1, int c2)
244{
245 while (*str)
246 {
247 if (t_iseq(str, c1) || t_iseq(str, c2))
248 return str;
250 }
251
252 return NULL;
253}
254
255
256/* backward string compare for suffix tree operations */
257static int
258strbcmp(const unsigned char *s1, const unsigned char *s2)
259{
260 int l1 = strlen((const char *) s1) - 1,
261 l2 = strlen((const char *) s2) - 1;
262
263 while (l1 >= 0 && l2 >= 0)
264 {
265 if (s1[l1] < s2[l2])
266 return -1;
267 if (s1[l1] > s2[l2])
268 return 1;
269 l1--;
270 l2--;
271 }
272 if (l1 < l2)
273 return -1;
274 if (l1 > l2)
275 return 1;
276
277 return 0;
278}
279
280static int
281strbncmp(const unsigned char *s1, const unsigned char *s2, size_t count)
282{
283 int l1 = strlen((const char *) s1) - 1,
284 l2 = strlen((const char *) s2) - 1,
285 l = count;
286
287 while (l1 >= 0 && l2 >= 0 && l > 0)
288 {
289 if (s1[l1] < s2[l2])
290 return -1;
291 if (s1[l1] > s2[l2])
292 return 1;
293 l1--;
294 l2--;
295 l--;
296 }
297 if (l == 0)
298 return 0;
299 if (l1 < l2)
300 return -1;
301 if (l1 > l2)
302 return 1;
303 return 0;
304}
305
306/*
307 * Compares affixes.
308 * First compares the type of an affix. Prefixes should go before affixes.
309 * If types are equal then compares replaceable string.
310 */
311static int
312cmpaffix(const void *s1, const void *s2)
313{
314 const AFFIX *a1 = (const AFFIX *) s1;
315 const AFFIX *a2 = (const AFFIX *) s2;
316
317 if (a1->type < a2->type)
318 return -1;
319 if (a1->type > a2->type)
320 return 1;
321 if (a1->type == FF_PREFIX)
322 return strcmp(a1->repl, a2->repl);
323 else
324 return strbcmp((const unsigned char *) a1->repl,
325 (const unsigned char *) a2->repl);
326}
327
328/*
329 * Gets an affix flag from the set of affix flags (sflagset).
330 *
331 * Several flags can be stored in a single string. Flags can be represented by:
332 * - 1 character (FM_CHAR). A character may be Unicode.
333 * - 2 characters (FM_LONG). A character may be Unicode.
334 * - numbers from 1 to 65000 (FM_NUM).
335 *
336 * Depending on the flagMode an affix string can have the following format:
337 * - FM_CHAR: ABCD
338 * Here we have 4 flags: A, B, C and D
339 * - FM_LONG: ABCDE*
340 * Here we have 3 flags: AB, CD and E*
341 * - FM_NUM: 200,205,50
342 * Here we have 3 flags: 200, 205 and 50
343 *
344 * Conf: current dictionary.
345 * sflagset: the set of affix flags. Returns a reference to the start of a next
346 * affix flag.
347 * sflag: returns an affix flag from sflagset.
348 */
349static void
351{
352 int32 s;
353 char *next;
354 const char *sbuf = *sflagset;
355 int maxstep;
356 int clen;
357 bool stop = false;
358 bool met_comma = false;
359
360 maxstep = (Conf->flagMode == FM_LONG) ? 2 : 1;
361
362 while (**sflagset)
363 {
364 switch (Conf->flagMode)
365 {
366 case FM_LONG:
367 case FM_CHAR:
369 sflag += clen;
370
371 /* Go to start of the next flag */
372 *sflagset += clen;
373
374 /* Check if we get all characters of flag */
375 maxstep--;
376 stop = (maxstep == 0);
377 break;
378 case FM_NUM:
379 errno = 0;
380 s = strtol(*sflagset, &next, 10);
381 if (*sflagset == next || errno == ERANGE)
384 errmsg("invalid affix flag \"%s\"", *sflagset)));
388 errmsg("affix flag \"%s\" is out of range",
389 *sflagset)));
390 sflag += sprintf(sflag, "%0d", s);
391
392 /* Go to start of the next flag */
393 *sflagset = next;
394 while (**sflagset)
395 {
396 if (isdigit((unsigned char) **sflagset))
397 {
398 if (!met_comma)
401 errmsg("invalid affix flag \"%s\"",
402 *sflagset)));
403 break;
404 }
405 else if (t_iseq(*sflagset, ','))
406 {
407 if (met_comma)
410 errmsg("invalid affix flag \"%s\"",
411 *sflagset)));
412 met_comma = true;
413 }
414 else if (!isspace((unsigned char) **sflagset))
415 {
418 errmsg("invalid character in affix flag \"%s\"",
419 *sflagset)));
420 }
421
423 }
424 stop = true;
425 break;
426 default:
427 elog(ERROR, "unrecognized type of Conf->flagMode: %d",
428 Conf->flagMode);
429 }
430
431 if (stop)
432 break;
433 }
434
435 if (Conf->flagMode == FM_LONG && maxstep > 0)
438 errmsg("invalid affix flag \"%s\" with \"long\" flag value",
439 sbuf)));
440
441 *sflag = '\0';
442}
443
444/*
445 * Checks if the affix set Conf->AffixData[affix] contains affixflag.
446 * Conf->AffixData[affix] does not contain affixflag if this flag is not used
447 * actually by the .dict file.
448 *
449 * Conf: current dictionary.
450 * affix: index of the Conf->AffixData array.
451 * affixflag: the affix flag.
452 *
453 * Returns true if the string Conf->AffixData[affix] contains affixflag,
454 * otherwise returns false.
455 */
456static bool
457IsAffixFlagInUse(IspellDict *Conf, int affix, const char *affixflag)
458{
459 const char *flagcur;
460 char flag[BUFSIZ];
461
462 if (*affixflag == 0)
463 return true;
464
465 Assert(affix < Conf->nAffixData);
466
467 flagcur = Conf->AffixData[affix];
468
469 while (*flagcur)
470 {
472 /* Compare first affix flag in flagcur with affixflag */
473 if (strcmp(flag, affixflag) == 0)
474 return true;
475 }
476
477 /* Could not find affixflag */
478 return false;
479}
480
481/*
482 * Adds the new word into the temporary array Spell.
483 *
484 * Conf: current dictionary.
485 * word: new word.
486 * flag: set of affix flags. Single flag can be get by getNextFlagFromString().
487 */
488static void
489NIAddSpell(IspellDict *Conf, const char *word, const char *flag)
490{
491 if (Conf->nspell >= Conf->mspell)
492 {
493 if (Conf->mspell)
494 {
495 Conf->mspell *= 2;
496 Conf->Spell = (SPELL **) repalloc(Conf->Spell, Conf->mspell * sizeof(SPELL *));
497 }
498 else
499 {
500 Conf->mspell = 1024 * 20;
501 Conf->Spell = (SPELL **) tmpalloc(Conf->mspell * sizeof(SPELL *));
502 }
503 }
504 Conf->Spell[Conf->nspell] = (SPELL *) tmpalloc(SPELLHDRSZ + strlen(word) + 1);
505 strcpy(Conf->Spell[Conf->nspell]->word, word);
506 Conf->Spell[Conf->nspell]->p.flag = (*flag != '\0')
508 Conf->nspell++;
509}
510
511/*
512 * Imports dictionary into the temporary array Spell.
513 *
514 * Note caller must already have applied get_tsearch_config_filename.
515 *
516 * Conf: current dictionary.
517 * filename: path to the .dict file.
518 */
519void
521{
523 char *line;
524
528 errmsg("could not open dictionary file \"%s\": %m",
529 filename)));
530
531 while ((line = tsearch_readline(&trst)) != NULL)
532 {
533 char *s,
534 *pstr;
535
536 /* Set of affix flags */
537 const char *flag;
538
539 /* Extract flag from the line */
540 flag = NULL;
541 if ((s = findchar(line, '/')))
542 {
543 *s++ = '\0';
544 flag = s;
545 while (*s)
546 {
547 /* we allow only single encoded flags for faster works */
548 if (pg_mblen_cstr(s) == 1 && isprint((unsigned char) *s) && !isspace((unsigned char) *s))
549 s++;
550 else
551 {
552 *s = '\0';
553 break;
554 }
555 }
556 }
557 else
558 flag = "";
559
560 /* Remove trailing spaces */
561 s = line;
562 while (*s)
563 {
564 if (isspace((unsigned char) *s))
565 {
566 *s = '\0';
567 break;
568 }
569 s += pg_mblen_cstr(s);
570 }
571 pstr = lowerstr_ctx(Conf, line);
572
574 pfree(pstr);
575
576 pfree(line);
577 }
579}
580
581/*
582 * Searches a basic form of word in the prefix tree. This word was generated
583 * using an affix rule. This rule may not be presented in an affix set of
584 * a basic form of word.
585 *
586 * For example, we have the entry in the .dict file:
587 * meter/GMD
588 *
589 * The affix rule with the flag S:
590 * SFX S y ies [^aeiou]y
591 * is not presented here.
592 *
593 * The affix rule with the flag M:
594 * SFX M 0 's .
595 * is presented here.
596 *
597 * Conf: current dictionary.
598 * word: basic form of word.
599 * affixflag: affix flag, by which a basic form of word was generated.
600 * flag: compound flag used to compare with StopMiddle->compoundflag.
601 *
602 * Returns 1 if the word was found in the prefix tree, else returns 0.
603 */
604static int
605FindWord(IspellDict *Conf, const char *word, const char *affixflag, int flag)
606{
607 SPNode *node = Conf->Dictionary;
609 *StopHigh,
610 *StopMiddle;
611 const uint8 *ptr = (const uint8 *) word;
612
614
615 while (node && *ptr)
616 {
617 StopLow = node->data;
618 StopHigh = node->data + node->length;
619 while (StopLow < StopHigh)
620 {
621 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
622 if (StopMiddle->val == *ptr)
623 {
624 if (*(ptr + 1) == '\0' && StopMiddle->isword)
625 {
626 if (flag == 0)
627 {
628 /*
629 * The word can be formed only with another word. And
630 * in the flag parameter there is not a sign that we
631 * search compound words.
632 */
633 if (StopMiddle->compoundflag & FF_COMPOUNDONLY)
634 return 0;
635 }
636 else if ((flag & StopMiddle->compoundflag) == 0)
637 return 0;
638
639 /*
640 * Check if this affix rule is presented in the affix set
641 * with index StopMiddle->affix.
642 */
644 return 1;
645 }
646 node = StopMiddle->node;
647 ptr++;
648 break;
649 }
650 else if (StopMiddle->val < *ptr)
651 StopLow = StopMiddle + 1;
652 else
654 }
655 if (StopLow >= StopHigh)
656 break;
657 }
658 return 0;
659}
660
661/*
662 * Adds a new affix rule to the Affix field.
663 *
664 * Conf: current dictionary.
665 * flag: affix flag ('\' in the below example).
666 * flagflags: set of flags from the flagval field for this affix rule. This set
667 * is listed after '/' character in the added string (repl).
668 *
669 * For example L flag in the hunspell_sample.affix:
670 * SFX \ 0 Y/L [^Y]
671 *
672 * mask: condition for search ('[^Y]' in the above example).
673 * find: stripping characters from beginning (at prefix) or end (at suffix)
674 * of the word ('0' in the above example, 0 means that there is not
675 * stripping character).
676 * repl: adding string after stripping ('Y' in the above example).
677 * type: FF_SUFFIX or FF_PREFIX.
678 */
679static void
680NIAddAffix(IspellDict *Conf, const char *flag, char flagflags, const char *mask,
681 const char *find, const char *repl, int type)
682{
683 AFFIX *Affix;
684
685 if (Conf->naffixes >= Conf->maffixes)
686 {
687 if (Conf->maffixes)
688 {
689 Conf->maffixes *= 2;
690 Conf->Affix = (AFFIX *) repalloc(Conf->Affix, Conf->maffixes * sizeof(AFFIX));
691 }
692 else
693 {
694 Conf->maffixes = 16;
695 Conf->Affix = palloc_array(AFFIX, Conf->maffixes);
696 }
697 }
698
699 Affix = Conf->Affix + Conf->naffixes;
700
701 /* This affix rule can be applied for words with any ending */
702 if (strcmp(mask, ".") == 0 || *mask == '\0')
703 {
704 Affix->issimple = 1;
705 Affix->isregis = 0;
706 }
707 /* This affix rule will use regis to search word ending */
708 else if (RS_isRegis(mask))
709 {
710 Affix->issimple = 0;
711 Affix->isregis = 1;
712 RS_compile(&(Affix->reg.regis), (type == FF_SUFFIX),
713 *mask ? mask : VoidString);
714 }
715 /* This affix rule will use regex_t to search word ending */
716 else
717 {
718 int masklen;
719 int wmasklen;
720 int err;
722 char *tmask;
723
724 Affix->issimple = 0;
725 Affix->isregis = 0;
726 tmask = (char *) tmpalloc(strlen(mask) + 3);
727 if (type == FF_SUFFIX)
728 sprintf(tmask, "%s$", mask);
729 else
730 sprintf(tmask, "^%s", mask);
731
732 masklen = strlen(tmask);
733 wmask = (pg_wchar *) tmpalloc((masklen + 1) * sizeof(pg_wchar));
735
736 /*
737 * The regex and all internal state created by pg_regcomp are
738 * allocated in the dictionary's memory context, and will be freed
739 * automatically when it is destroyed.
740 */
745 if (err)
746 {
747 char errstr[100];
748
749 pg_regerror(err, Affix->reg.pregex, errstr, sizeof(errstr));
752 errmsg("invalid regular expression: %s", errstr)));
753 }
754 }
755
756 Affix->flagflags = flagflags;
757 if ((Affix->flagflags & FF_COMPOUNDONLY) || (Affix->flagflags & FF_COMPOUNDPERMITFLAG))
758 {
759 if ((Affix->flagflags & FF_COMPOUNDFLAG) == 0)
760 Affix->flagflags |= FF_COMPOUNDFLAG;
761 }
762 Affix->flag = cpstrdup(Conf, flag);
763 Affix->type = type;
764
765 Affix->find = (find && *find) ? cpstrdup(Conf, find) : VoidString;
766 if ((Affix->replen = strlen(repl)) > 0)
767 Affix->repl = cpstrdup(Conf, repl);
768 else
769 Affix->repl = VoidString;
770 Conf->naffixes++;
771}
772
773/* Parsing states for parse_affentry() and friends */
774#define PAE_WAIT_MASK 0
775#define PAE_INMASK 1
776#define PAE_WAIT_FIND 2
777#define PAE_INFIND 3
778#define PAE_WAIT_REPL 4
779#define PAE_INREPL 5
780#define PAE_WAIT_TYPE 6
781#define PAE_WAIT_FLAG 7
782
783/*
784 * Parse next space-separated field of an .affix file line.
785 *
786 * *str is the input pointer (will be advanced past field)
787 * next is where to copy the field value to, with null termination
788 *
789 * The buffer at "next" must be of size BUFSIZ; we truncate the input to fit.
790 *
791 * Returns true if we found a field, false if not.
792 */
793static bool
794get_nextfield(char **str, char *next)
795{
796 int state = PAE_WAIT_MASK;
797 int avail = BUFSIZ;
798
799 while (**str)
800 {
801 int clen = pg_mblen_cstr(*str);
802
803 if (state == PAE_WAIT_MASK)
804 {
805 if (t_iseq(*str, '#'))
806 return false;
807 else if (!isspace((unsigned char) **str))
808 {
809 if (clen < avail)
810 {
812 next += clen;
813 avail -= clen;
814 }
816 }
817 }
818 else /* state == PAE_INMASK */
819 {
820 if (isspace((unsigned char) **str))
821 {
822 *next = '\0';
823 return true;
824 }
825 else
826 {
827 if (clen < avail)
828 {
830 next += clen;
831 avail -= clen;
832 }
833 }
834 }
835 *str += clen;
836 }
837
838 *next = '\0';
839
840 return (state == PAE_INMASK); /* OK if we got a nonempty field */
841}
842
843/*
844 * Parses entry of an .affix file of MySpell or Hunspell format.
845 *
846 * An .affix file entry has the following format:
847 * - header
848 * <type> <flag> <cross_flag> <flag_count>
849 * - fields after header:
850 * <type> <flag> <find> <replace> <mask>
851 *
852 * str is the input line
853 * field values are returned to type etc, which must be buffers of size BUFSIZ.
854 *
855 * Returns number of fields found; any omitted fields are set to empty strings.
856 */
857static int
858parse_ooaffentry(char *str, char *type, char *flag, char *find,
859 char *repl, char *mask)
860{
861 int state = PAE_WAIT_TYPE;
862 int fields_read = 0;
863 bool valid = false;
864
865 *type = *flag = *find = *repl = *mask = '\0';
866
867 while (*str)
868 {
869 switch (state)
870 {
871 case PAE_WAIT_TYPE:
872 valid = get_nextfield(&str, type);
874 break;
875 case PAE_WAIT_FLAG:
876 valid = get_nextfield(&str, flag);
878 break;
879 case PAE_WAIT_FIND:
880 valid = get_nextfield(&str, find);
882 break;
883 case PAE_WAIT_REPL:
884 valid = get_nextfield(&str, repl);
886 break;
887 case PAE_WAIT_MASK:
888 valid = get_nextfield(&str, mask);
889 state = -1; /* force loop exit */
890 break;
891 default:
892 elog(ERROR, "unrecognized state in parse_ooaffentry: %d",
893 state);
894 break;
895 }
896 if (valid)
897 fields_read++;
898 else
899 break; /* early EOL */
900 if (state < 0)
901 break; /* got all fields */
902 }
903
904 return fields_read;
905}
906
907/*
908 * Parses entry of an .affix file of Ispell format
909 *
910 * An .affix file entry has the following format:
911 * <mask> > [-<find>,]<replace>
912 */
913static bool
914parse_affentry(char *str, char *mask, char *find, char *repl)
915{
916 int state = PAE_WAIT_MASK;
917 char *pmask = mask,
918 *pfind = find,
919 *prepl = repl;
920
921 *mask = *find = *repl = '\0';
922
923 while (*str)
924 {
925 int clen = pg_mblen_cstr(str);
926
927 if (state == PAE_WAIT_MASK)
928 {
929 if (t_iseq(str, '#'))
930 return false;
931 else if (!isspace((unsigned char) *str))
932 {
935 }
936 }
937 else if (state == PAE_INMASK)
938 {
939 if (t_iseq(str, '>'))
940 {
941 *pmask = '\0';
943 }
944 else if (!isspace((unsigned char) *str))
945 {
947 }
948 }
949 else if (state == PAE_WAIT_FIND)
950 {
951 if (t_iseq(str, '-'))
952 {
954 }
955 else if (t_isalpha_cstr(str) || t_iseq(str, '\'') /* english 's */ )
956 {
959 }
960 else if (!isspace((unsigned char) *str))
963 errmsg("syntax error")));
964 }
965 else if (state == PAE_INFIND)
966 {
967 if (t_iseq(str, ','))
968 {
969 *pfind = '\0';
971 }
972 else if (t_isalpha_cstr(str))
973 {
975 }
976 else if (!isspace((unsigned char) *str))
979 errmsg("syntax error")));
980 }
981 else if (state == PAE_WAIT_REPL)
982 {
983 if (t_iseq(str, '-'))
984 {
985 break; /* void repl */
986 }
987 else if (t_isalpha_cstr(str))
988 {
991 }
992 else if (!isspace((unsigned char) *str))
995 errmsg("syntax error")));
996 }
997 else if (state == PAE_INREPL)
998 {
999 if (t_iseq(str, '#'))
1000 {
1001 *prepl = '\0';
1002 break;
1003 }
1004 else if (t_isalpha_cstr(str))
1005 {
1007 }
1008 else if (!isspace((unsigned char) *str))
1009 ereport(ERROR,
1011 errmsg("syntax error")));
1012 }
1013 else
1014 elog(ERROR, "unrecognized state in parse_affentry: %d", state);
1015
1016 str += clen;
1017 }
1018
1019 *pmask = *pfind = *prepl = '\0';
1020
1021 return (*mask && (*find || *repl));
1022}
1023
1024/*
1025 * Sets a Hunspell options depending on flag type.
1026 */
1027static void
1029 char *s, uint32 val)
1030{
1031 if (Conf->flagMode == FM_NUM)
1032 {
1033 char *next;
1034 int i;
1035
1036 errno = 0;
1037 i = strtol(s, &next, 10);
1038 if (s == next || errno == ERANGE)
1039 ereport(ERROR,
1041 errmsg("invalid affix flag \"%s\"", s)));
1043 ereport(ERROR,
1045 errmsg("affix flag \"%s\" is out of range", s)));
1046
1047 entry->flag.i = i;
1048 }
1049 else
1050 entry->flag.s = cpstrdup(Conf, s);
1051
1052 entry->flagMode = Conf->flagMode;
1053 entry->value = val;
1054}
1055
1056/*
1057 * Sets up a correspondence for the affix parameter with the affix flag.
1058 *
1059 * Conf: current dictionary.
1060 * s: affix flag in string.
1061 * val: affix parameter.
1062 */
1063static void
1065{
1067 char sbuf[BUFSIZ];
1068 char *sflag;
1069
1070 while (*s && isspace((unsigned char) *s))
1071 s += pg_mblen_cstr(s);
1072
1073 if (!*s)
1074 ereport(ERROR,
1076 errmsg("syntax error")));
1077
1078 /* Get flag without \n */
1079 sflag = sbuf;
1080 while (*s && !isspace((unsigned char) *s) && *s != '\n')
1081 {
1082 int clen = ts_copychar_cstr(sflag, s);
1083
1084 sflag += clen;
1085 s += clen;
1086 }
1087 *sflag = '\0';
1088
1089 /* Resize array or allocate memory for array CompoundAffixFlag */
1090 if (Conf->nCompoundAffixFlag >= Conf->mCompoundAffixFlag)
1091 {
1092 if (Conf->mCompoundAffixFlag)
1093 {
1094 Conf->mCompoundAffixFlag *= 2;
1095 Conf->CompoundAffixFlags = (CompoundAffixFlag *)
1096 repalloc(Conf->CompoundAffixFlags,
1097 Conf->mCompoundAffixFlag * sizeof(CompoundAffixFlag));
1098 }
1099 else
1100 {
1101 Conf->mCompoundAffixFlag = 10;
1102 Conf->CompoundAffixFlags = (CompoundAffixFlag *)
1103 tmpalloc(Conf->mCompoundAffixFlag * sizeof(CompoundAffixFlag));
1104 }
1105 }
1106
1107 newValue = Conf->CompoundAffixFlags + Conf->nCompoundAffixFlag;
1108
1110
1111 Conf->usecompound = true;
1112 Conf->nCompoundAffixFlag++;
1113}
1114
1115/*
1116 * Returns a set of affix parameters which correspondence to the set of affix
1117 * flags s.
1118 */
1119static int
1121{
1122 uint32 flag = 0;
1123 CompoundAffixFlag *found,
1124 key;
1125 char sflag[BUFSIZ];
1126 const char *flagcur;
1127
1128 if (Conf->nCompoundAffixFlag == 0)
1129 return 0;
1130
1131 flagcur = s;
1132 while (*flagcur)
1133 {
1136
1137 found = (CompoundAffixFlag *)
1138 bsearch(&key, Conf->CompoundAffixFlags,
1139 Conf->nCompoundAffixFlag, sizeof(CompoundAffixFlag),
1140 cmpcmdflag);
1141 if (found != NULL)
1142 flag |= found->value;
1143 }
1144
1145 return flag;
1146}
1147
1148/*
1149 * Returns a flag set using the s parameter.
1150 *
1151 * If Conf->useFlagAliases is true then the s parameter is index of the
1152 * Conf->AffixData array and function returns its entry.
1153 * Else function returns the s parameter.
1154 */
1155static const char *
1157{
1158 if (Conf->useFlagAliases && *s != '\0')
1159 {
1160 int curaffix;
1161 char *end;
1162
1163 errno = 0;
1164 curaffix = strtol(s, &end, 10);
1165 if (s == end || errno == ERANGE)
1166 ereport(ERROR,
1168 errmsg("invalid affix alias \"%s\"", s)));
1169
1170 if (curaffix > 0 && curaffix < Conf->nAffixData)
1171
1172 /*
1173 * Do not subtract 1 from curaffix because empty string was added
1174 * in NIImportOOAffixes
1175 */
1176 return Conf->AffixData[curaffix];
1177 else if (curaffix > Conf->nAffixData)
1178 ereport(ERROR,
1180 errmsg("invalid affix alias \"%s\"", s)));
1181 return VoidString;
1182 }
1183 else
1184 return s;
1185}
1186
1187/*
1188 * Import an affix file that follows MySpell or Hunspell format.
1189 *
1190 * Conf: current dictionary.
1191 * filename: path to the .affix file.
1192 */
1193static void
1195{
1196 char type[BUFSIZ],
1197 *ptype = NULL;
1198 char sflag[BUFSIZ];
1199 char mask[BUFSIZ],
1200 *pmask;
1201 char find[BUFSIZ],
1202 *pfind;
1203 char repl[BUFSIZ],
1204 *prepl;
1205 bool isSuffix = false;
1206 int naffix = 0,
1207 curaffix = 0;
1208 int sflaglen = 0;
1209 char flagflags = 0;
1211 char *recoded;
1212
1213 /* read file to find any flag */
1214 Conf->usecompound = false;
1215 Conf->useFlagAliases = false;
1216 Conf->flagMode = FM_CHAR;
1217
1219 ereport(ERROR,
1221 errmsg("could not open affix file \"%s\": %m",
1222 filename)));
1223
1224 while ((recoded = tsearch_readline(&trst)) != NULL)
1225 {
1226 if (*recoded == '\0' || isspace((unsigned char) *recoded) || t_iseq(recoded, '#'))
1227 {
1228 pfree(recoded);
1229 continue;
1230 }
1231
1232 if (STRNCMP(recoded, "COMPOUNDFLAG") == 0)
1233 addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDFLAG"),
1235 else if (STRNCMP(recoded, "COMPOUNDBEGIN") == 0)
1236 addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDBEGIN"),
1238 else if (STRNCMP(recoded, "COMPOUNDLAST") == 0)
1239 addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDLAST"),
1241 /* COMPOUNDLAST and COMPOUNDEND are synonyms */
1242 else if (STRNCMP(recoded, "COMPOUNDEND") == 0)
1243 addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDEND"),
1245 else if (STRNCMP(recoded, "COMPOUNDMIDDLE") == 0)
1246 addCompoundAffixFlagValue(Conf, recoded + strlen("COMPOUNDMIDDLE"),
1248 else if (STRNCMP(recoded, "ONLYINCOMPOUND") == 0)
1249 addCompoundAffixFlagValue(Conf, recoded + strlen("ONLYINCOMPOUND"),
1251 else if (STRNCMP(recoded, "COMPOUNDPERMITFLAG") == 0)
1253 recoded + strlen("COMPOUNDPERMITFLAG"),
1255 else if (STRNCMP(recoded, "COMPOUNDFORBIDFLAG") == 0)
1257 recoded + strlen("COMPOUNDFORBIDFLAG"),
1259 else if (STRNCMP(recoded, "FLAG") == 0)
1260 {
1261 char *s = recoded + strlen("FLAG");
1262
1263 while (*s && isspace((unsigned char) *s))
1264 s += pg_mblen_cstr(s);
1265
1266 if (*s)
1267 {
1268 if (STRNCMP(s, "long") == 0)
1269 Conf->flagMode = FM_LONG;
1270 else if (STRNCMP(s, "num") == 0)
1271 Conf->flagMode = FM_NUM;
1272 else if (STRNCMP(s, "default") != 0)
1273 ereport(ERROR,
1275 errmsg("Ispell dictionary supports only "
1276 "\"default\", \"long\", "
1277 "and \"num\" flag values")));
1278 }
1279 }
1280
1281 pfree(recoded);
1282 }
1284
1285 if (Conf->nCompoundAffixFlag > 1)
1286 qsort(Conf->CompoundAffixFlags, Conf->nCompoundAffixFlag,
1287 sizeof(CompoundAffixFlag), cmpcmdflag);
1288
1290 ereport(ERROR,
1292 errmsg("could not open affix file \"%s\": %m",
1293 filename)));
1294
1295 while ((recoded = tsearch_readline(&trst)) != NULL)
1296 {
1297 int fields_read;
1298
1299 if (*recoded == '\0' || isspace((unsigned char) *recoded) || t_iseq(recoded, '#'))
1300 goto nextline;
1301
1303
1304 if (ptype)
1305 pfree(ptype);
1306 ptype = lowerstr_ctx(Conf, type);
1307
1308 /* First try to parse AF parameter (alias compression) */
1309 if (STRNCMP(ptype, "af") == 0)
1310 {
1311 /* First line is the number of aliases */
1312 if (!Conf->useFlagAliases)
1313 {
1314 Conf->useFlagAliases = true;
1315 naffix = atoi(sflag);
1316 if (naffix <= 0)
1317 ereport(ERROR,
1319 errmsg("invalid number of flag vector aliases")));
1320
1321 /* Also reserve place for empty flag set */
1322 naffix++;
1323
1324 Conf->AffixData = palloc0_array(const char *, naffix);
1325 Conf->lenAffixData = Conf->nAffixData = naffix;
1326
1327 /* Add empty flag set into AffixData */
1328 Conf->AffixData[curaffix] = VoidString;
1329 curaffix++;
1330 }
1331 /* Other lines are aliases */
1332 else
1333 {
1334 if (curaffix < naffix)
1335 {
1336 Conf->AffixData[curaffix] = cpstrdup(Conf, sflag);
1337 curaffix++;
1338 }
1339 else
1340 ereport(ERROR,
1342 errmsg("number of aliases exceeds specified number %d",
1343 naffix - 1)));
1344 }
1345 goto nextline;
1346 }
1347 /* Else try to parse prefixes and suffixes */
1348 if (fields_read < 4 ||
1349 (STRNCMP(ptype, "sfx") != 0 && STRNCMP(ptype, "pfx") != 0))
1350 goto nextline;
1351
1353 if (sflaglen == 0
1354 || (sflaglen > 1 && Conf->flagMode == FM_CHAR)
1355 || (sflaglen > 2 && Conf->flagMode == FM_LONG))
1356 goto nextline;
1357
1358 /*--------
1359 * Affix header. For example:
1360 * SFX \ N 1
1361 *--------
1362 */
1363 if (fields_read == 4)
1364 {
1365 isSuffix = (STRNCMP(ptype, "sfx") == 0);
1366 if (t_iseq(find, 'y') || t_iseq(find, 'Y'))
1367 flagflags = FF_CROSSPRODUCT;
1368 else
1369 flagflags = 0;
1370 }
1371 /*--------
1372 * Affix fields. For example:
1373 * SFX \ 0 Y/L [^Y]
1374 *--------
1375 */
1376 else
1377 {
1378 char *ptr;
1379 int aflg = 0;
1380
1381 /* Get flags after '/' (flags are case sensitive) */
1382 if ((ptr = strchr(repl, '/')) != NULL)
1385 ptr + 1));
1386 /* Get lowercased version of string before '/' */
1387 prepl = lowerstr_ctx(Conf, repl);
1388 if ((ptr = strchr(prepl, '/')) != NULL)
1389 *ptr = '\0';
1391 pmask = lowerstr_ctx(Conf, mask);
1392 if (t_iseq(find, '0'))
1393 *pfind = '\0';
1394 if (t_iseq(repl, '0'))
1395 *prepl = '\0';
1396
1397 NIAddAffix(Conf, sflag, flagflags | aflg, pmask, pfind, prepl,
1399 pfree(prepl);
1400 pfree(pfind);
1401 pfree(pmask);
1402 }
1403
1404nextline:
1405 pfree(recoded);
1406 }
1407
1409 if (ptype)
1410 pfree(ptype);
1411}
1412
1413/*
1414 * import affixes
1415 *
1416 * Note caller must already have applied get_tsearch_config_filename
1417 *
1418 * This function is responsible for parsing ispell ("old format") affix files.
1419 * If we realize that the file contains new-format commands, we pass off the
1420 * work to NIImportOOAffixes(), which will re-read the whole file.
1421 */
1422void
1424{
1425 char *pstr = NULL;
1426 char flag[BUFSIZ];
1427 char mask[BUFSIZ];
1428 char find[BUFSIZ];
1429 char repl[BUFSIZ];
1430 char *s;
1431 bool suffixes = false;
1432 bool prefixes = false;
1433 char flagflags = 0;
1435 bool oldformat = false;
1436 char *recoded = NULL;
1437
1439 ereport(ERROR,
1441 errmsg("could not open affix file \"%s\": %m",
1442 filename)));
1443
1444 Conf->usecompound = false;
1445 Conf->useFlagAliases = false;
1446 Conf->flagMode = FM_CHAR;
1447
1448 while ((recoded = tsearch_readline(&trst)) != NULL)
1449 {
1451
1452 /* Skip comments and empty lines */
1453 if (*pstr == '#' || *pstr == '\n')
1454 goto nextline;
1455
1456 if (STRNCMP(pstr, "compoundwords") == 0)
1457 {
1458 /* Find case-insensitive L flag in non-lowercased string */
1459 s = findchar2(recoded, 'l', 'L');
1460 if (s)
1461 {
1462 while (*s && !isspace((unsigned char) *s))
1463 s += pg_mblen_cstr(s);
1464 while (*s && isspace((unsigned char) *s))
1465 s += pg_mblen_cstr(s);
1466
1467 if (*s && pg_mblen_cstr(s) == 1)
1468 {
1470 Conf->usecompound = true;
1471 }
1472 oldformat = true;
1473 goto nextline;
1474 }
1475 }
1476 if (STRNCMP(pstr, "suffixes") == 0)
1477 {
1478 suffixes = true;
1479 prefixes = false;
1480 oldformat = true;
1481 goto nextline;
1482 }
1483 if (STRNCMP(pstr, "prefixes") == 0)
1484 {
1485 suffixes = false;
1486 prefixes = true;
1487 oldformat = true;
1488 goto nextline;
1489 }
1490 if (STRNCMP(pstr, "flag") == 0)
1491 {
1492 s = recoded + 4; /* we need non-lowercased string */
1493 flagflags = 0;
1494
1495 while (*s && isspace((unsigned char) *s))
1496 s += pg_mblen_cstr(s);
1497
1498 if (*s == '*')
1499 {
1500 flagflags |= FF_CROSSPRODUCT;
1501 s++;
1502 }
1503 else if (*s == '~')
1504 {
1505 flagflags |= FF_COMPOUNDONLY;
1506 s++;
1507 }
1508
1509 if (*s == '\\')
1510 s++;
1511
1512 /*
1513 * An old-format flag is a single ASCII character; we expect it to
1514 * be followed by EOL, whitespace, or ':'. Otherwise this is a
1515 * new-format flag command.
1516 */
1517 if (*s && pg_mblen_cstr(s) == 1)
1518 {
1519 flag[0] = *s++;
1520 flag[1] = '\0';
1521
1522 if (*s == '\0' || *s == '#' || *s == '\n' || *s == ':' ||
1523 isspace((unsigned char) *s))
1524 {
1525 oldformat = true;
1526 goto nextline;
1527 }
1528 }
1529 goto isnewformat;
1530 }
1531 if (STRNCMP(recoded, "COMPOUNDFLAG") == 0 ||
1532 STRNCMP(recoded, "COMPOUNDMIN") == 0 ||
1533 STRNCMP(recoded, "PFX") == 0 ||
1534 STRNCMP(recoded, "SFX") == 0)
1535 goto isnewformat;
1536
1537 if ((!suffixes) && (!prefixes))
1538 goto nextline;
1539
1540 if (!parse_affentry(pstr, mask, find, repl))
1541 goto nextline;
1542
1543 NIAddAffix(Conf, flag, flagflags, mask, find, repl, suffixes ? FF_SUFFIX : FF_PREFIX);
1544
1545nextline:
1546 pfree(recoded);
1547 pfree(pstr);
1548 }
1550 return;
1551
1553 if (oldformat)
1554 ereport(ERROR,
1556 errmsg("affix file contains both old-style and new-style commands")));
1558
1560}
1561
1562/*
1563 * Merges two affix flag sets and stores a new affix flag set into
1564 * Conf->AffixData.
1565 *
1566 * Returns index of a new affix flag set.
1567 */
1568static int
1570{
1571 const char **ptr;
1572
1573 Assert(a1 < Conf->nAffixData && a2 < Conf->nAffixData);
1574
1575 /* Do not merge affix flags if one of affix flags is empty */
1576 if (*Conf->AffixData[a1] == '\0')
1577 return a2;
1578 else if (*Conf->AffixData[a2] == '\0')
1579 return a1;
1580
1581 /* Double the size of AffixData if there's not enough space */
1582 if (Conf->nAffixData + 1 >= Conf->lenAffixData)
1583 {
1584 Conf->lenAffixData *= 2;
1585 Conf->AffixData = (const char **) repalloc(Conf->AffixData,
1586 sizeof(char *) * Conf->lenAffixData);
1587 }
1588
1589 ptr = Conf->AffixData + Conf->nAffixData;
1590 if (Conf->flagMode == FM_NUM)
1591 {
1592 char *p = cpalloc(strlen(Conf->AffixData[a1]) +
1593 strlen(Conf->AffixData[a2]) +
1594 1 /* comma */ + 1 /* \0 */ );
1595
1596 sprintf(p, "%s,%s", Conf->AffixData[a1], Conf->AffixData[a2]);
1597 *ptr = p;
1598 }
1599 else
1600 {
1601 char *p = cpalloc(strlen(Conf->AffixData[a1]) +
1602 strlen(Conf->AffixData[a2]) +
1603 1 /* \0 */ );
1604
1605 sprintf(p, "%s%s", Conf->AffixData[a1], Conf->AffixData[a2]);
1606 *ptr = p;
1607 }
1608 ptr++;
1609 *ptr = NULL;
1610 Conf->nAffixData++;
1611
1612 return Conf->nAffixData - 1;
1613}
1614
1615/*
1616 * Returns a set of affix parameters which correspondence to the set of affix
1617 * flags with the given index.
1618 */
1619static uint32
1621{
1622 Assert(affix < Conf->nAffixData);
1623
1624 return (getCompoundAffixFlagValue(Conf, Conf->AffixData[affix]) &
1626}
1627
1628/*
1629 * Makes a prefix tree for the given level.
1630 *
1631 * Conf: current dictionary.
1632 * low: lower index of the Conf->Spell array.
1633 * high: upper index of the Conf->Spell array.
1634 * level: current prefix tree level.
1635 */
1636static SPNode *
1637mkSPNode(IspellDict *Conf, int low, int high, int level)
1638{
1639 int i;
1640 int nchar = 0;
1641 char lastchar = '\0';
1642 SPNode *rs;
1644 int lownew = low;
1645
1646 for (i = low; i < high; i++)
1647 if (Conf->Spell[i]->p.d.len > level && lastchar != Conf->Spell[i]->word[level])
1648 {
1649 nchar++;
1650 lastchar = Conf->Spell[i]->word[level];
1651 }
1652
1653 if (!nchar)
1654 return NULL;
1655
1656 rs = (SPNode *) cpalloc0(SPNHDRSZ + nchar * sizeof(SPNodeData));
1657 rs->length = nchar;
1658 data = rs->data;
1659
1660 lastchar = '\0';
1661 for (i = low; i < high; i++)
1662 if (Conf->Spell[i]->p.d.len > level)
1663 {
1664 if (lastchar != Conf->Spell[i]->word[level])
1665 {
1666 if (lastchar)
1667 {
1668 /* Next level of the prefix tree */
1669 data->node = mkSPNode(Conf, lownew, i, level + 1);
1670 lownew = i;
1671 data++;
1672 }
1673 lastchar = Conf->Spell[i]->word[level];
1674 }
1675 data->val = ((uint8 *) (Conf->Spell[i]->word))[level];
1676 if (Conf->Spell[i]->p.d.len == level + 1)
1677 {
1678 bool clearCompoundOnly = false;
1679
1680 if (data->isword && data->affix != Conf->Spell[i]->p.d.affix)
1681 {
1682 /*
1683 * MergeAffix called a few times. If one of word is
1684 * allowed to be in compound word and another isn't, then
1685 * clear FF_COMPOUNDONLY flag.
1686 */
1687
1688 clearCompoundOnly = (FF_COMPOUNDONLY & data->compoundflag
1689 & makeCompoundFlags(Conf, Conf->Spell[i]->p.d.affix))
1690 ? false : true;
1691 data->affix = MergeAffix(Conf, data->affix, Conf->Spell[i]->p.d.affix);
1692 }
1693 else
1694 data->affix = Conf->Spell[i]->p.d.affix;
1695 data->isword = 1;
1696
1697 data->compoundflag = makeCompoundFlags(Conf, data->affix);
1698
1699 if ((data->compoundflag & FF_COMPOUNDONLY) &&
1700 (data->compoundflag & FF_COMPOUNDFLAG) == 0)
1701 data->compoundflag |= FF_COMPOUNDFLAG;
1702
1704 data->compoundflag &= ~FF_COMPOUNDONLY;
1705 }
1706 }
1707
1708 /* Next level of the prefix tree */
1709 data->node = mkSPNode(Conf, lownew, high, level + 1);
1710
1711 return rs;
1712}
1713
1714/*
1715 * Builds the Conf->Dictionary tree and AffixData from the imported dictionary
1716 * and affixes.
1717 */
1718void
1720{
1721 int i;
1722 int naffix;
1723 int curaffix;
1724
1725 /* compress affixes */
1726
1727 /*
1728 * If we use flag aliases then we need to use Conf->AffixData filled in
1729 * the NIImportOOAffixes().
1730 */
1731 if (Conf->useFlagAliases)
1732 {
1733 for (i = 0; i < Conf->nspell; i++)
1734 {
1735 char *end;
1736
1737 if (*Conf->Spell[i]->p.flag != '\0')
1738 {
1739 errno = 0;
1740 curaffix = strtol(Conf->Spell[i]->p.flag, &end, 10);
1741 if (Conf->Spell[i]->p.flag == end || errno == ERANGE)
1742 ereport(ERROR,
1744 errmsg("invalid affix alias \"%s\"",
1745 Conf->Spell[i]->p.flag)));
1746 if (curaffix < 0 || curaffix >= Conf->nAffixData)
1747 ereport(ERROR,
1749 errmsg("invalid affix alias \"%s\"",
1750 Conf->Spell[i]->p.flag)));
1751 if (*end != '\0' && !isdigit((unsigned char) *end) && !isspace((unsigned char) *end))
1752 ereport(ERROR,
1754 errmsg("invalid affix alias \"%s\"",
1755 Conf->Spell[i]->p.flag)));
1756 }
1757 else
1758 {
1759 /*
1760 * If Conf->Spell[i]->p.flag is empty, then get empty value of
1761 * Conf->AffixData (0 index).
1762 */
1763 curaffix = 0;
1764 }
1765
1766 Conf->Spell[i]->p.d.affix = curaffix;
1767 Conf->Spell[i]->p.d.len = strlen(Conf->Spell[i]->word);
1768 }
1769 }
1770 /* Otherwise fill Conf->AffixData here */
1771 else
1772 {
1773 /* Count the number of different flags used in the dictionary */
1774 qsort(Conf->Spell, Conf->nspell, sizeof(SPELL *),
1776
1777 naffix = 0;
1778 for (i = 0; i < Conf->nspell; i++)
1779 {
1780 if (i == 0 ||
1781 strcmp(Conf->Spell[i]->p.flag, Conf->Spell[i - 1]->p.flag) != 0)
1782 naffix++;
1783 }
1784
1785 /*
1786 * Fill in Conf->AffixData with the affixes that were used in the
1787 * dictionary. Replace textual flag-field of Conf->Spell entries with
1788 * indexes into Conf->AffixData array.
1789 */
1790 Conf->AffixData = palloc0_array(const char *, naffix);
1791
1792 curaffix = -1;
1793 for (i = 0; i < Conf->nspell; i++)
1794 {
1795 if (i == 0 ||
1796 strcmp(Conf->Spell[i]->p.flag, Conf->AffixData[curaffix]) != 0)
1797 {
1798 curaffix++;
1800 Conf->AffixData[curaffix] = cpstrdup(Conf,
1801 Conf->Spell[i]->p.flag);
1802 }
1803
1804 Conf->Spell[i]->p.d.affix = curaffix;
1805 Conf->Spell[i]->p.d.len = strlen(Conf->Spell[i]->word);
1806 }
1807
1808 Conf->lenAffixData = Conf->nAffixData = naffix;
1809 }
1810
1811 /* Start build a prefix tree */
1812 qsort(Conf->Spell, Conf->nspell, sizeof(SPELL *), cmpspell);
1813 Conf->Dictionary = mkSPNode(Conf, 0, Conf->nspell, 0);
1814}
1815
1816/*
1817 * Makes a prefix tree for the given level using the repl string of an affix
1818 * rule. Affixes with empty replace string do not include in the prefix tree.
1819 * This affixes are included by mkVoidAffix().
1820 *
1821 * Conf: current dictionary.
1822 * low: lower index of the Conf->Affix array.
1823 * high: upper index of the Conf->Affix array.
1824 * level: current prefix tree level.
1825 * type: FF_SUFFIX or FF_PREFIX.
1826 */
1827static AffixNode *
1828mkANode(IspellDict *Conf, int low, int high, int level, int type)
1829{
1830 int i;
1831 int nchar = 0;
1832 uint8 lastchar = '\0';
1833 AffixNode *rs;
1835 int lownew = low;
1836 int naff;
1837 AFFIX **aff;
1838
1839 for (i = low; i < high; i++)
1840 if (Conf->Affix[i].replen > level && lastchar != GETCHAR(Conf->Affix + i, level, type))
1841 {
1842 nchar++;
1843 lastchar = GETCHAR(Conf->Affix + i, level, type);
1844 }
1845
1846 if (!nchar)
1847 return NULL;
1848
1849 aff = (AFFIX **) tmpalloc(sizeof(AFFIX *) * (high - low + 1));
1850 naff = 0;
1851
1852 rs = (AffixNode *) cpalloc0(ANHRDSZ + nchar * sizeof(AffixNodeData));
1853 rs->length = nchar;
1854 data = rs->data;
1855
1856 lastchar = '\0';
1857 for (i = low; i < high; i++)
1858 if (Conf->Affix[i].replen > level)
1859 {
1860 if (lastchar != GETCHAR(Conf->Affix + i, level, type))
1861 {
1862 if (lastchar)
1863 {
1864 /* Next level of the prefix tree */
1865 data->node = mkANode(Conf, lownew, i, level + 1, type);
1866 if (naff)
1867 {
1868 data->naff = naff;
1869 data->aff = (AFFIX **) cpalloc(sizeof(AFFIX *) * naff);
1870 memcpy(data->aff, aff, sizeof(AFFIX *) * naff);
1871 naff = 0;
1872 }
1873 data++;
1874 lownew = i;
1875 }
1876 lastchar = GETCHAR(Conf->Affix + i, level, type);
1877 }
1878 data->val = GETCHAR(Conf->Affix + i, level, type);
1879 if (Conf->Affix[i].replen == level + 1)
1880 { /* affix stopped */
1881 aff[naff++] = Conf->Affix + i;
1882 }
1883 }
1884
1885 /* Next level of the prefix tree */
1886 data->node = mkANode(Conf, lownew, high, level + 1, type);
1887 if (naff)
1888 {
1889 data->naff = naff;
1890 data->aff = (AFFIX **) cpalloc(sizeof(AFFIX *) * naff);
1891 memcpy(data->aff, aff, sizeof(AFFIX *) * naff);
1892 naff = 0;
1893 }
1894
1895 pfree(aff);
1896
1897 return rs;
1898}
1899
1900/*
1901 * Makes the root void node in the prefix tree. The root void node is created
1902 * for affixes which have empty replace string ("repl" field).
1903 */
1904static void
1906{
1907 int i,
1908 cnt = 0;
1909 int start = (issuffix) ? startsuffix : 0;
1910 int end = (issuffix) ? Conf->naffixes : startsuffix;
1911 AffixNode *Affix = (AffixNode *) palloc0(ANHRDSZ + sizeof(AffixNodeData));
1912
1913 Affix->length = 1;
1914 Affix->isvoid = 1;
1915
1916 if (issuffix)
1917 {
1918 Affix->data->node = Conf->Suffix;
1919 Conf->Suffix = Affix;
1920 }
1921 else
1922 {
1923 Affix->data->node = Conf->Prefix;
1924 Conf->Prefix = Affix;
1925 }
1926
1927 /* Count affixes with empty replace string */
1928 for (i = start; i < end; i++)
1929 if (Conf->Affix[i].replen == 0)
1930 cnt++;
1931
1932 /* There is not affixes with empty replace string */
1933 if (cnt == 0)
1934 return;
1935
1936 Affix->data->aff = (AFFIX **) cpalloc(sizeof(AFFIX *) * cnt);
1937 Affix->data->naff = (uint32) cnt;
1938
1939 cnt = 0;
1940 for (i = start; i < end; i++)
1941 if (Conf->Affix[i].replen == 0)
1942 {
1943 Affix->data->aff[cnt] = Conf->Affix + i;
1944 cnt++;
1945 }
1946}
1947
1948/*
1949 * Checks if the affixflag is used by dictionary. Conf->AffixData does not
1950 * contain affixflag if this flag is not used actually by the .dict file.
1951 *
1952 * Conf: current dictionary.
1953 * affixflag: affix flag.
1954 *
1955 * Returns true if the Conf->AffixData array contains affixflag, otherwise
1956 * returns false.
1957 */
1958static bool
1960{
1961 int i;
1962
1963 for (i = 0; i < Conf->nAffixData; i++)
1965 return true;
1966
1967 return false;
1968}
1969
1970/*
1971 * Builds Conf->Prefix and Conf->Suffix trees from the imported affixes.
1972 */
1973void
1975{
1976 AFFIX *Affix;
1977 size_t i;
1978 CMPDAffix *ptr;
1979 int firstsuffix = Conf->naffixes;
1980
1981 if (Conf->naffixes == 0)
1982 return;
1983
1984 /* Store compound affixes in the Conf->CompoundAffix array */
1985 if (Conf->naffixes > 1)
1986 qsort(Conf->Affix, Conf->naffixes, sizeof(AFFIX), cmpaffix);
1987 Conf->CompoundAffix = ptr = palloc_array(CMPDAffix, Conf->naffixes);
1988 ptr->affix = NULL;
1989
1990 for (i = 0; i < Conf->naffixes; i++)
1991 {
1992 Affix = &(((AFFIX *) Conf->Affix)[i]);
1993 if (Affix->type == FF_SUFFIX && i < firstsuffix)
1994 firstsuffix = i;
1995
1996 if ((Affix->flagflags & FF_COMPOUNDFLAG) && Affix->replen > 0 &&
1997 isAffixInUse(Conf, Affix->flag))
1998 {
1999 bool issuffix = (Affix->type == FF_SUFFIX);
2000
2001 if (ptr == Conf->CompoundAffix ||
2002 issuffix != (ptr - 1)->issuffix ||
2003 strbncmp((const unsigned char *) (ptr - 1)->affix,
2004 (const unsigned char *) Affix->repl,
2005 (ptr - 1)->len))
2006 {
2007 /* leave only unique and minimal suffixes */
2008 ptr->affix = Affix->repl;
2009 ptr->len = Affix->replen;
2010 ptr->issuffix = issuffix;
2011 ptr++;
2012 }
2013 }
2014 }
2015 ptr->affix = NULL;
2016 Conf->CompoundAffix = (CMPDAffix *) repalloc(Conf->CompoundAffix, sizeof(CMPDAffix) * (ptr - Conf->CompoundAffix + 1));
2017
2018 /* Start build a prefix tree */
2019 Conf->Prefix = mkANode(Conf, 0, firstsuffix, 0, FF_PREFIX);
2020 Conf->Suffix = mkANode(Conf, firstsuffix, Conf->naffixes, 0, FF_SUFFIX);
2022 mkVoidAffix(Conf, false, firstsuffix);
2023}
2024
2025static AffixNodeData *
2026FindAffixes(AffixNode *node, const char *word, int wrdlen, int *level, int type)
2027{
2029 *StopHigh,
2030 *StopMiddle;
2031 uint8 symbol;
2032
2033 if (node->isvoid)
2034 { /* search void affixes */
2035 if (node->data->naff)
2036 return node->data;
2037 node = node->data->node;
2038 }
2039
2040 while (node && *level < wrdlen)
2041 {
2042 StopLow = node->data;
2043 StopHigh = node->data + node->length;
2044 while (StopLow < StopHigh)
2045 {
2046 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
2047 symbol = GETWCHAR(word, wrdlen, *level, type);
2048
2049 if (StopMiddle->val == symbol)
2050 {
2051 (*level)++;
2052 if (StopMiddle->naff)
2053 return StopMiddle;
2054 node = StopMiddle->node;
2055 break;
2056 }
2057 else if (StopMiddle->val < symbol)
2058 StopLow = StopMiddle + 1;
2059 else
2061 }
2062 if (StopLow >= StopHigh)
2063 break;
2064 }
2065 return NULL;
2066}
2067
2068static char *
2069CheckAffix(const char *word, size_t len, AFFIX *Affix, int flagflags, char *newword, int *baselen)
2070{
2071 /*
2072 * Check compound allow flags
2073 */
2074
2075 if (flagflags == 0)
2076 {
2077 if (Affix->flagflags & FF_COMPOUNDONLY)
2078 return NULL;
2079 }
2080 else if (flagflags & FF_COMPOUNDBEGIN)
2081 {
2082 if (Affix->flagflags & FF_COMPOUNDFORBIDFLAG)
2083 return NULL;
2084 if ((Affix->flagflags & FF_COMPOUNDBEGIN) == 0)
2085 if (Affix->type == FF_SUFFIX)
2086 return NULL;
2087 }
2088 else if (flagflags & FF_COMPOUNDMIDDLE)
2089 {
2090 if ((Affix->flagflags & FF_COMPOUNDMIDDLE) == 0 ||
2092 return NULL;
2093 }
2094 else if (flagflags & FF_COMPOUNDLAST)
2095 {
2096 if (Affix->flagflags & FF_COMPOUNDFORBIDFLAG)
2097 return NULL;
2098 if ((Affix->flagflags & FF_COMPOUNDLAST) == 0)
2099 if (Affix->type == FF_PREFIX)
2100 return NULL;
2101 }
2102
2103 /*
2104 * make replace pattern of affix
2105 */
2106 if (Affix->type == FF_SUFFIX)
2107 {
2109 strcpy(newword + len - Affix->replen, Affix->find);
2110 if (baselen) /* store length of non-changed part of word */
2111 *baselen = len - Affix->replen;
2112 }
2113 else
2114 {
2115 /*
2116 * if prefix is an all non-changed part's length then all word
2117 * contains only prefix and suffix, so out
2118 */
2119 if (baselen && *baselen + strlen(Affix->find) <= Affix->replen)
2120 return NULL;
2121 strcpy(newword, Affix->find);
2122 strcat(newword, word + Affix->replen);
2123 }
2124
2125 /*
2126 * check resulting word
2127 */
2128 if (Affix->issimple)
2129 return newword;
2130 else if (Affix->isregis)
2131 {
2132 if (RS_execute(&(Affix->reg.regis), newword))
2133 return newword;
2134 }
2135 else
2136 {
2137 pg_wchar *data;
2138 size_t data_len;
2139 int newword_len;
2140
2141 /* Convert data string to wide characters */
2145
2146 if (pg_regexec(Affix->reg.pregex, data, data_len,
2147 0, NULL, 0, NULL, 0) == REG_OKAY)
2148 {
2149 pfree(data);
2150 return newword;
2151 }
2152 pfree(data);
2153 }
2154
2155 return NULL;
2156}
2157
2158static int
2159addToResult(char **forms, char **cur, char *word)
2160{
2161 if (cur - forms >= MAX_NORM - 1)
2162 return 0;
2163 if (forms == cur || strcmp(word, *(cur - 1)) != 0)
2164 {
2165 *cur = pstrdup(word);
2166 *(cur + 1) = NULL;
2167 return 1;
2168 }
2169
2170 return 0;
2171}
2172
2173static char **
2175{
2176 AffixNodeData *suffix = NULL,
2177 *prefix = NULL;
2178 int slevel = 0,
2179 plevel = 0;
2180 int wrdlen = strlen(word),
2181 swrdlen;
2182 char **forms;
2183 char **cur;
2184 char newword[2 * MAXNORMLEN] = "";
2185 char pnewword[2 * MAXNORMLEN] = "";
2186 AffixNode *snode = Conf->Suffix,
2187 *pnode;
2188 int i,
2189 j;
2190
2191 if (wrdlen > MAXNORMLEN)
2192 return NULL;
2193 cur = forms = palloc_array(char *, MAX_NORM);
2194 *cur = NULL;
2195
2196
2197 /* Check that the word itself is normal form */
2199 {
2200 *cur = pstrdup(word);
2201 cur++;
2202 *cur = NULL;
2203 }
2204
2205 /* Find all other NORMAL forms of the 'word' (check only prefix) */
2206 pnode = Conf->Prefix;
2207 plevel = 0;
2208 while (pnode)
2209 {
2211 if (!prefix)
2212 break;
2213 for (j = 0; j < prefix->naff; j++)
2214 {
2215 if (CheckAffix(word, wrdlen, prefix->aff[j], flag, newword, NULL))
2216 {
2217 /* prefix success */
2218 if (FindWord(Conf, newword, prefix->aff[j]->flag, flag))
2220 }
2221 }
2222 pnode = prefix->node;
2223 }
2224
2225 /*
2226 * Find all other NORMAL forms of the 'word' (check suffix and then
2227 * prefix)
2228 */
2229 while (snode)
2230 {
2231 int baselen = 0;
2232
2233 /* find possible suffix */
2235 if (!suffix)
2236 break;
2237 /* foreach suffix check affix */
2238 for (i = 0; i < suffix->naff; i++)
2239 {
2240 if (CheckAffix(word, wrdlen, suffix->aff[i], flag, newword, &baselen))
2241 {
2242 /* suffix success */
2243 if (FindWord(Conf, newword, suffix->aff[i]->flag, flag))
2245
2246 /* now we will look changed word with prefixes */
2247 pnode = Conf->Prefix;
2248 plevel = 0;
2250 while (pnode)
2251 {
2253 if (!prefix)
2254 break;
2255 for (j = 0; j < prefix->naff; j++)
2256 {
2257 if (CheckAffix(newword, swrdlen, prefix->aff[j], flag, pnewword, &baselen))
2258 {
2259 /* prefix success */
2260 const char *ff = (prefix->aff[j]->flagflags & suffix->aff[i]->flagflags & FF_CROSSPRODUCT) ?
2261 VoidString : prefix->aff[j]->flag;
2262
2263 if (FindWord(Conf, pnewword, ff, flag))
2265 }
2266 }
2267 pnode = prefix->node;
2268 }
2269 }
2270 }
2271
2272 snode = suffix->node;
2273 }
2274
2275 if (cur == forms)
2276 {
2277 pfree(forms);
2278 return NULL;
2279 }
2280 return forms;
2281}
2282
2283typedef struct SplitVar
2284{
2287 char **stem;
2290
2291static int
2293{
2294 bool issuffix;
2295
2296 /* in case CompoundAffix is null: */
2297 if (*ptr == NULL)
2298 return -1;
2299
2300 if (CheckInPlace)
2301 {
2302 while ((*ptr)->affix)
2303 {
2304 if (len > (*ptr)->len && strncmp((*ptr)->affix, word, (*ptr)->len) == 0)
2305 {
2306 len = (*ptr)->len;
2307 issuffix = (*ptr)->issuffix;
2308 (*ptr)++;
2309 return (issuffix) ? len : 0;
2310 }
2311 (*ptr)++;
2312 }
2313 }
2314 else
2315 {
2316 const char *affbegin;
2317
2318 while ((*ptr)->affix)
2319 {
2320 if (len > (*ptr)->len && (affbegin = strstr(word, (*ptr)->affix)) != NULL)
2321 {
2322 len = (*ptr)->len + (affbegin - word);
2323 issuffix = (*ptr)->issuffix;
2324 (*ptr)++;
2325 return (issuffix) ? len : 0;
2326 }
2327 (*ptr)++;
2328 }
2329 }
2330 return -1;
2331}
2332
2333static SplitVar *
2335{
2337
2338 v->next = NULL;
2339 if (s)
2340 {
2341 int i;
2342
2343 v->lenstem = s->lenstem;
2344 v->stem = palloc_array(char *, v->lenstem);
2345 v->nstem = s->nstem;
2346 for (i = 0; i < s->nstem; i++)
2347 v->stem[i] = (makedup) ? pstrdup(s->stem[i]) : s->stem[i];
2348 }
2349 else
2350 {
2351 v->lenstem = 16;
2352 v->stem = palloc_array(char *, v->lenstem);
2353 v->nstem = 0;
2354 }
2355 return v;
2356}
2357
2358static void
2360{
2361 if (v->nstem >= v->lenstem)
2362 {
2363 v->lenstem *= 2;
2364 v->stem = (char **) repalloc(v->stem, sizeof(char *) * v->lenstem);
2365 }
2366
2367 v->stem[v->nstem] = word;
2368 v->nstem++;
2369}
2370
2371static SplitVar *
2372SplitToVariants(IspellDict *Conf, SPNode *snode, SplitVar *orig, const char *word, int wordlen, int startpos, int minpos)
2373{
2374 SplitVar *var = NULL;
2376 *StopHigh,
2377 *StopMiddle = NULL;
2378 SPNode *node = (snode) ? snode : Conf->Dictionary;
2379 int level = (snode) ? minpos : startpos; /* recursive
2380 * minpos==level */
2381 int lenaff;
2382 CMPDAffix *caff;
2383 char *notprobed;
2384 int compoundflag = 0;
2385
2386 /* since this function recurses, it could be driven to stack overflow */
2388
2389 notprobed = (char *) palloc(wordlen);
2390 memset(notprobed, 1, wordlen);
2391 var = CopyVar(orig, 1);
2392
2393 while (level < wordlen)
2394 {
2395 /* find word with epenthetic or/and compound affix */
2396 caff = Conf->CompoundAffix;
2397 while (level > startpos && (lenaff = CheckCompoundAffixes(&caff, word + level, wordlen - level, (node) ? true : false)) >= 0)
2398 {
2399 /*
2400 * there is one of compound affixes, so check word for existings
2401 */
2402 char buf[MAXNORMLEN];
2403 char **subres;
2404
2405 lenaff = level - startpos + lenaff;
2406
2407 if (!notprobed[startpos + lenaff - 1])
2408 continue;
2409
2410 if (level + lenaff - 1 <= minpos)
2411 continue;
2412
2413 if (lenaff >= MAXNORMLEN)
2414 continue; /* skip too big value */
2415 if (lenaff > 0)
2417 buf[lenaff] = '\0';
2418
2419 if (level == 0)
2420 compoundflag = FF_COMPOUNDBEGIN;
2421 else if (level == wordlen - 1)
2422 compoundflag = FF_COMPOUNDLAST;
2423 else
2424 compoundflag = FF_COMPOUNDMIDDLE;
2425 subres = NormalizeSubWord(Conf, buf, compoundflag);
2426 if (subres)
2427 {
2428 /* Yes, it was a word from dictionary */
2429 SplitVar *new = CopyVar(var, 0);
2430 SplitVar *ptr = var;
2431 char **sptr = subres;
2432
2433 notprobed[startpos + lenaff - 1] = 0;
2434
2435 while (*sptr)
2436 {
2437 AddStem(new, *sptr);
2438 sptr++;
2439 }
2440 pfree(subres);
2441
2442 while (ptr->next)
2443 ptr = ptr->next;
2444 ptr->next = SplitToVariants(Conf, NULL, new, word, wordlen, startpos + lenaff, startpos + lenaff);
2445
2446 pfree(new->stem);
2447 pfree(new);
2448 }
2449 }
2450
2451 if (!node)
2452 break;
2453
2454 StopLow = node->data;
2455 StopHigh = node->data + node->length;
2456 while (StopLow < StopHigh)
2457 {
2458 StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
2459 if (StopMiddle->val == ((const uint8 *) (word))[level])
2460 break;
2461 else if (StopMiddle->val < ((const uint8 *) (word))[level])
2462 StopLow = StopMiddle + 1;
2463 else
2465 }
2466
2467 if (StopLow < StopHigh)
2468 {
2469 if (startpos == 0)
2470 compoundflag = FF_COMPOUNDBEGIN;
2471 else if (level == wordlen - 1)
2472 compoundflag = FF_COMPOUNDLAST;
2473 else
2474 compoundflag = FF_COMPOUNDMIDDLE;
2475
2476 /* find infinitive */
2477 if (StopMiddle->isword &&
2478 (StopMiddle->compoundflag & compoundflag) &&
2479 notprobed[level])
2480 {
2481 /* ok, we found full compoundallowed word */
2482 if (level > minpos)
2483 {
2484 /* and its length more than minimal */
2485 if (wordlen == level + 1)
2486 {
2487 /* well, it was last word */
2488 AddStem(var, pnstrdup(word + startpos, wordlen - startpos));
2490 return var;
2491 }
2492 else
2493 {
2494 /* then we will search more big word at the same point */
2495 SplitVar *ptr = var;
2496
2497 while (ptr->next)
2498 ptr = ptr->next;
2499 ptr->next = SplitToVariants(Conf, node, var, word, wordlen, startpos, level);
2500 /* we can find next word */
2501 level++;
2502 AddStem(var, pnstrdup(word + startpos, level - startpos));
2503 node = Conf->Dictionary;
2504 startpos = level;
2505 continue;
2506 }
2507 }
2508 }
2509 node = StopMiddle->node;
2510 }
2511 else
2512 node = NULL;
2513 level++;
2514 }
2515
2516 AddStem(var, pnstrdup(word + startpos, wordlen - startpos));
2518 return var;
2519}
2520
2521static void
2523{
2524 if (*lres == NULL)
2526
2527 if (*lcur - *lres < MAX_NORM - 1)
2528 {
2529 (*lcur)->lexeme = word;
2530 (*lcur)->flags = flags;
2531 (*lcur)->nvariant = NVariant;
2532 (*lcur)++;
2533 (*lcur)->lexeme = NULL;
2534 }
2535}
2536
2537TSLexeme *
2539{
2540 char **res;
2541 TSLexeme *lcur = NULL,
2542 *lres = NULL;
2543 uint16 NVariant = 1;
2544
2545 res = NormalizeSubWord(Conf, word, 0);
2546
2547 if (res)
2548 {
2549 char **ptr = res;
2550
2551 while (*ptr && (lcur - lres) < MAX_NORM)
2552 {
2553 addNorm(&lres, &lcur, *ptr, 0, NVariant++);
2554 ptr++;
2555 }
2556 pfree(res);
2557 }
2558
2559 if (Conf->usecompound)
2560 {
2561 int wordlen = strlen(word);
2562 SplitVar *ptr,
2563 *var = SplitToVariants(Conf, NULL, NULL, word, wordlen, 0, -1);
2564 int i;
2565
2566 while (var)
2567 {
2568 if (var->nstem > 1)
2569 {
2570 char **subres = NormalizeSubWord(Conf, var->stem[var->nstem - 1], FF_COMPOUNDLAST);
2571
2572 if (subres)
2573 {
2574 char **subptr = subres;
2575
2576 while (*subptr)
2577 {
2578 for (i = 0; i < var->nstem - 1; i++)
2579 {
2580 addNorm(&lres, &lcur, (subptr == subres) ? var->stem[i] : pstrdup(var->stem[i]), 0, NVariant);
2581 }
2582
2583 addNorm(&lres, &lcur, *subptr, 0, NVariant);
2584 subptr++;
2585 NVariant++;
2586 }
2587
2588 pfree(subres);
2589 var->stem[0] = NULL;
2590 pfree(var->stem[var->nstem - 1]);
2591 }
2592 }
2593
2594 for (i = 0; i < var->nstem && var->stem[i]; i++)
2595 pfree(var->stem[i]);
2596 ptr = var->next;
2597 pfree(var->stem);
2598 pfree(var);
2599 var = ptr;
2600 }
2601 }
2602
2603 return lres;
2604}
unsigned char symbol
Definition api.h:4
static int32 next
Definition blutils.c:225
#define MAXALIGN(LEN)
Definition c.h:826
uint8_t uint8
Definition c.h:544
#define Assert(condition)
Definition c.h:873
int32_t int32
Definition c.h:542
uint16_t uint16
Definition c.h:545
uint32_t uint32
Definition c.h:546
struct cursor * cur
Definition ecpg.c:29
int errcode(int sqlerrcode)
Definition elog.c:863
int errmsg(const char *fmt,...)
Definition elog.c:1080
#define ERROR
Definition elog.h:39
#define elog(elevel,...)
Definition elog.h:226
#define ereport(elevel,...)
Definition elog.h:150
void err(int eval, const char *fmt,...)
Definition err.c:43
#define palloc_object(type)
Definition fe_memutils.h:74
#define palloc_array(type, count)
Definition fe_memutils.h:76
#define palloc0_array(type, count)
Definition fe_memutils.h:77
char * str_tolower(const char *buff, size_t nbytes, Oid collid)
return str start
const char * str
static const FormData_pg_attribute a1
Definition heap.c:144
static const FormData_pg_attribute a2
Definition heap.c:157
long val
Definition informix.c:689
int j
Definition isn.c:78
int i
Definition isn.c:77
unsigned int pg_wchar
Definition mbprint.c:31
int pg_mblen_cstr(const char *mbstr)
Definition mbutils.c:1045
int pg_mb2wchar_with_len(const char *from, pg_wchar *to, int len)
Definition mbutils.c:997
char * pstrdup(const char *in)
Definition mcxt.c:1781
void * repalloc(void *pointer, Size size)
Definition mcxt.c:1632
void pfree(void *pointer)
Definition mcxt.c:1616
void * palloc0(Size size)
Definition mcxt.c:1417
void * palloc(Size size)
Definition mcxt.c:1387
MemoryContext CurTransactionContext
Definition mcxt.c:172
char * pnstrdup(const char *in, Size len)
Definition mcxt.c:1792
void MemoryContextDelete(MemoryContext context)
Definition mcxt.c:472
#define AllocSetContextCreate
Definition memutils.h:129
#define ALLOCSET_DEFAULT_SIZES
Definition memutils.h:160
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:124
const void size_t len
const void * data
static char * filename
Definition pg_dumpall.c:120
static XLogRecPtr startpos
static char buf[DEFAULT_XLOG_SEG_SIZE]
#define sprintf
Definition port.h:262
#define qsort(a, b, c, d)
Definition port.h:495
char * c
static int fb(int x)
char * s1
char * s2
static void prefixes(struct vars *v)
Definition regc_lex.c:99
int pg_regcomp(regex_t *re, const chr *string, size_t len, int flags, Oid collation)
Definition regcomp.c:372
static void word(struct vars *v, int dir, struct state *lp, struct state *rp)
Definition regcomp.c:1476
size_t pg_regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size)
Definition regerror.c:60
#define REG_ADVANCED
Definition regex.h:181
#define REG_OKAY
Definition regex.h:215
#define REG_NOSUB
Definition regex.h:185
#define regex_t
Definition regex.h:245
static int find(struct vars *v, struct cnfa *cnfa, struct colormap *cm)
Definition regexec.c:419
int pg_regexec(regex_t *re, const chr *string, size_t len, size_t search_start, rm_detail_t *details, size_t nmatch, regmatch_t pmatch[], int flags)
Definition regexec.c:185
void RS_compile(Regis *r, bool issuffix, const char *str)
Definition regis.c:85
bool RS_execute(Regis *r, char *str)
Definition regis.c:208
bool RS_isRegis(const char *str)
Definition regis.c:31
void NIStartBuild(IspellDict *Conf)
Definition spell.c:89
#define GETWCHAR(W, L, N, T)
Definition spell.c:192
static int strbcmp(const unsigned char *s1, const unsigned char *s2)
Definition spell.c:258
void NIFinishBuild(IspellDict *Conf)
Definition spell.c:104
void NIImportAffixes(IspellDict *Conf, const char *filename)
Definition spell.c:1423
static char * cpstrdup(IspellDict *Conf, const char *str)
Definition spell.c:163
#define GETCHAR(A, N, T)
Definition spell.c:193
static int parse_ooaffentry(char *str, char *type, char *flag, char *find, char *repl, char *mask)
Definition spell.c:858
static const char * getAffixFlagSet(IspellDict *Conf, char *s)
Definition spell.c:1156
static SPNode * mkSPNode(IspellDict *Conf, int low, int high, int level)
Definition spell.c:1637
static int cmpspell(const void *s1, const void *s2)
Definition spell.c:198
#define MAX_NORM
Definition spell.c:188
#define PAE_WAIT_REPL
Definition spell.c:778
static bool get_nextfield(char **str, char *next)
Definition spell.c:794
void NISortDictionary(IspellDict *Conf)
Definition spell.c:1719
static int FindWord(IspellDict *Conf, const char *word, const char *affixflag, int flag)
Definition spell.c:605
static AffixNodeData * FindAffixes(AffixNode *node, const char *word, int wrdlen, int *level, int type)
Definition spell.c:2026
static char * CheckAffix(const char *word, size_t len, AFFIX *Affix, int flagflags, char *newword, int *baselen)
Definition spell.c:2069
static SplitVar * CopyVar(SplitVar *s, int makedup)
Definition spell.c:2334
static void NIAddSpell(IspellDict *Conf, const char *word, const char *flag)
Definition spell.c:489
static void NIImportOOAffixes(IspellDict *Conf, const char *filename)
Definition spell.c:1194
#define COMPACT_ALLOC_CHUNK
Definition spell.c:127
static void addNorm(TSLexeme **lres, TSLexeme **lcur, char *word, int flags, uint16 NVariant)
Definition spell.c:2522
static void NIAddAffix(IspellDict *Conf, const char *flag, char flagflags, const char *mask, const char *find, const char *repl, int type)
Definition spell.c:680
static char * findchar(char *str, int c)
Definition spell.c:230
static const char * VoidString
Definition spell.c:195
static char ** NormalizeSubWord(IspellDict *Conf, const char *word, int flag)
Definition spell.c:2174
static int CheckCompoundAffixes(CMPDAffix **ptr, const char *word, int len, bool CheckInPlace)
Definition spell.c:2292
#define MAXNORMLEN
Definition spell.c:189
#define STRNCMP(s, p)
Definition spell.c:191
static void getNextFlagFromString(IspellDict *Conf, const char **sflagset, char *sflag)
Definition spell.c:350
void NISortAffixes(IspellDict *Conf)
Definition spell.c:1974
static int cmpcmdflag(const void *f1, const void *f2)
Definition spell.c:211
static char * findchar2(char *str, int c1, int c2)
Definition spell.c:243
#define PAE_INREPL
Definition spell.c:779
static void setCompoundAffixFlagValue(IspellDict *Conf, CompoundAffixFlag *entry, char *s, uint32 val)
Definition spell.c:1028
void NIImportDictionary(IspellDict *Conf, const char *filename)
Definition spell.c:520
static AffixNode * mkANode(IspellDict *Conf, int low, int high, int level, int type)
Definition spell.c:1828
static bool IsAffixFlagInUse(IspellDict *Conf, int affix, const char *affixflag)
Definition spell.c:457
static bool parse_affentry(char *str, char *mask, char *find, char *repl)
Definition spell.c:914
static SplitVar * SplitToVariants(IspellDict *Conf, SPNode *snode, SplitVar *orig, const char *word, int wordlen, int startpos, int minpos)
Definition spell.c:2372
static uint32 makeCompoundFlags(IspellDict *Conf, int affix)
Definition spell.c:1620
static int cmpspellaffix(const void *s1, const void *s2)
Definition spell.c:204
#define cpalloc(size)
Definition spell.c:159
static int strbncmp(const unsigned char *s1, const unsigned char *s2, size_t count)
Definition spell.c:281
TSLexeme * NINormalizeWord(IspellDict *Conf, const char *word)
Definition spell.c:2538
#define tmpalloc(sz)
Definition spell.c:80
#define cpalloc0(size)
Definition spell.c:160
static int MergeAffix(IspellDict *Conf, int a1, int a2)
Definition spell.c:1569
#define PAE_WAIT_FIND
Definition spell.c:776
static void * compact_palloc0(IspellDict *Conf, size_t size)
Definition spell.c:131
static int getCompoundAffixFlagValue(IspellDict *Conf, const char *s)
Definition spell.c:1120
static void AddStem(SplitVar *v, char *word)
Definition spell.c:2359
#define PAE_INMASK
Definition spell.c:775
#define PAE_INFIND
Definition spell.c:777
static int addToResult(char **forms, char **cur, char *word)
Definition spell.c:2159
#define COMPACT_MAX_REQ
Definition spell.c:128
#define PAE_WAIT_FLAG
Definition spell.c:781
#define PAE_WAIT_MASK
Definition spell.c:774
static void mkVoidAffix(IspellDict *Conf, bool issuffix, int startsuffix)
Definition spell.c:1905
static bool isAffixInUse(IspellDict *Conf, const char *affixflag)
Definition spell.c:1959
static void addCompoundAffixFlagValue(IspellDict *Conf, char *s, uint32 val)
Definition spell.c:1064
static char * lowerstr_ctx(IspellDict *Conf, const char *src)
Definition spell.c:176
#define PAE_WAIT_TYPE
Definition spell.c:780
static int cmpaffix(const void *s1, const void *s2)
Definition spell.c:312
#define FLAGNUM_MAXSIZE
Definition spell.h:182
#define FF_SUFFIX
Definition spell.h:121
#define FF_COMPOUNDFLAG
Definition spell.h:46
#define FF_PREFIX
Definition spell.h:122
#define ANHRDSZ
Definition spell.h:145
#define FF_COMPOUNDFLAGMASK
Definition spell.h:48
#define SPELLHDRSZ
Definition spell.h:82
#define FF_COMPOUNDFORBIDFLAG
Definition spell.h:114
#define FF_COMPOUNDBEGIN
Definition spell.h:43
#define FF_COMPOUNDPERMITFLAG
Definition spell.h:113
#define FF_CROSSPRODUCT
Definition spell.h:115
#define FF_COMPOUNDMIDDLE
Definition spell.h:44
@ FM_LONG
Definition spell.h:160
@ FM_CHAR
Definition spell.h:159
@ FM_NUM
Definition spell.h:161
#define SPNHDRSZ
Definition spell.h:56
#define FF_COMPOUNDONLY
Definition spell.h:42
#define FF_COMPOUNDLAST
Definition spell.h:45
int f1[ARRAY_SIZE]
int f2[ARRAY_SIZE]
void check_stack_depth(void)
Definition stack_depth.c:95
uint32 naff
Definition spell.h:133
AFFIX ** aff
Definition spell.h:134
struct AffixNode * node
Definition spell.h:135
uint32 isvoid
Definition spell.h:140
AffixNodeData data[FLEXIBLE_ARRAY_MEMBER]
Definition spell.h:142
uint32 length
Definition spell.h:141
int len
Definition spell.h:150
bool issuffix
Definition spell.h:151
const char * affix
Definition spell.h:149
union CompoundAffixFlag::@143 flag
FlagMode flagMode
Definition spell.h:178
const char * s
Definition spell.h:173
struct SPNode * node
Definition spell.h:35
Definition spell.h:51
SPNodeData data[FLEXIBLE_ARRAY_MEMBER]
Definition spell.h:53
uint32 length
Definition spell.h:52
int nstem
Definition spell.c:2285
struct SplitVar * next
Definition spell.c:2288
int lenstem
Definition spell.c:2286
char ** stem
Definition spell.c:2287
const char * find
Definition spell.h:96
uint32 isregis
Definition spell.h:94
uint32 type
Definition spell.h:91
Regis regis
Definition spell.h:106
const char * flag
Definition spell.h:89
uint32 replen
Definition spell.h:95
union aff_struct::@142 reg
regex_t * pregex
Definition spell.h:105
uint32 flagflags
Definition spell.h:92
const char * repl
Definition spell.h:97
uint32 issimple
Definition spell.h:93
char * flag(int b)
Definition test-ctype.c:33
bool tsearch_readline_begin(tsearch_readline_state *stp, const char *filename)
Definition ts_locale.c:85
char * tsearch_readline(tsearch_readline_state *stp)
Definition ts_locale.c:108
void tsearch_readline_end(tsearch_readline_state *stp)
Definition ts_locale.c:153
static int ts_copychar_with_len(void *dest, const void *src, int length)
Definition ts_locale.h:42
static int ts_copychar_cstr(void *dest, const void *src)
Definition ts_locale.h:50
#define t_iseq(x, c)
Definition ts_locale.h:38
const char * type