Coverage Report

Created: 2026-08-13 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/backend/tsearch/ts_utils.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * ts_utils.c
4
 *    various support functions
5
 *
6
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
7
 *
8
 *
9
 * IDENTIFICATION
10
 *    src/backend/tsearch/ts_utils.c
11
 *
12
 *-------------------------------------------------------------------------
13
 */
14
15
#include "postgres.h"
16
17
#include <ctype.h>
18
19
#include "catalog/pg_collation_d.h"
20
#include "miscadmin.h"
21
#include "tsearch/ts_locale.h"
22
#include "tsearch/ts_public.h"
23
24
25
/*
26
 * Given the base name and extension of a tsearch config file, return
27
 * its full path name.  The base name is assumed to be user-supplied,
28
 * and is checked to prevent pathname attacks.  The extension is assumed
29
 * to be safe.
30
 *
31
 * The result is a palloc'd string.
32
 */
33
char *
34
get_tsearch_config_filename(const char *basename,
35
              const char *extension)
36
0
{
37
0
  char    sharepath[MAXPGPATH];
38
0
  char     *result;
39
40
  /*
41
   * We limit the basename to contain a-z, 0-9, and underscores.  This may
42
   * be overly restrictive, but we don't want to allow access to anything
43
   * outside the tsearch_data directory, so for instance '/' *must* be
44
   * rejected, and on some platforms '\' and ':' are risky as well. Allowing
45
   * uppercase might result in incompatible behavior between case-sensitive
46
   * and case-insensitive filesystems, and non-ASCII characters create other
47
   * interesting risks, so on the whole a tight policy seems best.
48
   */
49
0
  if (strspn(basename, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(basename))
50
0
    ereport(ERROR,
51
0
        (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
52
0
         errmsg("invalid text search configuration file name \"%s\"",
53
0
            basename)));
54
55
0
  get_share_path(my_exec_path, sharepath);
56
0
  result = palloc(MAXPGPATH);
57
0
  snprintf(result, MAXPGPATH, "%s/tsearch_data/%s.%s",
58
0
       sharepath, basename, extension);
59
60
0
  return result;
61
0
}
62
63
/*
64
 * Reads a stop-word file. Each word is run through 'wordop'
65
 * function, if given.  wordop may either modify the input in-place,
66
 * or palloc a new version.
67
 */
68
void
69
readstoplist(const char *fname, StopList *s, char *(*wordop) (const char *, size_t, Oid))
70
0
{
71
0
  char    **stop = NULL;
72
73
0
  s->len = 0;
74
0
  if (fname && *fname)
75
0
  {
76
0
    char     *filename = get_tsearch_config_filename(fname, "stop");
77
0
    tsearch_readline_state trst;
78
0
    char     *line;
79
0
    int     reallen = 0;
80
81
0
    if (!tsearch_readline_begin(&trst, filename))
82
0
      ereport(ERROR,
83
0
          (errcode(ERRCODE_CONFIG_FILE_ERROR),
84
0
           errmsg("could not open stop-word file \"%s\": %m",
85
0
              filename)));
86
87
0
    while ((line = tsearch_readline(&trst)) != NULL)
88
0
    {
89
0
      char     *pbuf = line;
90
91
      /* Trim trailing space */
92
0
      while (*pbuf && !isspace((unsigned char) *pbuf))
93
0
        pbuf += pg_mblen_cstr(pbuf);
94
0
      *pbuf = '\0';
95
96
      /* Skip empty lines */
97
0
      if (*line == '\0')
98
0
      {
99
0
        pfree(line);
100
0
        continue;
101
0
      }
102
103
0
      if (s->len >= reallen)
104
0
      {
105
0
        if (reallen == 0)
106
0
        {
107
0
          reallen = 64;
108
0
          stop = palloc_array(char *, reallen);
109
0
        }
110
0
        else
111
0
        {
112
0
          reallen *= 2;
113
0
          stop = repalloc_array(stop, char *, reallen);
114
0
        }
115
0
      }
116
117
0
      if (wordop)
118
0
      {
119
0
        stop[s->len] = wordop(line, strlen(line), DEFAULT_COLLATION_OID);
120
0
        if (stop[s->len] != line)
121
0
          pfree(line);
122
0
      }
123
0
      else
124
0
        stop[s->len] = line;
125
126
0
      (s->len)++;
127
0
    }
128
129
0
    tsearch_readline_end(&trst);
130
0
    pfree(filename);
131
0
  }
132
133
0
  s->stop = stop;
134
135
  /* Sort to allow binary searching */
136
0
  if (s->stop && s->len > 0)
137
0
    qsort(s->stop, s->len, sizeof(char *), pg_qsort_strcmp);
138
0
}
139
140
bool
141
searchstoplist(StopList *s, char *key)
142
0
{
143
0
  return (s->stop && s->len > 0 &&
144
0
      bsearch(&key, s->stop, s->len,
145
0
          sizeof(char *), pg_qsort_strcmp));
146
0
}