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