LCOV - code coverage report
Current view: top level - src/backend/tsearch - ts_parse.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 254 274 92.7 %
Date: 2019-11-15 23:07:02 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * ts_parse.c
       4             :  *      main parse functions for tsearch
       5             :  *
       6             :  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/tsearch/ts_parse.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : 
      15             : #include "postgres.h"
      16             : 
      17             : #include "tsearch/ts_cache.h"
      18             : #include "tsearch/ts_utils.h"
      19             : 
      20             : #define IGNORE_LONGLEXEME   1
      21             : 
      22             : /*
      23             :  * Lexize subsystem
      24             :  */
      25             : 
      26             : typedef struct ParsedLex
      27             : {
      28             :     int         type;
      29             :     char       *lemm;
      30             :     int         lenlemm;
      31             :     struct ParsedLex *next;
      32             : } ParsedLex;
      33             : 
      34             : typedef struct ListParsedLex
      35             : {
      36             :     ParsedLex  *head;
      37             :     ParsedLex  *tail;
      38             : } ListParsedLex;
      39             : 
      40             : typedef struct
      41             : {
      42             :     TSConfigCacheEntry *cfg;
      43             :     Oid         curDictId;
      44             :     int         posDict;
      45             :     DictSubState dictState;
      46             :     ParsedLex  *curSub;
      47             :     ListParsedLex towork;       /* current list to work */
      48             :     ListParsedLex waste;        /* list of lexemes that already lexized */
      49             : 
      50             :     /*
      51             :      * fields to store last variant to lexize (basically, thesaurus or similar
      52             :      * to, which wants  several lexemes
      53             :      */
      54             : 
      55             :     ParsedLex  *lastRes;
      56             :     TSLexeme   *tmpRes;
      57             : } LexizeData;
      58             : 
      59             : static void
      60        3056 : LexizeInit(LexizeData *ld, TSConfigCacheEntry *cfg)
      61             : {
      62        3056 :     ld->cfg = cfg;
      63        3056 :     ld->curDictId = InvalidOid;
      64        3056 :     ld->posDict = 0;
      65        3056 :     ld->towork.head = ld->towork.tail = ld->curSub = NULL;
      66        3056 :     ld->waste.head = ld->waste.tail = NULL;
      67        3056 :     ld->lastRes = NULL;
      68        3056 :     ld->tmpRes = NULL;
      69        3056 : }
      70             : 
      71             : static void
      72       30736 : LPLAddTail(ListParsedLex *list, ParsedLex *newpl)
      73             : {
      74       30736 :     if (list->tail)
      75             :     {
      76         148 :         list->tail->next = newpl;
      77         148 :         list->tail = newpl;
      78             :     }
      79             :     else
      80       30588 :         list->head = list->tail = newpl;
      81       30736 :     newpl->next = NULL;
      82       30736 : }
      83             : 
      84             : static ParsedLex *
      85       15368 : LPLRemoveHead(ListParsedLex *list)
      86             : {
      87       15368 :     ParsedLex  *res = list->head;
      88             : 
      89       15368 :     if (list->head)
      90       15368 :         list->head = list->head->next;
      91             : 
      92       15368 :     if (list->head == NULL)
      93       15284 :         list->tail = NULL;
      94             : 
      95       15368 :     return res;
      96             : }
      97             : 
      98             : static void
      99       15368 : LexizeAddLemm(LexizeData *ld, int type, char *lemm, int lenlemm)
     100             : {
     101       15368 :     ParsedLex  *newpl = (ParsedLex *) palloc(sizeof(ParsedLex));
     102             : 
     103       15368 :     newpl->type = type;
     104       15368 :     newpl->lemm = lemm;
     105       15368 :     newpl->lenlemm = lenlemm;
     106       15368 :     LPLAddTail(&ld->towork, newpl);
     107       15368 :     ld->curSub = ld->towork.tail;
     108       15368 : }
     109             : 
     110             : static void
     111       15368 : RemoveHead(LexizeData *ld)
     112             : {
     113       15368 :     LPLAddTail(&ld->waste, LPLRemoveHead(&ld->towork));
     114             : 
     115       15368 :     ld->posDict = 0;
     116       15368 : }
     117             : 
     118             : static void
     119       22858 : setCorrLex(LexizeData *ld, ParsedLex **correspondLexem)
     120             : {
     121       22858 :     if (correspondLexem)
     122             :     {
     123        6448 :         *correspondLexem = ld->waste.head;
     124             :     }
     125             :     else
     126             :     {
     127             :         ParsedLex  *tmp,
     128       16410 :                    *ptr = ld->waste.head;
     129             : 
     130       43848 :         while (ptr)
     131             :         {
     132       11028 :             tmp = ptr->next;
     133       11028 :             pfree(ptr);
     134       11028 :             ptr = tmp;
     135             :         }
     136             :     }
     137       22858 :     ld->waste.head = ld->waste.tail = NULL;
     138       22858 : }
     139             : 
     140             : static void
     141          32 : moveToWaste(LexizeData *ld, ParsedLex *stop)
     142             : {
     143          32 :     bool        go = true;
     144             : 
     145         152 :     while (ld->towork.head && go)
     146             :     {
     147          88 :         if (ld->towork.head == stop)
     148             :         {
     149          32 :             ld->curSub = stop->next;
     150          32 :             go = false;
     151             :         }
     152          88 :         RemoveHead(ld);
     153             :     }
     154          32 : }
     155             : 
     156             : static void
     157          32 : setNewTmpRes(LexizeData *ld, ParsedLex *lex, TSLexeme *res)
     158             : {
     159          32 :     if (ld->tmpRes)
     160             :     {
     161             :         TSLexeme   *ptr;
     162             : 
     163          16 :         for (ptr = ld->tmpRes; ptr->lexeme; ptr++)
     164           8 :             pfree(ptr->lexeme);
     165           8 :         pfree(ld->tmpRes);
     166             :     }
     167          32 :     ld->tmpRes = res;
     168          32 :     ld->lastRes = lex;
     169          32 : }
     170             : 
     171             : static TSLexeme *
     172       22890 : LexizeExec(LexizeData *ld, ParsedLex **correspondLexem)
     173             : {
     174             :     int         i;
     175             :     ListDictionary *map;
     176             :     TSDictionaryCacheEntry *dict;
     177             :     TSLexeme   *res;
     178             : 
     179       22890 :     if (ld->curDictId == InvalidOid)
     180             :     {
     181             :         /*
     182             :          * usual mode: dictionary wants only one word, but we should keep in
     183             :          * mind that we should go through all stack
     184             :          */
     185             : 
     186       53370 :         while (ld->towork.head)
     187             :         {
     188       15312 :             ParsedLex  *curVal = ld->towork.head;
     189       15312 :             char       *curValLemm = curVal->lemm;
     190       15312 :             int         curValLenLemm = curVal->lenlemm;
     191             : 
     192       15312 :             map = ld->cfg->map + curVal->type;
     193             : 
     194       15312 :             if (curVal->type == 0 || curVal->type >= ld->cfg->lenmap || map->len == 0)
     195             :             {
     196             :                 /* skip this type of lexeme */
     197        7822 :                 RemoveHead(ld);
     198        7822 :                 continue;
     199             :             }
     200             : 
     201        7818 :             for (i = ld->posDict; i < map->len; i++)
     202             :             {
     203        7818 :                 dict = lookup_ts_dictionary_cache(map->dictIds[i]);
     204             : 
     205        7818 :                 ld->dictState.isend = ld->dictState.getnext = false;
     206        7818 :                 ld->dictState.private_state = NULL;
     207        7818 :                 res = (TSLexeme *) DatumGetPointer(FunctionCall4(
     208             :                                                                  &(dict->lexize),
     209             :                                                                  PointerGetDatum(dict->dictData),
     210             :                                                                  PointerGetDatum(curValLemm),
     211             :                                                                  Int32GetDatum(curValLenLemm),
     212             :                                                                  PointerGetDatum(&ld->dictState)
     213             :                                                                  ));
     214             : 
     215        7818 :                 if (ld->dictState.getnext)
     216             :                 {
     217             :                     /*
     218             :                      * dictionary wants next word, so setup and store current
     219             :                      * position and go to multiword mode
     220             :                      */
     221             : 
     222          32 :                     ld->curDictId = DatumGetObjectId(map->dictIds[i]);
     223          32 :                     ld->posDict = i + 1;
     224          32 :                     ld->curSub = curVal->next;
     225          32 :                     if (res)
     226          24 :                         setNewTmpRes(ld, curVal, res);
     227          32 :                     return LexizeExec(ld, correspondLexem);
     228             :                 }
     229             : 
     230        7786 :                 if (!res)       /* dictionary doesn't know this lexeme */
     231         328 :                     continue;
     232             : 
     233        7458 :                 if (res->flags & TSL_FILTER)
     234             :                 {
     235           0 :                     curValLemm = res->lexeme;
     236           0 :                     curValLenLemm = strlen(res->lexeme);
     237           0 :                     continue;
     238             :                 }
     239             : 
     240        7458 :                 RemoveHead(ld);
     241        7458 :                 setCorrLex(ld, correspondLexem);
     242        7458 :                 return res;
     243             :             }
     244             : 
     245           0 :             RemoveHead(ld);
     246             :         }
     247             :     }
     248             :     else
     249             :     {                           /* curDictId is valid */
     250         116 :         dict = lookup_ts_dictionary_cache(ld->curDictId);
     251             : 
     252             :         /*
     253             :          * Dictionary ld->curDictId asks  us about following words
     254             :          */
     255             : 
     256         116 :         while (ld->curSub)
     257             :         {
     258          84 :             ParsedLex  *curVal = ld->curSub;
     259             : 
     260          84 :             map = ld->cfg->map + curVal->type;
     261             : 
     262          84 :             if (curVal->type != 0)
     263             :             {
     264          80 :                 bool        dictExists = false;
     265             : 
     266          80 :                 if (curVal->type >= ld->cfg->lenmap || map->len == 0)
     267             :                 {
     268             :                     /* skip this type of lexeme */
     269          40 :                     ld->curSub = curVal->next;
     270          40 :                     continue;
     271             :                 }
     272             : 
     273             :                 /*
     274             :                  * We should be sure that current type of lexeme is recognized
     275             :                  * by our dictionary: we just check is it exist in list of
     276             :                  * dictionaries ?
     277             :                  */
     278         120 :                 for (i = 0; i < map->len && !dictExists; i++)
     279          80 :                     if (ld->curDictId == DatumGetObjectId(map->dictIds[i]))
     280          40 :                         dictExists = true;
     281             : 
     282          40 :                 if (!dictExists)
     283             :                 {
     284             :                     /*
     285             :                      * Dictionary can't work with current type of lexeme,
     286             :                      * return to basic mode and redo all stored lexemes
     287             :                      */
     288           0 :                     ld->curDictId = InvalidOid;
     289           0 :                     return LexizeExec(ld, correspondLexem);
     290             :                 }
     291             :             }
     292             : 
     293          44 :             ld->dictState.isend = (curVal->type == 0) ? true : false;
     294          44 :             ld->dictState.getnext = false;
     295             : 
     296          44 :             res = (TSLexeme *) DatumGetPointer(FunctionCall4(
     297             :                                                              &(dict->lexize),
     298             :                                                              PointerGetDatum(dict->dictData),
     299             :                                                              PointerGetDatum(curVal->lemm),
     300             :                                                              Int32GetDatum(curVal->lenlemm),
     301             :                                                              PointerGetDatum(&ld->dictState)
     302             :                                                              ));
     303             : 
     304          44 :             if (ld->dictState.getnext)
     305             :             {
     306             :                 /* Dictionary wants one more */
     307          12 :                 ld->curSub = curVal->next;
     308          12 :                 if (res)
     309           8 :                     setNewTmpRes(ld, curVal, res);
     310          12 :                 continue;
     311             :             }
     312             : 
     313          32 :             if (res || ld->tmpRes)
     314             :             {
     315             :                 /*
     316             :                  * Dictionary normalizes lexemes, so we remove from stack all
     317             :                  * used lexemes, return to basic mode and redo end of stack
     318             :                  * (if it exists)
     319             :                  */
     320          32 :                 if (res)
     321             :                 {
     322          16 :                     moveToWaste(ld, ld->curSub);
     323             :                 }
     324             :                 else
     325             :                 {
     326          16 :                     res = ld->tmpRes;
     327          16 :                     moveToWaste(ld, ld->lastRes);
     328             :                 }
     329             : 
     330             :                 /* reset to initial state */
     331          32 :                 ld->curDictId = InvalidOid;
     332          32 :                 ld->posDict = 0;
     333          32 :                 ld->lastRes = NULL;
     334          32 :                 ld->tmpRes = NULL;
     335          32 :                 setCorrLex(ld, correspondLexem);
     336          32 :                 return res;
     337             :             }
     338             : 
     339             :             /*
     340             :              * Dict don't want next lexem and didn't recognize anything, redo
     341             :              * from ld->towork.head
     342             :              */
     343           0 :             ld->curDictId = InvalidOid;
     344           0 :             return LexizeExec(ld, correspondLexem);
     345             :         }
     346             :     }
     347             : 
     348       15368 :     setCorrLex(ld, correspondLexem);
     349       15368 :     return NULL;
     350             : }
     351             : 
     352             : /*
     353             :  * Parse string and lexize words.
     354             :  *
     355             :  * prs will be filled in.
     356             :  */
     357             : void
     358        2850 : parsetext(Oid cfgId, ParsedText *prs, char *buf, int buflen)
     359             : {
     360             :     int         type,
     361             :                 lenlemm;
     362        2850 :     char       *lemm = NULL;
     363             :     LexizeData  ldata;
     364             :     TSLexeme   *norms;
     365             :     TSConfigCacheEntry *cfg;
     366             :     TSParserCacheEntry *prsobj;
     367             :     void       *prsdata;
     368             : 
     369        2850 :     cfg = lookup_ts_config_cache(cfgId);
     370        2850 :     prsobj = lookup_ts_parser_cache(cfg->prsId);
     371             : 
     372        2850 :     prsdata = (void *) DatumGetPointer(FunctionCall2(&prsobj->prsstart,
     373             :                                                      PointerGetDatum(buf),
     374             :                                                      Int32GetDatum(buflen)));
     375             : 
     376        2850 :     LexizeInit(&ldata, cfg);
     377             : 
     378             :     do
     379             :     {
     380       11028 :         type = DatumGetInt32(FunctionCall3(&(prsobj->prstoken),
     381             :                                            PointerGetDatum(prsdata),
     382             :                                            PointerGetDatum(&lemm),
     383             :                                            PointerGetDatum(&lenlemm)));
     384             : 
     385       11028 :         if (type > 0 && lenlemm >= MAXSTRLEN)
     386             :         {
     387             : #ifdef IGNORE_LONGLEXEME
     388           0 :             ereport(NOTICE,
     389             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     390             :                      errmsg("word is too long to be indexed"),
     391             :                      errdetail("Words longer than %d characters are ignored.",
     392             :                                MAXSTRLEN)));
     393           0 :             continue;
     394             : #else
     395             :             ereport(ERROR,
     396             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     397             :                      errmsg("word is too long to be indexed"),
     398             :                      errdetail("Words longer than %d characters are ignored.",
     399             :                                MAXSTRLEN)));
     400             : #endif
     401             :         }
     402             : 
     403       11028 :         LexizeAddLemm(&ldata, type, lemm, lenlemm);
     404             : 
     405       27438 :         while ((norms = LexizeExec(&ldata, NULL)) != NULL)
     406             :         {
     407        5382 :             TSLexeme   *ptr = norms;
     408             : 
     409        5382 :             prs->pos++;          /* set pos */
     410             : 
     411       15342 :             while (ptr->lexeme)
     412             :             {
     413        4578 :                 if (prs->curwords == prs->lenwords)
     414             :                 {
     415         170 :                     prs->lenwords *= 2;
     416         170 :                     prs->words = (ParsedWord *) repalloc((void *) prs->words, prs->lenwords * sizeof(ParsedWord));
     417             :                 }
     418             : 
     419        4578 :                 if (ptr->flags & TSL_ADDPOS)
     420          16 :                     prs->pos++;
     421        4578 :                 prs->words[prs->curwords].len = strlen(ptr->lexeme);
     422        4578 :                 prs->words[prs->curwords].word = ptr->lexeme;
     423        4578 :                 prs->words[prs->curwords].nvariant = ptr->nvariant;
     424        4578 :                 prs->words[prs->curwords].flags = ptr->flags & TSL_PREFIX;
     425        4578 :                 prs->words[prs->curwords].alen = 0;
     426        4578 :                 prs->words[prs->curwords].pos.pos = LIMITPOS(prs->pos);
     427        4578 :                 ptr++;
     428        4578 :                 prs->curwords++;
     429             :             }
     430        5382 :             pfree(norms);
     431             :         }
     432       11028 :     } while (type > 0);
     433             : 
     434        2850 :     FunctionCall1(&(prsobj->prsend), PointerGetDatum(prsdata));
     435        2850 : }
     436             : 
     437             : /*
     438             :  * Headline framework
     439             :  */
     440             : static void
     441        4134 : hladdword(HeadlineParsedText *prs, char *buf, int buflen, int type)
     442             : {
     443        8296 :     while (prs->curwords >= prs->lenwords)
     444             :     {
     445          28 :         prs->lenwords *= 2;
     446          28 :         prs->words = (HeadlineWordEntry *) repalloc((void *) prs->words, prs->lenwords * sizeof(HeadlineWordEntry));
     447             :     }
     448        4134 :     memset(&(prs->words[prs->curwords]), 0, sizeof(HeadlineWordEntry));
     449        4134 :     prs->words[prs->curwords].type = (uint8) type;
     450        4134 :     prs->words[prs->curwords].len = buflen;
     451        4134 :     prs->words[prs->curwords].word = palloc(buflen);
     452        4134 :     memcpy(prs->words[prs->curwords].word, buf, buflen);
     453        4134 :     prs->curwords++;
     454        4134 : }
     455             : 
     456             : static void
     457        1388 : hlfinditem(HeadlineParsedText *prs, TSQuery query, int32 pos, char *buf, int buflen)
     458             : {
     459             :     int         i;
     460        1388 :     QueryItem  *item = GETQUERY(query);
     461             :     HeadlineWordEntry *word;
     462             : 
     463        2824 :     while (prs->curwords + query->size >= prs->lenwords)
     464             :     {
     465          48 :         prs->lenwords *= 2;
     466          48 :         prs->words = (HeadlineWordEntry *) repalloc((void *) prs->words, prs->lenwords * sizeof(HeadlineWordEntry));
     467             :     }
     468             : 
     469        1388 :     word = &(prs->words[prs->curwords - 1]);
     470        1388 :     word->pos = LIMITPOS(pos);
     471        6192 :     for (i = 0; i < query->size; i++)
     472             :     {
     473        7900 :         if (item->type == QI_VAL &&
     474        3096 :             tsCompareString(GETOPERAND(query) + item->qoperand.distance, item->qoperand.length,
     475        3096 :                             buf, buflen, item->qoperand.prefix) == 0)
     476             :         {
     477         258 :             if (word->item)
     478             :             {
     479           0 :                 memcpy(&(prs->words[prs->curwords]), word, sizeof(HeadlineWordEntry));
     480           0 :                 prs->words[prs->curwords].item = &item->qoperand;
     481           0 :                 prs->words[prs->curwords].repeated = 1;
     482           0 :                 prs->curwords++;
     483             :             }
     484             :             else
     485         258 :                 word->item = &item->qoperand;
     486             :         }
     487        4804 :         item++;
     488             :     }
     489        1388 : }
     490             : 
     491             : static void
     492        6448 : addHLParsedLex(HeadlineParsedText *prs, TSQuery query, ParsedLex *lexs, TSLexeme *norms)
     493             : {
     494             :     ParsedLex  *tmplexs;
     495             :     TSLexeme   *ptr;
     496             :     int32       savedpos;
     497             : 
     498       17236 :     while (lexs)
     499             :     {
     500        4340 :         if (lexs->type > 0)
     501        4134 :             hladdword(prs, lexs->lemm, lexs->lenlemm, lexs->type);
     502             : 
     503        4340 :         ptr = norms;
     504        4340 :         savedpos = prs->vectorpos;
     505       10068 :         while (ptr && ptr->lexeme)
     506             :         {
     507        1388 :             if (ptr->flags & TSL_ADDPOS)
     508           0 :                 savedpos++;
     509        1388 :             hlfinditem(prs, query, savedpos, ptr->lexeme, strlen(ptr->lexeme));
     510        1388 :             ptr++;
     511             :         }
     512             : 
     513        4340 :         tmplexs = lexs->next;
     514        4340 :         pfree(lexs);
     515        4340 :         lexs = tmplexs;
     516             :     }
     517             : 
     518        6448 :     if (norms)
     519             :     {
     520        2108 :         ptr = norms;
     521        5604 :         while (ptr->lexeme)
     522             :         {
     523        1388 :             if (ptr->flags & TSL_ADDPOS)
     524           0 :                 prs->vectorpos++;
     525        1388 :             pfree(ptr->lexeme);
     526        1388 :             ptr++;
     527             :         }
     528        2108 :         pfree(norms);
     529             :     }
     530        6448 : }
     531             : 
     532             : void
     533         206 : hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query, char *buf, int buflen)
     534             : {
     535             :     int         type,
     536             :                 lenlemm;
     537         206 :     char       *lemm = NULL;
     538             :     LexizeData  ldata;
     539             :     TSLexeme   *norms;
     540             :     ParsedLex  *lexs;
     541             :     TSConfigCacheEntry *cfg;
     542             :     TSParserCacheEntry *prsobj;
     543             :     void       *prsdata;
     544             : 
     545         206 :     cfg = lookup_ts_config_cache(cfgId);
     546         206 :     prsobj = lookup_ts_parser_cache(cfg->prsId);
     547             : 
     548         206 :     prsdata = (void *) DatumGetPointer(FunctionCall2(&(prsobj->prsstart),
     549             :                                                      PointerGetDatum(buf),
     550             :                                                      Int32GetDatum(buflen)));
     551             : 
     552         206 :     LexizeInit(&ldata, cfg);
     553             : 
     554             :     do
     555             :     {
     556        4340 :         type = DatumGetInt32(FunctionCall3(&(prsobj->prstoken),
     557             :                                            PointerGetDatum(prsdata),
     558             :                                            PointerGetDatum(&lemm),
     559             :                                            PointerGetDatum(&lenlemm)));
     560             : 
     561        4340 :         if (type > 0 && lenlemm >= MAXSTRLEN)
     562             :         {
     563             : #ifdef IGNORE_LONGLEXEME
     564           0 :             ereport(NOTICE,
     565             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     566             :                      errmsg("word is too long to be indexed"),
     567             :                      errdetail("Words longer than %d characters are ignored.",
     568             :                                MAXSTRLEN)));
     569           0 :             continue;
     570             : #else
     571             :             ereport(ERROR,
     572             :                     (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     573             :                      errmsg("word is too long to be indexed"),
     574             :                      errdetail("Words longer than %d characters are ignored.",
     575             :                                MAXSTRLEN)));
     576             : #endif
     577             :         }
     578             : 
     579        4340 :         LexizeAddLemm(&ldata, type, lemm, lenlemm);
     580             : 
     581             :         do
     582             :         {
     583        6448 :             if ((norms = LexizeExec(&ldata, &lexs)) != NULL)
     584             :             {
     585        2108 :                 prs->vectorpos++;
     586        2108 :                 addHLParsedLex(prs, query, lexs, norms);
     587             :             }
     588             :             else
     589        4340 :                 addHLParsedLex(prs, query, lexs, NULL);
     590        6448 :         } while (norms);
     591             : 
     592        4340 :     } while (type > 0);
     593             : 
     594         206 :     FunctionCall1(&(prsobj->prsend), PointerGetDatum(prsdata));
     595         206 : }
     596             : 
     597             : text *
     598         206 : generateHeadline(HeadlineParsedText *prs)
     599             : {
     600             :     text       *out;
     601             :     char       *ptr;
     602         206 :     int         len = 128;
     603         206 :     int         numfragments = 0;
     604         206 :     int16       infrag = 0;
     605             : 
     606         206 :     HeadlineWordEntry *wrd = prs->words;
     607             : 
     608         206 :     out = (text *) palloc(len);
     609         206 :     ptr = ((char *) out) + VARHDRSZ;
     610             : 
     611        4546 :     while (wrd - prs->words < prs->curwords)
     612             :     {
     613        8308 :         while (wrd->len + prs->stopsellen + prs->startsellen + prs->fragdelimlen + (ptr - ((char *) out)) >= len)
     614             :         {
     615          40 :             int         dist = ptr - ((char *) out);
     616             : 
     617          40 :             len *= 2;
     618          40 :             out = (text *) repalloc(out, len);
     619          40 :             ptr = ((char *) out) + dist;
     620             :         }
     621             : 
     622        4134 :         if (wrd->in && !wrd->repeated)
     623             :         {
     624        2398 :             if (!infrag)
     625             :             {
     626             : 
     627             :                 /* start of a new fragment */
     628         214 :                 infrag = 1;
     629         214 :                 numfragments++;
     630             :                 /* add a fragment delimiter if this is after the first one */
     631         214 :                 if (numfragments > 1)
     632             :                 {
     633           8 :                     memcpy(ptr, prs->fragdelim, prs->fragdelimlen);
     634           8 :                     ptr += prs->fragdelimlen;
     635             :                 }
     636             : 
     637             :             }
     638        4796 :             if (wrd->replace)
     639             :             {
     640           0 :                 *ptr = ' ';
     641           0 :                 ptr++;
     642             :             }
     643        2398 :             else if (!wrd->skip)
     644             :             {
     645        2394 :                 if (wrd->selected)
     646             :                 {
     647         218 :                     memcpy(ptr, prs->startsel, prs->startsellen);
     648         218 :                     ptr += prs->startsellen;
     649             :                 }
     650        2394 :                 memcpy(ptr, wrd->word, wrd->len);
     651        2394 :                 ptr += wrd->len;
     652        2394 :                 if (wrd->selected)
     653             :                 {
     654         218 :                     memcpy(ptr, prs->stopsel, prs->stopsellen);
     655         218 :                     ptr += prs->stopsellen;
     656             :                 }
     657             :             }
     658             :         }
     659        1736 :         else if (!wrd->repeated)
     660             :         {
     661        1736 :             if (infrag)
     662          48 :                 infrag = 0;
     663        1736 :             pfree(wrd->word);
     664             :         }
     665             : 
     666        4134 :         wrd++;
     667             :     }
     668             : 
     669         206 :     SET_VARSIZE(out, ptr - ((char *) out));
     670         206 :     return out;
     671             : }

Generated by: LCOV version 1.13