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