]> git.ipfire.org Git - thirdparty/cups.git/blame - cgi-bin/search.c
License change: Apache License, Version 2.0.
[thirdparty/cups.git] / cgi-bin / search.c
CommitLineData
ef416fc2 1/*
7e86f2f6 2 * Search routines for CUPS.
ef416fc2 3 *
7e86f2f6
MS
4 * Copyright 2007-2014 by Apple Inc.
5 * Copyright 1997-2006 by Easy Software Products.
ef416fc2 6 *
e3101897 7 * Licensed under Apache License v2.0. See the file "LICENSE" for more information.
ef416fc2 8 */
9
10/*
11 * Include necessary headers...
12 */
13
14#include "cgi-private.h"
15#include <regex.h>
16
17
18/*
19 * 'cgiCompileSearch()' - Compile a search string.
20 */
21
22void * /* O - Search context */
23cgiCompileSearch(const char *query) /* I - Query string */
24{
25 regex_t *re; /* Regular expression */
26 char *s, /* Regular expression string */
27 *sptr, /* Pointer into RE string */
28 *sword; /* Pointer to start of word */
7e86f2f6 29 size_t slen; /* Allocated size of RE string */
ef416fc2 30 const char *qptr, /* Pointer into query string */
31 *qend; /* End of current word */
32 const char *prefix; /* Prefix to add to next word */
33 int quoted; /* Word is quoted */
7e86f2f6 34 size_t wlen; /* Word length */
ef416fc2 35 char *lword; /* Last word in query */
36
37
1f0275e3
MS
38 DEBUG_printf(("cgiCompileSearch(query=\"%s\")\n", query));
39
40 /*
41 * Range check input...
42 */
43
44 if (!query)
45 return (NULL);
ef416fc2 46
47 /*
48 * Allocate a regular expression storage structure...
49 */
50
91c84a35
MS
51 if ((re = (regex_t *)calloc(1, sizeof(regex_t))) == NULL)
52 return (NULL);
ef416fc2 53
54 /*
55 * Allocate a buffer to hold the regular expression string, starting
56 * at 1024 bytes or 3 times the length of the query string, whichever
57 * is greater. We'll expand the string as needed...
58 */
59
60 slen = strlen(query) * 3;
61 if (slen < 1024)
62 slen = 1024;
63
91c84a35
MS
64 if ((s = (char *)malloc(slen)) == NULL)
65 {
66 free(re);
67 return (NULL);
68 }
ef416fc2 69
70 /*
71 * Copy the query string to the regular expression, handling basic
72 * AND and OR logic...
73 */
74
75 prefix = ".*";
76 qptr = query;
77 sptr = s;
78 lword = NULL;
79
80 while (*qptr)
81 {
82 /*
83 * Skip leading whitespace...
84 */
85
86 while (isspace(*qptr & 255))
87 qptr ++;
88
89 if (!*qptr)
90 break;
91
92 /*
93 * Find the end of the current word...
94 */
95
96 if (*qptr == '\"' || *qptr == '\'')
97 {
98 /*
99 * Scan quoted string...
100 */
101
102 quoted = *qptr ++;
103 for (qend = qptr; *qend && *qend != quoted; qend ++);
104
105 if (!*qend)
106 {
107 /*
108 * No closing quote, error out!
109 */
110
111 free(s);
112 free(re);
113
114 if (lword)
115 free(lword);
116
117 return (NULL);
118 }
119 }
120 else
121 {
122 /*
123 * Scan whitespace-delimited string...
124 */
125
126 quoted = 0;
127 for (qend = qptr + 1; *qend && !isspace(*qend); qend ++);
128 }
129
7e86f2f6 130 wlen = (size_t)(qend - qptr);
ef416fc2 131
132 /*
133 * Look for logic words: AND, OR
134 */
135
88f9aafc 136 if (wlen == 3 && !_cups_strncasecmp(qptr, "AND", 3))
ef416fc2 137 {
138 /*
139 * Logical AND with the following text...
140 */
141
142 if (sptr > s)
143 prefix = ".*";
144
145 qptr = qend;
146 }
88f9aafc 147 else if (wlen == 2 && !_cups_strncasecmp(qptr, "OR", 2))
ef416fc2 148 {
149 /*
150 * Logical OR with the following text...
151 */
152
153 if (sptr > s)
154 prefix = ".*|.*";
155
156 qptr = qend;
157 }
158 else
159 {
160 /*
161 * Add a search word, making sure we have enough room for the
162 * string + RE overhead...
163 */
164
7e86f2f6 165 wlen = (size_t)(sptr - s) + 2 * 4 * wlen + 2 * strlen(prefix) + 11;
839a51c8
MS
166 if (lword)
167 wlen += strlen(lword);
ef416fc2 168
169 if (wlen > slen)
170 {
171 /*
172 * Expand the RE string buffer...
173 */
174
175 char *temp; /* Temporary string pointer */
176
177
178 slen = wlen + 128;
179 temp = (char *)realloc(s, slen);
180 if (!temp)
181 {
182 free(s);
183 free(re);
184
185 if (lword)
186 free(lword);
187
188 return (NULL);
189 }
190
191 sptr = temp + (sptr - s);
192 s = temp;
193 }
194
195 /*
196 * Add the prefix string...
197 */
198
5a9febac 199 memcpy(sptr, prefix, strlen(prefix) + 1);
ef416fc2 200 sptr += strlen(sptr);
201
202 /*
203 * Then quote the remaining word characters as needed for the
204 * RE...
205 */
206
207 sword = sptr;
208
209 while (qptr < qend)
210 {
211 /*
212 * Quote: ^ . [ $ ( ) | * + ? { \
213 */
214
215 if (strchr("^.[$()|*+?{\\", *qptr))
216 *sptr++ = '\\';
217
218 *sptr++ = *qptr++;
219 }
220
07725fee 221 *sptr = '\0';
222
ef416fc2 223 /*
224 * For "word1 AND word2", add reciprocal "word2 AND word1"...
225 */
226
227 if (!strcmp(prefix, ".*") && lword)
228 {
229 char *lword2; /* New "last word" */
230
231
91c84a35
MS
232 if ((lword2 = strdup(sword)) == NULL)
233 {
234 free(lword);
235 free(s);
236 free(re);
237 return (NULL);
238 }
ef416fc2 239
5a9febac 240 memcpy(sptr, ".*|.*", 6);
ef416fc2 241 sptr += 5;
242
5a9febac 243 memcpy(sptr, lword2, strlen(lword2) + 1);
ef416fc2 244 sptr += strlen(sptr);
245
5a9febac 246 memcpy(sptr, ".*", 3);
ef416fc2 247 sptr += 2;
248
5a9febac 249 memcpy(sptr, lword, strlen(lword) + 1);
ef416fc2 250 sptr += strlen(sptr);
251
252 free(lword);
253 lword = lword2;
254 }
255 else
256 {
257 if (lword)
258 free(lword);
259
260 lword = strdup(sword);
261 }
262
263 prefix = ".*|.*";
264 }
265
266 /*
267 * Advance to the next string...
268 */
269
270 if (quoted)
271 qptr ++;
272 }
273
274 if (lword)
275 free(lword);
276
277 if (sptr > s)
5a9febac 278 memcpy(sptr, ".*", 3);
ef416fc2 279 else
280 {
281 /*
282 * No query data, return NULL...
283 */
284
285 free(s);
286 free(re);
287
288 return (NULL);
289 }
290
291 /*
292 * Compile the regular expression...
293 */
294
295 DEBUG_printf((" s=\"%s\"\n", s));
296
297 if (regcomp(re, s, REG_EXTENDED | REG_ICASE))
298 {
299 free(re);
300 free(s);
301
302 return (NULL);
303 }
304
305 /*
306 * Free the RE string and return the new regular expression we compiled...
307 */
308
309 free(s);
310
311 return ((void *)re);
312}
313
314
315/*
316 * 'cgiDoSearch()' - Do a search of some text.
317 */
318
319int /* O - Number of matches */
320cgiDoSearch(void *search, /* I - Search context */
321 const char *text) /* I - Text to search */
322{
323 int i; /* Looping var */
324 regmatch_t matches[100]; /* RE matches */
325
326
327 /*
328 * Range check...
329 */
330
331 if (!search || !text)
332 return (0);
333
334 /*
335 * Do a lookup...
336 */
337
338 if (!regexec((regex_t *)search, text, sizeof(matches) / sizeof(matches[0]),
339 matches, 0))
340 {
341 /*
342 * Figure out the number of matches in the string...
343 */
344
345 for (i = 0; i < (int)(sizeof(matches) / sizeof(matches[0])); i ++)
346 if (matches[i].rm_so < 0)
347 break;
348
349 return (i);
350 }
351 else
352 return (0);
353}
354
355
356/*
357 * 'cgiFreeSearch()' - Free a compiled search context.
358 */
359
360void
361cgiFreeSearch(void *search) /* I - Search context */
362{
363 regfree((regex_t *)search);
364}