Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * ts_utils.c
4 : : * various support functions
5 : : *
6 : : * Portions Copyright (c) 1996-2025, 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 *
6591 tgl@sss.pgh.pa.us 34 :CBC 192 : get_tsearch_config_filename(const char *basename,
35 : : const char *extension)
36 : : {
37 : : char sharepath[MAXPGPATH];
38 : : 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 : : */
6577 49 [ - + ]: 192 : if (strspn(basename, "abcdefghijklmnopqrstuvwxyz0123456789_") != strlen(basename))
6577 tgl@sss.pgh.pa.us 50 [ # # ]:UBC 0 : ereport(ERROR,
51 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
52 : : errmsg("invalid text search configuration file name \"%s\"",
53 : : basename)));
54 : :
6591 tgl@sss.pgh.pa.us 55 :CBC 192 : get_share_path(my_exec_path, sharepath);
56 : 192 : result = palloc(MAXPGPATH);
57 : 192 : snprintf(result, MAXPGPATH, "%s/tsearch_data/%s.%s",
58 : : sharepath, basename, extension);
59 : :
60 : 192 : return result;
61 : : }
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
263 peter@eisentraut.org 69 : 19 : readstoplist(const char *fname, StopList *s, char *(*wordop) (const char *, size_t, Oid))
70 : : {
6591 tgl@sss.pgh.pa.us 71 : 19 : char **stop = NULL;
72 : :
73 : 19 : s->len = 0;
6587 74 [ + - + - ]: 19 : if (fname && *fname)
75 : : {
76 : 19 : char *filename = get_tsearch_config_filename(fname, "stop");
77 : : tsearch_readline_state trst;
78 : : char *line;
6591 79 : 19 : int reallen = 0;
80 : :
6289 81 [ - + ]: 19 : if (!tsearch_readline_begin(&trst, filename))
6591 tgl@sss.pgh.pa.us 82 [ # # ]:UBC 0 : ereport(ERROR,
83 : : (errcode(ERRCODE_CONFIG_FILE_ERROR),
84 : : errmsg("could not open stop-word file \"%s\": %m",
85 : : filename)));
86 : :
6289 tgl@sss.pgh.pa.us 87 [ + + ]:CBC 2432 : while ((line = tsearch_readline(&trst)) != NULL)
88 : : {
6505 bruce@momjian.us 89 : 2413 : char *pbuf = line;
90 : :
91 : : /* Trim trailing space */
263 peter@eisentraut.org 92 [ + - + + ]: 11818 : while (*pbuf && !isspace((unsigned char) *pbuf))
6288 tgl@sss.pgh.pa.us 93 : 9405 : pbuf += pg_mblen(pbuf);
6591 94 : 2413 : *pbuf = '\0';
95 : :
96 : : /* Skip empty lines */
6587 97 [ - + ]: 2413 : if (*line == '\0')
98 : : {
6587 tgl@sss.pgh.pa.us 99 :UBC 0 : pfree(line);
100 : 0 : continue;
101 : : }
102 : :
6591 tgl@sss.pgh.pa.us 103 [ + + ]:CBC 2413 : if (s->len >= reallen)
104 : : {
105 [ + + ]: 38 : if (reallen == 0)
106 : : {
6587 107 : 19 : reallen = 64;
6591 108 : 19 : stop = (char **) palloc(sizeof(char *) * reallen);
109 : : }
110 : : else
111 : : {
112 : 19 : reallen *= 2;
942 peter@eisentraut.org 113 : 19 : stop = (char **) repalloc(stop, sizeof(char *) * reallen);
114 : : }
115 : : }
116 : :
6587 tgl@sss.pgh.pa.us 117 [ + - ]: 2413 : if (wordop)
118 : : {
263 peter@eisentraut.org 119 : 2413 : stop[s->len] = wordop(line, strlen(line), DEFAULT_COLLATION_OID);
6587 tgl@sss.pgh.pa.us 120 [ + - ]: 2413 : if (stop[s->len] != line)
121 : 2413 : pfree(line);
122 : : }
123 : : else
6587 tgl@sss.pgh.pa.us 124 :UBC 0 : stop[s->len] = line;
125 : :
6591 tgl@sss.pgh.pa.us 126 :CBC 2413 : (s->len)++;
127 : : }
128 : :
6289 129 : 19 : tsearch_readline_end(&trst);
6591 130 : 19 : pfree(filename);
131 : : }
132 : :
133 : 19 : s->stop = stop;
134 : :
135 : : /* Sort to allow binary searching */
136 [ + - + - ]: 19 : if (s->stop && s->len > 0)
4796 rhaas@postgresql.org 137 : 19 : qsort(s->stop, s->len, sizeof(char *), pg_qsort_strcmp);
6591 tgl@sss.pgh.pa.us 138 : 19 : }
139 : :
140 : : bool
6505 bruce@momjian.us 141 : 7641 : searchstoplist(StopList *s, char *key)
142 : : {
6591 tgl@sss.pgh.pa.us 143 [ + + + - : 12776 : return (s->stop && s->len > 0 &&
+ + ]
144 : 5135 : bsearch(&key, s->stop, s->len,
145 : : sizeof(char *), pg_qsort_strcmp));
146 : : }
|