Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * regis.c
4 : * Fast regex subset
5 : *
6 : * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group
7 : *
8 : *
9 : * IDENTIFICATION
10 : * src/backend/tsearch/regis.c
11 : *
12 : *-------------------------------------------------------------------------
13 : */
14 :
15 : #include "postgres.h"
16 :
17 : #include "tsearch/dicts/regis.h"
18 : #include "tsearch/ts_locale.h"
19 :
20 : #define RS_IN_ONEOF 1
21 : #define RS_IN_ONEOF_IN 2
22 : #define RS_IN_NONEOF 3
23 : #define RS_IN_WAIT 4
24 :
25 :
26 : /*
27 : * Test whether a regex is of the subset supported here.
28 : * Keep this in sync with RS_compile!
29 : */
30 : bool
31 65 : RS_isRegis(const char *str)
32 : {
33 65 : int state = RS_IN_WAIT;
34 65 : const char *c = str;
35 :
36 507 : while (*c)
37 : {
38 377 : if (state == RS_IN_WAIT)
39 : {
40 78 : if (t_isalpha(c))
41 : /* okay */ ;
42 65 : else if (t_iseq(c, '['))
43 65 : state = RS_IN_ONEOF;
44 : else
45 0 : return false;
46 : }
47 299 : else if (state == RS_IN_ONEOF)
48 : {
49 65 : if (t_iseq(c, '^'))
50 65 : state = RS_IN_NONEOF;
51 0 : else if (t_isalpha(c))
52 0 : state = RS_IN_ONEOF_IN;
53 : else
54 0 : return false;
55 : }
56 234 : else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
57 : {
58 468 : if (t_isalpha(c))
59 : /* okay */ ;
60 65 : else if (t_iseq(c, ']'))
61 65 : state = RS_IN_WAIT;
62 : else
63 0 : return false;
64 : }
65 : else
66 0 : elog(ERROR, "internal error in RS_isRegis: state %d", state);
67 377 : c += pg_mblen(c);
68 : }
69 :
70 65 : return (state == RS_IN_WAIT);
71 : }
72 :
73 : static RegisNode *
74 78 : newRegisNode(RegisNode *prev, int len)
75 : {
76 : RegisNode *ptr;
77 :
78 78 : ptr = (RegisNode *) palloc0(RNHDRSZ + len + 1);
79 78 : if (prev)
80 13 : prev->next = ptr;
81 78 : return ptr;
82 : }
83 :
84 : void
85 65 : RS_compile(Regis *r, bool issuffix, const char *str)
86 : {
87 65 : int len = strlen(str);
88 65 : int state = RS_IN_WAIT;
89 65 : const char *c = str;
90 65 : RegisNode *ptr = NULL;
91 :
92 65 : memset(r, 0, sizeof(Regis));
93 65 : r->issuffix = (issuffix) ? 1 : 0;
94 :
95 507 : while (*c)
96 : {
97 377 : if (state == RS_IN_WAIT)
98 : {
99 78 : if (t_isalpha(c))
100 : {
101 13 : if (ptr)
102 13 : ptr = newRegisNode(ptr, len);
103 : else
104 0 : ptr = r->node = newRegisNode(NULL, len);
105 13 : COPYCHAR(ptr->data, c);
106 13 : ptr->type = RSF_ONEOF;
107 13 : ptr->len = pg_mblen(c);
108 : }
109 65 : else if (t_iseq(c, '['))
110 : {
111 65 : if (ptr)
112 0 : ptr = newRegisNode(ptr, len);
113 : else
114 65 : ptr = r->node = newRegisNode(NULL, len);
115 65 : ptr->type = RSF_ONEOF;
116 65 : state = RS_IN_ONEOF;
117 : }
118 : else /* shouldn't get here */
119 0 : elog(ERROR, "invalid regis pattern: \"%s\"", str);
120 : }
121 299 : else if (state == RS_IN_ONEOF)
122 : {
123 65 : if (t_iseq(c, '^'))
124 : {
125 65 : ptr->type = RSF_NONEOF;
126 65 : state = RS_IN_NONEOF;
127 : }
128 0 : else if (t_isalpha(c))
129 : {
130 0 : COPYCHAR(ptr->data, c);
131 0 : ptr->len = pg_mblen(c);
132 0 : state = RS_IN_ONEOF_IN;
133 : }
134 : else /* shouldn't get here */
135 0 : elog(ERROR, "invalid regis pattern: \"%s\"", str);
136 : }
137 234 : else if (state == RS_IN_ONEOF_IN || state == RS_IN_NONEOF)
138 : {
139 468 : if (t_isalpha(c))
140 : {
141 169 : COPYCHAR(ptr->data + ptr->len, c);
142 169 : ptr->len += pg_mblen(c);
143 : }
144 65 : else if (t_iseq(c, ']'))
145 65 : state = RS_IN_WAIT;
146 : else /* shouldn't get here */
147 0 : elog(ERROR, "invalid regis pattern: \"%s\"", str);
148 : }
149 : else
150 0 : elog(ERROR, "internal error in RS_compile: state %d", state);
151 377 : c += pg_mblen(c);
152 : }
153 :
154 65 : if (state != RS_IN_WAIT) /* shouldn't get here */
155 0 : elog(ERROR, "invalid regis pattern: \"%s\"", str);
156 :
157 65 : ptr = r->node;
158 208 : while (ptr)
159 : {
160 78 : r->nchar++;
161 78 : ptr = ptr->next;
162 : }
163 65 : }
164 :
165 : void
166 0 : RS_free(Regis *r)
167 : {
168 0 : RegisNode *ptr = r->node,
169 : *tmp;
170 :
171 0 : while (ptr)
172 : {
173 0 : tmp = ptr->next;
174 0 : pfree(ptr);
175 0 : ptr = tmp;
176 : }
177 :
178 0 : r->node = NULL;
179 0 : }
180 :
181 : #ifdef USE_WIDE_UPPER_LOWER
182 : static bool
183 108 : mb_strchr(char *str, char *c)
184 : {
185 : int clen,
186 : plen,
187 : i;
188 108 : char *ptr = str;
189 108 : bool res = false;
190 :
191 108 : clen = pg_mblen(c);
192 484 : while (*ptr && !res)
193 : {
194 268 : plen = pg_mblen(ptr);
195 268 : if (plen == clen)
196 : {
197 268 : i = plen;
198 268 : res = true;
199 544 : while (i--)
200 268 : if (*(ptr + i) != *(c + i))
201 : {
202 260 : res = false;
203 260 : break;
204 : }
205 : }
206 :
207 268 : ptr += plen;
208 : }
209 :
210 108 : return res;
211 : }
212 : #else
213 : #define mb_strchr(s,c) ( (strchr((s),*(c)) == NULL) ? false : true )
214 : #endif
215 :
216 :
217 : bool
218 104 : RS_execute(Regis *r, char *str)
219 : {
220 104 : RegisNode *ptr = r->node;
221 104 : char *c = str;
222 104 : int len = 0;
223 :
224 780 : while (*c)
225 : {
226 572 : len++;
227 572 : c += pg_mblen(c);
228 : }
229 :
230 104 : if (len < r->nchar)
231 4 : return 0;
232 :
233 100 : c = str;
234 100 : if (r->issuffix)
235 : {
236 100 : len -= r->nchar;
237 664 : while (len-- > 0)
238 464 : c += pg_mblen(c);
239 : }
240 :
241 :
242 308 : while (ptr)
243 : {
244 108 : switch (ptr->type)
245 : {
246 : case RSF_ONEOF:
247 8 : if (!mb_strchr((char *) ptr->data, c))
248 0 : return false;
249 8 : break;
250 : case RSF_NONEOF:
251 100 : if (mb_strchr((char *) ptr->data, c))
252 0 : return false;
253 100 : break;
254 : default:
255 0 : elog(ERROR, "unrecognized regis node type: %d", ptr->type);
256 : }
257 108 : ptr = ptr->next;
258 108 : c += pg_mblen(c);
259 : }
260 :
261 100 : return true;
262 : }
|