LCOV - differential code coverage report
Current view: top level - src/backend/tsearch - spell.c (source / functions) Coverage Total Hit UNC UBC GNC CBC DUB DCB
Current: f76c13edadc2b319036e0d238703ed56bb23f934 vs 9e17d25e79d4756be08b4a5521b4b58450217137 Lines: 92.1 % 1159 1067 4 88 11 1056 6 12
Current Date: 2026-09-20 14:13:17 +0900 Functions: 100.0 % 48 48 11 37 1
Baseline: lcov-20260920-baseline Branches: 73.6 % 923 679 244 4 675
Baseline Date: 2026-09-20 14:13:13 +0900 Line coverage date bins:
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
(7,30] days: 93.9 % 33 31 2 8 23
(30,360] days: 90.7 % 86 78 4 4 3 75
(360..) days: 92.1 % 1040 958 82 958
Function coverage date bins:
(7,30] days: 100.0 % 3 3 2 1
(30,360] days: 100.0 % 2 2 1 1
(360..) days: 100.0 % 43 43 8 35
Branch coverage date bins:
(7,30] days: 54.5 % 22 12 10 2 10
(30,360] days: 59.4 % 64 38 26 2 36
(360..) days: 75.1 % 837 629 208 629

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

Generated by: LCOV version 2.0-1