Branch data Line data Source code
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 : 53 : get_tsearch_config_filename(const char *basename,
35 : : const char *extension)
36 : : {
37 : 53 : char sharepath[MAXPGPATH];
38 : 53 : 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 [ + - ]: 53 : if (strspn(basename, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(basename))
50 [ # # # # ]: 0 : ereport(ERROR,
51 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
52 : : errmsg("invalid text search configuration file name \"%s\"",
53 : : basename)));
54 : :
55 : 53 : get_share_path(my_exec_path, sharepath);
56 : 53 : result = palloc(MAXPGPATH);
57 : 106 : snprintf(result, MAXPGPATH, "%s/tsearch_data/%s.%s",
58 : 53 : sharepath, basename, extension);
59 : :
60 : 106 : return result;
61 : 53 : }
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 : 6 : readstoplist(const char *fname, StopList *s, char *(*wordop) (const char *, size_t, Oid))
70 : : {
71 : 6 : char **stop = NULL;
72 : :
73 : 6 : s->len = 0;
74 [ + - - + ]: 6 : if (fname && *fname)
75 : : {
76 : 6 : char *filename = get_tsearch_config_filename(fname, "stop");
77 : 6 : tsearch_readline_state trst;
78 : 6 : char *line;
79 : 6 : int reallen = 0;
80 : :
81 [ + - ]: 6 : if (!tsearch_readline_begin(&trst, filename))
82 [ # # # # ]: 0 : ereport(ERROR,
83 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
84 : : errmsg("could not open stop-word file \"%s\": %m",
85 : : filename)));
86 : :
87 [ + + ]: 768 : while ((line = tsearch_readline(&trst)) != NULL)
88 : : {
89 : 762 : char *pbuf = line;
90 : :
91 : : /* Trim trailing space */
92 [ + - + + ]: 3732 : while (*pbuf && !isspace((unsigned char) *pbuf))
93 : 2970 : pbuf += pg_mblen(pbuf);
94 : 762 : *pbuf = '\0';
95 : :
96 : : /* Skip empty lines */
97 [ - + ]: 762 : if (*line == '\0')
98 : : {
99 : 0 : pfree(line);
100 : 0 : continue;
101 : : }
102 : :
103 [ + + ]: 762 : if (s->len >= reallen)
104 : : {
105 [ + + ]: 12 : if (reallen == 0)
106 : : {
107 : 6 : reallen = 64;
108 : 6 : stop = palloc_array(char *, reallen);
109 : 6 : }
110 : : else
111 : : {
112 : 6 : reallen *= 2;
113 : 6 : stop = repalloc_array(stop, char *, reallen);
114 : : }
115 : 12 : }
116 : :
117 [ + - ]: 762 : if (wordop)
118 : : {
119 : 762 : stop[s->len] = wordop(line, strlen(line), DEFAULT_COLLATION_OID);
120 [ - + ]: 762 : if (stop[s->len] != line)
121 : 762 : pfree(line);
122 : 762 : }
123 : : else
124 : 0 : stop[s->len] = line;
125 : :
126 : 762 : (s->len)++;
127 [ - - + ]: 762 : }
128 : :
129 : 6 : tsearch_readline_end(&trst);
130 : 6 : pfree(filename);
131 : 6 : }
132 : :
133 : 6 : s->stop = stop;
134 : :
135 : : /* Sort to allow binary searching */
136 [ + - - + ]: 6 : if (s->stop && s->len > 0)
137 : 6 : qsort(s->stop, s->len, sizeof(char *), pg_qsort_strcmp);
138 : 6 : }
139 : :
140 : : bool
141 : 2534 : searchstoplist(StopList *s, char *key)
142 : : {
143 [ + + - + ]: 2534 : return (s->stop && s->len > 0 &&
144 : 1705 : bsearch(&key, s->stop, s->len,
145 : : sizeof(char *), pg_qsort_strcmp));
146 : : }
|