2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: match.c,v 1.23 2006/05/25 01:47:12 johnfranks Exp $
29 * functions for checking and matching regular expressions
36 static int match_word(const char *glob, const char *word, const char separator);
37 static char *tar_to_regex(const char *glob);
45 static char errmsg[STR_SIZE];
47 if ((result = regcomp(®c, regex,
48 REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
49 regerror(result, ®c, errmsg, SIZEOF(errmsg));
66 result = alloc(2*strlen(str)+3);
71 for(i=0;i<strlen(str);i++) {
72 if(!isalnum((int)str[i]))
83 make_exact_host_expression(
89 result = alloc(2*strlen(host)+3);
93 for(i=0;i<strlen(host);i++) {
94 /* quote host expression metcharacters *except* '.'. Note that
95 * most of these are invalid in a DNS hostname anyway. */
118 make_exact_disk_expression(
124 result = alloc(2*strlen(disk)+3);
128 for(i=0;i<strlen(disk);i++) {
129 /* quote disk expression metcharacters *except* '/' */
158 char errmsg[STR_SIZE];
160 if((result = regcomp(®c, regex,
161 REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
162 regerror(result, ®c, errmsg, SIZEOF(errmsg));
163 error(_("regex \"%s\": %s"), regex, errmsg);
167 if((result = regexec(®c, str, 0, 0, 0)) != 0
168 && result != REG_NOMATCH) {
169 regerror(result, ®c, errmsg, SIZEOF(errmsg));
170 error(_("regex \"%s\": %s"), regex, errmsg);
186 char errmsg[STR_SIZE];
188 if((result = regcomp(®c, regex,
189 REG_EXTENDED|REG_NOSUB)) != 0) {
190 regerror(result, ®c, errmsg, SIZEOF(errmsg));
191 error(_("regex \"%s\": %s"), regex, errmsg);
195 if((result = regexec(®c, str, 0, 0, 0)) != 0
196 && result != REG_NOMATCH) {
197 regerror(result, ®c, errmsg, SIZEOF(errmsg));
198 error(_("regex \"%s\": %s"), regex, errmsg);
214 static char errmsg[STR_SIZE];
216 regex = glob_to_regex(glob);
217 if ((result = regcomp(®c, regex,
218 REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
219 regerror(result, ®c, errmsg, SIZEOF(errmsg));
238 char errmsg[STR_SIZE];
240 regex = glob_to_regex(glob);
241 if((result = regcomp(®c, regex,
242 REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
243 regerror(result, ®c, errmsg, SIZEOF(errmsg));
244 error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
248 if((result = regexec(®c, str, 0, 0, 0)) != 0
249 && result != REG_NOMATCH) {
250 regerror(result, ®c, errmsg, SIZEOF(errmsg));
251 error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
272 * Allocate an area to convert into. The worst case is a five to
276 regex = alloc(1 + len * 5 + 1 + 1);
285 * The following are given a leading backslash to protect them
286 * unless they already have a backslash:
290 * Put a leading ^ and trailing $ around the result. If the last
291 * non-escaped character is \ leave the $ off to cause a syntax
292 * error when the regex is compiled.
298 for (ch = *glob++; ch != '\0'; last_ch = ch, ch = *glob++) {
299 if (last_ch == '\\') {
301 ch = '\0'; /* so last_ch != '\\' next time */
302 } else if (last_ch == '[' && ch == '!') {
304 } else if (ch == '\\') {
306 } else if (ch == '*' || ch == '?') {
329 if (last_ch != '\\') {
346 char errmsg[STR_SIZE];
348 regex = tar_to_regex(glob);
349 if((result = regcomp(®c, regex,
350 REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
351 regerror(result, ®c, errmsg, SIZEOF(errmsg));
352 error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
356 if((result = regexec(®c, str, 0, 0, 0)) != 0
357 && result != REG_NOMATCH) {
358 regerror(result, ®c, errmsg, SIZEOF(errmsg));
359 error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
380 * Allocate an area to convert into. The worst case is a five to
384 regex = alloc(1 + len * 5 + 1 + 1);
393 * The following are given a leading backslash to protect them
394 * unless they already have a backslash:
398 * Put a leading ^ and trailing $ around the result. If the last
399 * non-escaped character is \ leave the $ off to cause a syntax
400 * error when the regex is compiled.
406 for (ch = *glob++; ch != '\0'; last_ch = ch, ch = *glob++) {
407 if (last_ch == '\\') {
409 ch = '\0'; /* so last_ch != '\\' next time */
410 } else if (last_ch == '[' && ch == '!') {
412 } else if (ch == '\\') {
414 } else if (ch == '*') {
417 } else if (ch == '?') {
437 if (last_ch != '\\') {
450 const char separator)
465 lenword = strlen(word);
466 nword = (char *)alloc(lenword + 3);
468 if (separator == '/' && lenword > 2 && word[0] == '\\' && word[1] == '\\' && !strchr(word, '/')) {
469 /* Convert all "\" to '/' */
470 mword = (char *)alloc(lenword + 1);
482 lenword = strlen(word);
484 /* Convert all "\\" to '/' */
485 mglob = (char *)alloc(strlen(glob) + 1);
489 if (*w == '\\' && *(w+1) == '\\') {
498 mword = stralloc(word);
499 mglob = stralloc(glob);
504 if(lenword == 1 && *w == separator) {
513 if(*(r-1) != separator)
519 * Allocate an area to convert into. The worst case is a six to
523 regex = (char *)alloc(1 + len * 6 + 1 + 1 + 2 + 2);
525 nglob = stralloc(mglob);
528 if((len == 1 && nglob[0] == separator) ||
529 (len == 2 && nglob[0] == '^' && nglob[1] == separator) ||
530 (len == 2 && nglob[0] == separator && nglob[1] == '$') ||
531 (len == 3 && nglob[0] == '^' && nglob[1] == separator &&
545 * * -> [^\separator]*
549 * The following are given a leading backslash to protect them
550 * unless they already have a backslash:
555 * non-escaped character is \ leave it to cause a syntax
556 * error when the regex is compiled.
561 *r++ = '\\'; /* escape the separator */
564 if(*g == separator) g++;
566 else if(*g != separator) {
567 *r++ = '\\'; /* add a leading \separator */
571 for (ch = *g++; ch != '\0'; last_ch = ch, ch = *g++) {
573 if (last_ch == '\\') {
575 ch = '\0'; /* so last_ch != '\\' next time */
576 } else if (last_ch == '[' && ch == '!') {
578 } else if (ch == '\\') {
580 } else if (ch == '*' || ch == '?') {
581 if(ch == '*' && next_ch == '*') {
595 } else if (ch == '$' && next_ch == '\0') {
596 if(last_ch != separator) {
601 } else if ( ch == '('
616 if(last_ch != '\\') {
617 if(last_ch != separator && last_ch != '$') {
619 *r++ = separator; /* add a trailing \separator */
625 i = match(regex,nword);
647 lglob = (char *)alloc(strlen(glob)+1);
650 *c++ = (char)tolower(*d++);
653 lhost = (char *)alloc(strlen(host)+1);
656 *c++ = (char)tolower(*d++);
659 i = match_word(lglob, lhost, (int)'.');
671 return match_word(glob, disk, '/');
679 if (!isdigit((int)*(str++)))
687 const char * dateexp,
688 const char * datestamp)
691 size_t len, len_suffix;
693 char firstdate[100], lastdate[100];
697 if(strlen(dateexp) >= 100 || strlen(dateexp) < 1) {
701 /* strip and ignore an initial "^" */
702 if(dateexp[0] == '^') {
703 strncpy(mydateexp, dateexp+1, sizeof(mydateexp)-1);
704 mydateexp[sizeof(mydateexp)-1] = '\0';
707 strncpy(mydateexp, dateexp, sizeof(mydateexp)-1);
708 mydateexp[sizeof(mydateexp)-1] = '\0';
711 if(mydateexp[strlen(mydateexp)-1] == '$') {
713 mydateexp[strlen(mydateexp)-1] = '\0'; /* strip the trailing $ */
718 /* a single dash represents a date range */
719 if((dash = strchr(mydateexp,'-'))) {
720 if(match_exact == 1 || strchr(dash+1, '-')) {
724 /* format: XXXYYYY-ZZZZ, indicating dates XXXYYYY to XXXZZZZ */
726 len = (size_t)(dash - mydateexp); /* length of XXXYYYY */
727 len_suffix = strlen(dash) - 1; /* length of ZZZZ */
728 if (len_suffix > len) goto illegal;
729 len_prefix = len - len_suffix; /* length of XXX */
733 strncpy(firstdate, mydateexp, len);
734 firstdate[len] = '\0';
735 strncpy(lastdate, mydateexp, len_prefix);
736 strncpy(&(lastdate[len_prefix]), dash, len_suffix);
737 lastdate[len] = '\0';
738 if (!alldigits(firstdate) || !alldigits(lastdate))
740 if (strncmp(firstdate, lastdate, strlen(firstdate)) > 0)
742 return ((strncmp(datestamp, firstdate, strlen(firstdate)) >= 0) &&
743 (strncmp(datestamp, lastdate , strlen(lastdate)) <= 0));
746 if (!alldigits(mydateexp))
748 if(match_exact == 1) {
749 return (strcmp(datestamp, mydateexp) == 0);
752 return (strncmp(datestamp, mydateexp, strlen(mydateexp)) == 0);
756 error(_("Illegal datestamp expression %s"),dateexp);
763 const char * levelexp,
767 long int low, hi, level_i;
768 char mylevelexp[100];
771 if(strlen(levelexp) >= 100 || strlen(levelexp) < 1) {
772 error(_("Illegal level expression %s"),levelexp);
776 if(levelexp[0] == '^') {
777 strncpy(mylevelexp, levelexp+1, strlen(levelexp)-1);
778 mylevelexp[strlen(levelexp)-1] = '\0';
781 strncpy(mylevelexp, levelexp, strlen(levelexp));
782 mylevelexp[strlen(levelexp)] = '\0';
785 if(mylevelexp[strlen(mylevelexp)] == '$') {
787 mylevelexp[strlen(mylevelexp)] = '\0';
792 if((dash = strchr(mylevelexp,'-'))) {
793 if(match_exact == 1) {
798 if (!alldigits(mylevelexp) || !alldigits(dash+1)) goto illegal;
801 low = strtol(mylevelexp, (char **) NULL, 10);
802 if (errno) goto illegal;
803 hi = strtol(dash+1, (char **) NULL, 10);
804 if (errno) goto illegal;
805 level_i = strtol(level, (char **) NULL, 10);
806 if (errno) goto illegal;
808 return ((level_i >= low) && (level_i <= hi));
811 if (!alldigits(mylevelexp)) goto illegal;
812 if(match_exact == 1) {
813 return (strcmp(level, mylevelexp) == 0);
816 return (strncmp(level, mylevelexp, strlen(mylevelexp)) == 0);
820 error(_("Illegal level expression %s"),levelexp);