LCOV - differential code coverage report
Current view: top level - src/backend/tsearch - spell.c (source / functions) Coverage Total Hit UNC LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 92.1 % 1127 1038 3 16 46 24 33 435 8 562 30 450 2 10
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 46 46 28 18 28
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 [..60] days: 75.0 % 4 3 1 3
Legend: Lines: hit not hit (60,120] days: 71.4 % 7 5 2 5
(180,240] days: 100.0 % 1 1 1 1
(240..) days: 92.3 % 1115 1029 16 46 24 33 435 561 29 450
Function coverage date bins:
(240..) days: 62.2 % 74 46 28 18 28

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

Generated by: LCOV version v1.16-55-g56c0a2a