LCOV - code coverage report
Current view: top level - src/backend/tsearch - ts_parse.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 254 274 92.7 %
Date: 2017-09-29 15:12:54 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-2017, 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         448 : LexizeInit(LexizeData *ld, TSConfigCacheEntry *cfg)
      61             : {
      62         448 :     ld->cfg = cfg;
      63         448 :     ld->curDictId = InvalidOid;
      64         448 :     ld->posDict = 0;
      65         448 :     ld->towork.head = ld->towork.tail = ld->curSub = NULL;
      66         448 :     ld->waste.head = ld->waste.tail = NULL;
      67         448 :     ld->lastRes = NULL;
      68         448 :     ld->tmpRes = NULL;
      69         448 : }
      70             : 
      71             : static void
      72        6134 : LPLAddTail(ListParsedLex *list, ParsedLex *newpl)
      73             : {
      74        6134 :     if (list->tail)
      75             :     {
      76          37 :         list->tail->next = newpl;
      77          37 :         list->tail = newpl;
      78             :     }
      79             :     else
      80        6097 :         list->head = list->tail = newpl;
      81        6134 :     newpl->next = NULL;
      82        6134 : }
      83             : 
      84             : static ParsedLex *
      85        3067 : LPLRemoveHead(ListParsedLex *list)
      86             : {
      87        3067 :     ParsedLex  *res = list->head;
      88             : 
      89        3067 :     if (list->head)
      90        3067 :         list->head = list->head->next;
      91             : 
      92        3067 :     if (list->head == NULL)
      93        3046 :         list->tail = NULL;
      94             : 
      95        3067 :     return res;
      96             : }
      97             : 
      98             : static void
      99        3067 : LexizeAddLemm(LexizeData *ld, int type, char *lemm, int lenlemm)
     100             : {
     101        3067 :     ParsedLex  *newpl = (ParsedLex *) palloc(sizeof(ParsedLex));
     102             : 
     103        3067 :     newpl->type = type;
     104        3067 :     newpl->lemm = lemm;
     105        3067 :     newpl->lenlemm = lenlemm;
     106        3067 :     LPLAddTail(&ld->towork, newpl);
     107        3067 :     ld->curSub = ld->towork.tail;
     108        3067 : }
     109             : 
     110             : static void
     111        3067 : RemoveHead(LexizeData *ld)
     112             : {
     113        3067 :     LPLAddTail(&ld->waste, LPLRemoveHead(&ld->towork));
     114             : 
     115        3067 :     ld->posDict = 0;
     116        3067 : }
     117             : 
     118             : static void
     119        4571 : setCorrLex(LexizeData *ld, ParsedLex **correspondLexem)
     120             : {
     121        4571 :     if (correspondLexem)
     122             :     {
     123        1600 :         *correspondLexem = ld->waste.head;
     124             :     }
     125             :     else
     126             :     {
     127             :         ParsedLex  *tmp,
     128        2971 :                    *ptr = ld->waste.head;
     129             : 
     130        7932 :         while (ptr)
     131             :         {
     132        1990 :             tmp = ptr->next;
     133        1990 :             pfree(ptr);
     134        1990 :             ptr = tmp;
     135             :         }
     136             :     }
     137        4571 :     ld->waste.head = ld->waste.tail = NULL;
     138        4571 : }
     139             : 
     140             : static void
     141           8 : moveToWaste(LexizeData *ld, ParsedLex *stop)
     142             : {
     143           8 :     bool        go = true;
     144             : 
     145          38 :     while (ld->towork.head && go)
     146             :     {
     147          22 :         if (ld->towork.head == stop)
     148             :         {
     149           8 :             ld->curSub = stop->next;
     150           8 :             go = false;
     151             :         }
     152          22 :         RemoveHead(ld);
     153             :     }
     154           8 : }
     155             : 
     156             : static void
     157           8 : setNewTmpRes(LexizeData *ld, ParsedLex *lex, TSLexeme *res)
     158             : {
     159           8 :     if (ld->tmpRes)
     160             :     {
     161             :         TSLexeme   *ptr;
     162             : 
     163           4 :         for (ptr = ld->tmpRes; ptr->lexeme; ptr++)
     164           2 :             pfree(ptr->lexeme);
     165           2 :         pfree(ld->tmpRes);
     166             :     }
     167           8 :     ld->tmpRes = res;
     168           8 :     ld->lastRes = lex;
     169           8 : }
     170             : 
     171             : static TSLexeme *
     172        4579 : LexizeExec(LexizeData *ld, ParsedLex **correspondLexem)
     173             : {
     174             :     int         i;
     175             :     ListDictionary *map;
     176             :     TSDictionaryCacheEntry *dict;
     177             :     TSLexeme   *res;
     178             : 
     179        4579 :     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       10649 :         while (ld->towork.head)
     187             :         {
     188        3053 :             ParsedLex  *curVal = ld->towork.head;
     189        3053 :             char       *curValLemm = curVal->lemm;
     190        3053 :             int         curValLenLemm = curVal->lenlemm;
     191             : 
     192        3053 :             map = ld->cfg->map + curVal->type;
     193             : 
     194        3053 :             if (curVal->type == 0 || curVal->type >= ld->cfg->lenmap || map->len == 0)
     195             :             {
     196             :                 /* skip this type of lexeme */
     197        1549 :                 RemoveHead(ld);
     198        1549 :                 continue;
     199             :             }
     200             : 
     201        1586 :             for (i = ld->posDict; i < map->len; i++)
     202             :             {
     203        1586 :                 dict = lookup_ts_dictionary_cache(map->dictIds[i]);
     204             : 
     205        1586 :                 ld->dictState.isend = ld->dictState.getnext = false;
     206        1586 :                 ld->dictState.private_state = NULL;
     207        1586 :                 res = (TSLexeme *) DatumGetPointer(FunctionCall4(
     208             :                                                                  &(dict->lexize),
     209             :                                                                  PointerGetDatum(dict->dictData),
     210             :                                                                  PointerGetDatum(curValLemm),
     211             :                                                                  Int32GetDatum(curValLenLemm),
     212             :                                                                  PointerGetDatum(&ld->dictState)
     213             :                                                                  ));
     214             : 
     215        1586 :                 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           8 :                     ld->curDictId = DatumGetObjectId(map->dictIds[i]);
     223           8 :                     ld->posDict = i + 1;
     224           8 :                     ld->curSub = curVal->next;
     225           8 :                     if (res)
     226           6 :                         setNewTmpRes(ld, curVal, res);
     227           8 :                     return LexizeExec(ld, correspondLexem);
     228             :                 }
     229             : 
     230        1578 :                 if (!res)       /* dictionary doesn't know this lexeme */
     231          82 :                     continue;
     232             : 
     233        1496 :                 if (res->flags & TSL_FILTER)
     234             :                 {
     235           0 :                     curValLemm = res->lexeme;
     236           0 :                     curValLenLemm = strlen(res->lexeme);
     237           0 :                     continue;
     238             :                 }
     239             : 
     240        1496 :                 RemoveHead(ld);
     241        1496 :                 setCorrLex(ld, correspondLexem);
     242        1496 :                 return res;
     243             :             }
     244             : 
     245           0 :             RemoveHead(ld);
     246             :         }
     247             :     }
     248             :     else
     249             :     {                           /* curDictId is valid */
     250          29 :         dict = lookup_ts_dictionary_cache(ld->curDictId);
     251             : 
     252             :         /*
     253             :          * Dictionary ld->curDictId asks  us about following words
     254             :          */
     255             : 
     256          29 :         while (ld->curSub)
     257             :         {
     258          21 :             ParsedLex  *curVal = ld->curSub;
     259             : 
     260          21 :             map = ld->cfg->map + curVal->type;
     261             : 
     262          21 :             if (curVal->type != 0)
     263             :             {
     264          20 :                 bool        dictExists = false;
     265             : 
     266          20 :                 if (curVal->type >= ld->cfg->lenmap || map->len == 0)
     267             :                 {
     268             :                     /* skip this type of lexeme */
     269          10 :                     ld->curSub = curVal->next;
     270          10 :                     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          30 :                 for (i = 0; i < map->len && !dictExists; i++)
     279          20 :                     if (ld->curDictId == DatumGetObjectId(map->dictIds[i]))
     280          10 :                         dictExists = true;
     281             : 
     282          10 :                 if (!dictExists)
     283             :                 {
     284             :                     /*
     285             :                      * Dictionary can't work with current tpe 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          11 :             ld->dictState.isend = (curVal->type == 0) ? true : false;
     294          11 :             ld->dictState.getnext = false;
     295             : 
     296          11 :             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          11 :             if (ld->dictState.getnext)
     305             :             {
     306             :                 /* Dictionary wants one more */
     307           3 :                 ld->curSub = curVal->next;
     308           3 :                 if (res)
     309           2 :                     setNewTmpRes(ld, curVal, res);
     310           3 :                 continue;
     311             :             }
     312             : 
     313           8 :             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           8 :                 if (res)
     321             :                 {
     322           4 :                     moveToWaste(ld, ld->curSub);
     323             :                 }
     324             :                 else
     325             :                 {
     326           4 :                     res = ld->tmpRes;
     327           4 :                     moveToWaste(ld, ld->lastRes);
     328             :                 }
     329             : 
     330             :                 /* reset to initial state */
     331           8 :                 ld->curDictId = InvalidOid;
     332           8 :                 ld->posDict = 0;
     333           8 :                 ld->lastRes = NULL;
     334           8 :                 ld->tmpRes = NULL;
     335           8 :                 setCorrLex(ld, correspondLexem);
     336           8 :                 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        3067 :     setCorrLex(ld, correspondLexem);
     349        3067 :     return NULL;
     350             : }
     351             : 
     352             : /*
     353             :  * Parse string and lexize words.
     354             :  *
     355             :  * prs will be filled in.
     356             :  */
     357             : void
     358         397 : parsetext(Oid cfgId, ParsedText *prs, char *buf, int buflen)
     359             : {
     360             :     int         type,
     361             :                 lenlemm;
     362         397 :     char       *lemm = NULL;
     363             :     LexizeData  ldata;
     364             :     TSLexeme   *norms;
     365             :     TSConfigCacheEntry *cfg;
     366             :     TSParserCacheEntry *prsobj;
     367             :     void       *prsdata;
     368             : 
     369         397 :     cfg = lookup_ts_config_cache(cfgId);
     370         397 :     prsobj = lookup_ts_parser_cache(cfg->prsId);
     371             : 
     372         397 :     prsdata = (void *) DatumGetPointer(FunctionCall2(&prsobj->prsstart,
     373             :                                                      PointerGetDatum(buf),
     374             :                                                      Int32GetDatum(buflen)));
     375             : 
     376         397 :     LexizeInit(&ldata, cfg);
     377             : 
     378             :     do
     379             :     {
     380        1990 :         type = DatumGetInt32(FunctionCall3(&(prsobj->prstoken),
     381             :                                            PointerGetDatum(prsdata),
     382             :                                            PointerGetDatum(&lemm),
     383             :                                            PointerGetDatum(&lenlemm)));
     384             : 
     385        1990 :         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        1990 :         LexizeAddLemm(&ldata, type, lemm, lenlemm);
     404             : 
     405        4961 :         while ((norms = LexizeExec(&ldata, NULL)) != NULL)
     406             :         {
     407         981 :             TSLexeme   *ptr = norms;
     408             : 
     409         981 :             prs->pos++;          /* set pos */
     410             : 
     411        2800 :             while (ptr->lexeme)
     412             :             {
     413         838 :                 if (prs->curwords == prs->lenwords)
     414             :                 {
     415          40 :                     prs->lenwords *= 2;
     416          40 :                     prs->words = (ParsedWord *) repalloc((void *) prs->words, prs->lenwords * sizeof(ParsedWord));
     417             :                 }
     418             : 
     419         838 :                 if (ptr->flags & TSL_ADDPOS)
     420           4 :                     prs->pos++;
     421         838 :                 prs->words[prs->curwords].len = strlen(ptr->lexeme);
     422         838 :                 prs->words[prs->curwords].word = ptr->lexeme;
     423         838 :                 prs->words[prs->curwords].nvariant = ptr->nvariant;
     424         838 :                 prs->words[prs->curwords].flags = ptr->flags & TSL_PREFIX;
     425         838 :                 prs->words[prs->curwords].alen = 0;
     426         838 :                 prs->words[prs->curwords].pos.pos = LIMITPOS(prs->pos);
     427         838 :                 ptr++;
     428         838 :                 prs->curwords++;
     429             :             }
     430         981 :             pfree(norms);
     431             :         }
     432        1990 :     } while (type > 0);
     433             : 
     434         397 :     FunctionCall1(&(prsobj->prsend), PointerGetDatum(prsdata));
     435         397 : }
     436             : 
     437             : /*
     438             :  * Headline framework
     439             :  */
     440             : static void
     441        1026 : hladdword(HeadlineParsedText *prs, char *buf, int buflen, int type)
     442             : {
     443        2059 :     while (prs->curwords >= prs->lenwords)
     444             :     {
     445           7 :         prs->lenwords *= 2;
     446           7 :         prs->words = (HeadlineWordEntry *) repalloc((void *) prs->words, prs->lenwords * sizeof(HeadlineWordEntry));
     447             :     }
     448        1026 :     memset(&(prs->words[prs->curwords]), 0, sizeof(HeadlineWordEntry));
     449        1026 :     prs->words[prs->curwords].type = (uint8) type;
     450        1026 :     prs->words[prs->curwords].len = buflen;
     451        1026 :     prs->words[prs->curwords].word = palloc(buflen);
     452        1026 :     memcpy(prs->words[prs->curwords].word, buf, buflen);
     453        1026 :     prs->curwords++;
     454        1026 : }
     455             : 
     456             : static void
     457         343 : hlfinditem(HeadlineParsedText *prs, TSQuery query, int32 pos, char *buf, int buflen)
     458             : {
     459             :     int         i;
     460         343 :     QueryItem  *item = GETQUERY(query);
     461             :     HeadlineWordEntry *word;
     462             : 
     463         698 :     while (prs->curwords + query->size >= prs->lenwords)
     464             :     {
     465          12 :         prs->lenwords *= 2;
     466          12 :         prs->words = (HeadlineWordEntry *) repalloc((void *) prs->words, prs->lenwords * sizeof(HeadlineWordEntry));
     467             :     }
     468             : 
     469         343 :     word = &(prs->words[prs->curwords - 1]);
     470         343 :     word->pos = LIMITPOS(pos);
     471        1540 :     for (i = 0; i < query->size; i++)
     472             :     {
     473        1967 :         if (item->type == QI_VAL &&
     474         770 :             tsCompareString(GETOPERAND(query) + item->qoperand.distance, item->qoperand.length,
     475         770 :                             buf, buflen, item->qoperand.prefix) == 0)
     476             :         {
     477          64 :             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          64 :                 word->item = &item->qoperand;
     486             :         }
     487        1197 :         item++;
     488             :     }
     489         343 : }
     490             : 
     491             : static void
     492        1600 : addHLParsedLex(HeadlineParsedText *prs, TSQuery query, ParsedLex *lexs, TSLexeme *norms)
     493             : {
     494             :     ParsedLex  *tmplexs;
     495             :     TSLexeme   *ptr;
     496             :     int32       savedpos;
     497             : 
     498        4277 :     while (lexs)
     499             :     {
     500        1077 :         if (lexs->type > 0)
     501        1026 :             hladdword(prs, lexs->lemm, lexs->lenlemm, lexs->type);
     502             : 
     503        1077 :         ptr = norms;
     504        1077 :         savedpos = prs->vectorpos;
     505        2497 :         while (ptr && ptr->lexeme)
     506             :         {
     507         343 :             if (ptr->flags & TSL_ADDPOS)
     508           0 :                 savedpos++;
     509         343 :             hlfinditem(prs, query, savedpos, ptr->lexeme, strlen(ptr->lexeme));
     510         343 :             ptr++;
     511             :         }
     512             : 
     513        1077 :         tmplexs = lexs->next;
     514        1077 :         pfree(lexs);
     515        1077 :         lexs = tmplexs;
     516             :     }
     517             : 
     518        1600 :     if (norms)
     519             :     {
     520         523 :         ptr = norms;
     521        1389 :         while (ptr->lexeme)
     522             :         {
     523         343 :             if (ptr->flags & TSL_ADDPOS)
     524           0 :                 prs->vectorpos++;
     525         343 :             pfree(ptr->lexeme);
     526         343 :             ptr++;
     527             :         }
     528         523 :         pfree(norms);
     529             :     }
     530        1600 : }
     531             : 
     532             : void
     533          51 : hlparsetext(Oid cfgId, HeadlineParsedText *prs, TSQuery query, char *buf, int buflen)
     534             : {
     535             :     int         type,
     536             :                 lenlemm;
     537          51 :     char       *lemm = NULL;
     538             :     LexizeData  ldata;
     539             :     TSLexeme   *norms;
     540             :     ParsedLex  *lexs;
     541             :     TSConfigCacheEntry *cfg;
     542             :     TSParserCacheEntry *prsobj;
     543             :     void       *prsdata;
     544             : 
     545          51 :     cfg = lookup_ts_config_cache(cfgId);
     546          51 :     prsobj = lookup_ts_parser_cache(cfg->prsId);
     547             : 
     548          51 :     prsdata = (void *) DatumGetPointer(FunctionCall2(&(prsobj->prsstart),
     549             :                                                      PointerGetDatum(buf),
     550             :                                                      Int32GetDatum(buflen)));
     551             : 
     552          51 :     LexizeInit(&ldata, cfg);
     553             : 
     554             :     do
     555             :     {
     556        1077 :         type = DatumGetInt32(FunctionCall3(&(prsobj->prstoken),
     557             :                                            PointerGetDatum(prsdata),
     558             :                                            PointerGetDatum(&lemm),
     559             :                                            PointerGetDatum(&lenlemm)));
     560             : 
     561        1077 :         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        1077 :         LexizeAddLemm(&ldata, type, lemm, lenlemm);
     580             : 
     581             :         do
     582             :         {
     583        1600 :             if ((norms = LexizeExec(&ldata, &lexs)) != NULL)
     584             :             {
     585         523 :                 prs->vectorpos++;
     586         523 :                 addHLParsedLex(prs, query, lexs, norms);
     587             :             }
     588             :             else
     589        1077 :                 addHLParsedLex(prs, query, lexs, NULL);
     590        1600 :         } while (norms);
     591             : 
     592        1077 :     } while (type > 0);
     593             : 
     594          51 :     FunctionCall1(&(prsobj->prsend), PointerGetDatum(prsdata));
     595          51 : }
     596             : 
     597             : text *
     598          51 : generateHeadline(HeadlineParsedText *prs)
     599             : {
     600             :     text       *out;
     601             :     char       *ptr;
     602          51 :     int         len = 128;
     603          51 :     int         numfragments = 0;
     604          51 :     int16       infrag = 0;
     605             : 
     606          51 :     HeadlineWordEntry *wrd = prs->words;
     607             : 
     608          51 :     out = (text *) palloc(len);
     609          51 :     ptr = ((char *) out) + VARHDRSZ;
     610             : 
     611        1128 :     while (wrd - prs->words < prs->curwords)
     612             :     {
     613        2062 :         while (wrd->len + prs->stopsellen + prs->startsellen + prs->fragdelimlen + (ptr - ((char *) out)) >= len)
     614             :         {
     615          10 :             int         dist = ptr - ((char *) out);
     616             : 
     617          10 :             len *= 2;
     618          10 :             out = (text *) repalloc(out, len);
     619          10 :             ptr = ((char *) out) + dist;
     620             :         }
     621             : 
     622        1026 :         if (wrd->in && !wrd->repeated)
     623             :         {
     624         592 :             if (!infrag)
     625             :             {
     626             : 
     627             :                 /* start of a new fragment */
     628          53 :                 infrag = 1;
     629          53 :                 numfragments++;
     630             :                 /* add a fragment delimiter if this is after the first one */
     631          53 :                 if (numfragments > 1)
     632             :                 {
     633           2 :                     memcpy(ptr, prs->fragdelim, prs->fragdelimlen);
     634           2 :                     ptr += prs->fragdelimlen;
     635             :                 }
     636             : 
     637             :             }
     638        1184 :             if (wrd->replace)
     639             :             {
     640           0 :                 *ptr = ' ';
     641           0 :                 ptr++;
     642             :             }
     643         592 :             else if (!wrd->skip)
     644             :             {
     645         591 :                 if (wrd->selected)
     646             :                 {
     647          54 :                     memcpy(ptr, prs->startsel, prs->startsellen);
     648          54 :                     ptr += prs->startsellen;
     649             :                 }
     650         591 :                 memcpy(ptr, wrd->word, wrd->len);
     651         591 :                 ptr += wrd->len;
     652         591 :                 if (wrd->selected)
     653             :                 {
     654          54 :                     memcpy(ptr, prs->stopsel, prs->stopsellen);
     655          54 :                     ptr += prs->stopsellen;
     656             :                 }
     657             :             }
     658             :         }
     659         434 :         else if (!wrd->repeated)
     660             :         {
     661         434 :             if (infrag)
     662          12 :                 infrag = 0;
     663         434 :             pfree(wrd->word);
     664             :         }
     665             : 
     666        1026 :         wrd++;
     667             :     }
     668             : 
     669          51 :     SET_VARSIZE(out, ptr - ((char *) out));
     670          51 :     return out;
     671             : }

Generated by: LCOV version 1.11