Imported Upstream version 2.4.4p3
[debian/amanda] / common-src / match.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: match.c,v 1.10.4.1.4.1.2.4 2002/11/12 18:01:19 martinea Exp $
28  *
29  * functions for checking and matching regular expressions
30  */
31
32 #include "amanda.h"
33 #include "regex.h"
34
35 char *validate_regexp(regex)
36 char *regex;
37 {
38     regex_t regc;
39     int result;
40     static char errmsg[STR_SIZE];
41
42     if ((result = regcomp(&regc, regex,
43                           REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
44       regerror(result, &regc, errmsg, sizeof(errmsg));
45       return errmsg;
46     }
47
48     regfree(&regc);
49
50     return NULL;
51 }
52
53 char *clean_regex(regex)
54 char *regex;
55 {
56     char *result;
57     int j;
58     size_t i;
59     result = alloc(2*strlen(regex)+1);
60
61     for(i=0,j=0;i<strlen(regex);i++) {
62         if(!isalnum((int)regex[i]))
63             result[j++]='\\';
64         result[j++]=regex[i];
65     }
66     result[j++] = '\0';
67     return result;
68 }
69
70 int match(regex, str)
71 char *regex, *str;
72 {
73     regex_t regc;
74     int result;
75     char errmsg[STR_SIZE];
76
77     if((result = regcomp(&regc, regex,
78                          REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
79         regerror(result, &regc, errmsg, sizeof(errmsg));
80         error("regex \"%s\": %s", regex, errmsg);
81     }
82
83     if((result = regexec(&regc, str, 0, 0, 0)) != 0
84        && result != REG_NOMATCH) {
85         regerror(result, &regc, errmsg, sizeof(errmsg));
86         error("regex \"%s\": %s", regex, errmsg);
87     }
88
89     regfree(&regc);
90
91     return result == 0;
92 }
93
94 char *validate_glob(glob)
95 char *glob;
96 {
97     char *regex = NULL;
98     regex_t regc;
99     int result;
100     static char errmsg[STR_SIZE];
101
102     regex = glob_to_regex(glob);
103     if ((result = regcomp(&regc, regex,
104                           REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
105       regerror(result, &regc, errmsg, sizeof(errmsg));
106       amfree(regex);
107       return errmsg;
108     }
109
110     regfree(&regc);
111     amfree(regex);
112
113     return NULL;
114 }
115
116 int match_glob(glob, str)
117 char *glob, *str;
118 {
119     char *regex = NULL;
120     regex_t regc;
121     int result;
122     char errmsg[STR_SIZE];
123
124     regex = glob_to_regex(glob);
125     if((result = regcomp(&regc, regex,
126                          REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
127         regerror(result, &regc, errmsg, sizeof(errmsg));
128         amfree(regex);
129         error("glob \"%s\" -> regex \"%s\": %s", glob, regex, errmsg);
130     }
131
132     if((result = regexec(&regc, str, 0, 0, 0)) != 0
133        && result != REG_NOMATCH) {
134         regerror(result, &regc, errmsg, sizeof(errmsg));
135         amfree(regex);
136         error("glob \"%s\" -> regex \"%s\": %s", glob, regex, errmsg);
137     }
138
139     regfree(&regc);
140     amfree(regex);
141
142     return result == 0;
143 }
144
145 char *glob_to_regex(glob)
146 char *glob;
147 {
148     char *regex;
149     char *r;
150     size_t len;
151     int ch;
152     int last_ch;
153
154     /*
155      * Allocate an area to convert into.  The worst case is a five to
156      * one expansion.
157      */
158     len = strlen(glob);
159     regex = alloc(1 + len * 5 + 1 + 1);
160
161     /*
162      * Do the conversion:
163      *
164      *  ?      -> [^/]
165      *  *      -> [^/]*
166      *  [!...] -> [^...]
167      *
168      * The following are given a leading backslash to protect them
169      * unless they already have a backslash:
170      *
171      *   ( ) { } + . ^ $ |
172      *
173      * Put a leading ^ and trailing $ around the result.  If the last
174      * non-escaped character is \ leave the $ off to cause a syntax
175      * error when the regex is compiled.
176      */
177
178     r = regex;
179     *r++ = '^';
180     last_ch = '\0';
181     for (ch = *glob++; ch != '\0'; last_ch = ch, ch = *glob++) {
182         if (last_ch == '\\') {
183             *r++ = ch;
184             ch = '\0';                  /* so last_ch != '\\' next time */
185         } else if (last_ch == '[' && ch == '!') {
186             *r++ = '^';
187         } else if (ch == '\\') {
188             *r++ = ch;
189         } else if (ch == '*' || ch == '?') {
190             *r++ = '[';
191             *r++ = '^';
192             *r++ = '/';
193             *r++ = ']';
194             if (ch == '*') {
195                 *r++ = '*';
196             }
197         } else if (ch == '('
198                    || ch == ')'
199                    || ch == '{'
200                    || ch == '}'
201                    || ch == '+'
202                    || ch == '.'
203                    || ch == '^'
204                    || ch == '$'
205                    || ch == '|') {
206             *r++ = '\\';
207             *r++ = ch;
208         } else {
209             *r++ = ch;
210         }
211     }
212     if (last_ch != '\\') {
213         *r++ = '$';
214     }
215     *r = '\0';
216
217     return regex;
218 }
219
220
221 int match_word(glob, word, separator)
222 char *glob, *word;
223 char separator;
224 {
225     char *regex;
226     char *r;
227     size_t  len;
228     int  ch;
229     int  last_ch;
230     int  next_ch;
231     size_t  lenword;
232     char *nword;
233     char *nglob;
234     char *g, *w;
235     int  i;
236
237     lenword = strlen(word);
238     nword = (char *)alloc(lenword + 3);
239
240     r = nword;
241     w = word;
242     if(lenword == 1 && *w == separator) {
243         *r++ = separator;
244         *r++ = separator;
245     }
246     else {
247         if(*w != separator)
248             *r++ = separator;
249         while(*w != '\0')
250             *r++ = *w++;
251         if(*(r-1) != separator)
252             *r++ = separator;    
253     }
254     *r = '\0';
255
256     /*
257      * Allocate an area to convert into.  The worst case is a six to
258      * one expansion.
259      */
260     len = strlen(glob);
261     regex = (char *)alloc(1 + len * 6 + 1 + 1 + 2 + 2);
262     r = regex;
263     nglob = stralloc(glob);
264     g = nglob;
265
266     if((len == 1 && nglob[0] == separator) ||
267        (len == 2 && nglob[0] == '^' && nglob[1] == separator) ||
268        (len == 2 && nglob[0] == separator && nglob[1] == '$') ||
269        (len == 3 && nglob[0] == '^' && nglob[1] == separator &&
270         nglob[2] == '$')) {
271         *r++ = '^';
272         *r++ = '\\';
273         *r++ = separator;
274         *r++ = '\\';
275         *r++ = separator;
276         *r++ = '$';
277     }
278     else {
279         /*
280          * Do the conversion:
281          *
282          *  ?      -> [^\separator]
283          *  *      -> [^\separator]*
284          *  [!...] -> [^...]
285          *  **     -> .*
286          *
287          * The following are given a leading backslash to protect them
288          * unless they already have a backslash:
289          *
290          *   ( ) { } + . ^ $ |
291          *
292          * If the last
293          * non-escaped character is \ leave it to cause a syntax
294          * error when the regex is compiled.
295          */
296
297         if(*g == '^') {
298             *r++ = '^';
299             *r++ = '\\';        /* escape the separator */
300             *r++ = separator;
301             g++;
302             if(*g == separator) g++;
303         }
304         else if(*g != separator) {
305             *r++ = '\\';        /* add a leading \separator */
306             *r++ = separator;
307         }
308         last_ch = '\0';
309         for (ch = *g++; ch != '\0'; last_ch = ch, ch = *g++) {
310             next_ch = *g;
311             if (last_ch == '\\') {
312                 *r++ = ch;
313                 ch = '\0';              /* so last_ch != '\\' next time */
314             } else if (last_ch == '[' && ch == '!') {
315                 *r++ = '^';
316             } else if (ch == '\\') {
317                 *r++ = ch;
318             } else if (ch == '*' || ch == '?') {
319                 if(ch == '*' && next_ch == '*') {
320                     *r++ = '.';
321                     g++;
322                 }
323                 else {
324                     *r++ = '[';
325                     *r++ = '^';
326                     *r++ = '\\';
327                     *r++ = separator;
328                     *r++ = ']';
329                 }
330                 if (ch == '*') {
331                     *r++ = '*';
332                 }
333             } else if (ch == '$' && next_ch == '\0') {
334                 if(last_ch != separator) {
335                     *r++ = '\\';
336                     *r++ = separator;
337                 }
338                 *r++ = ch;
339             } else if (   ch == '('
340                        || ch == ')'
341                        || ch == '{'
342                        || ch == '}'
343                        || ch == '+'
344                        || ch == '.'
345                        || ch == '^'
346                        || ch == '$'
347                        || ch == '|') {
348                 *r++ = '\\';
349                 *r++ = ch;
350             } else {
351                 *r++ = ch;
352             }
353         }
354         if(last_ch != '\\') {
355             if(last_ch != separator && last_ch != '$') {
356                 *r++ = '\\';
357                 *r++ = separator;               /* add a trailing \separator */
358             }
359         }
360     }
361     *r = '\0';
362
363     i = match(regex,nword);
364
365     amfree(nword);
366     amfree(nglob);
367     amfree(regex);
368     return i;
369 }
370
371
372 int match_host(glob, host)
373 char *glob, *host;
374 {
375     char *lglob, *lhost;
376     char *c, *d;
377     int i;
378
379     
380     lglob = (char *)alloc(strlen(glob)+1);
381     c = lglob, d=glob;
382     while( *d != '\0')
383         *c++ = tolower(*d++);
384     *c = *d;
385
386     lhost = (char *)alloc(strlen(host)+1);
387     c = lhost, d=host;
388     while( *d != '\0')
389         *c++ = tolower(*d++);
390     *c = *d;
391
392     i = match_word(lglob, lhost, '.');
393     amfree(lglob);
394     amfree(lhost);
395     return i;
396 }
397
398
399 int match_disk(glob, disk)
400 char *glob, *disk;
401 {
402     int i;
403     i = match_word(glob, disk, '/');
404     return i;
405 }
406
407 int match_datestamp(dateexp, datestamp)
408 char *dateexp, *datestamp;
409 {
410     char *dash;
411     size_t len, len_suffix;
412     int len_prefix;
413     char firstdate[100], lastdate[100];
414     char mydateexp[100];
415     int match_exact;
416
417     if(strlen(dateexp) >= 100 || strlen(dateexp) < 1) {
418         error("Illegal datestamp expression %s",dateexp);
419     }
420    
421     if(dateexp[0] == '^') {
422         strncpy(mydateexp, dateexp+1, strlen(dateexp)-1); 
423         mydateexp[strlen(dateexp)-1] = '\0';
424     }
425     else {
426         strncpy(mydateexp, dateexp, strlen(dateexp));
427         mydateexp[strlen(dateexp)] = '\0';
428     }
429
430     if(mydateexp[strlen(mydateexp)] == '$') {
431         match_exact = 1;
432         mydateexp[strlen(mydateexp)] = '\0';
433     }
434     else
435         match_exact = 0;
436
437     if((dash = strchr(mydateexp,'-'))) {
438         if(match_exact == 1) {
439             error("Illegal datestamp expression %s",dateexp);
440         }
441         len = dash - mydateexp;
442         len_suffix = strlen(dash) - 1;
443         len_prefix = len - len_suffix;
444
445         if(len_prefix < 0) {
446             error("Illegal datestamp expression %s",dateexp);
447         }
448
449         dash++;
450         strncpy(firstdate, mydateexp, len);
451         firstdate[len] = '\0';
452         strncpy(lastdate, mydateexp, len_prefix);
453         strncpy(&(lastdate[len_prefix]), dash, len_suffix);
454         lastdate[len] = '\0';
455         return ((strncmp(datestamp, firstdate, strlen(firstdate)) >= 0) &&
456                 (strncmp(datestamp, lastdate , strlen(lastdate))  <= 0));
457     }
458     else {
459         if(match_exact == 1) {
460             return (strcmp(datestamp, mydateexp) == 0);
461         }
462         else {
463             return (strncmp(datestamp, mydateexp, strlen(mydateexp)) == 0);
464         }
465     }
466 }