Imported Upstream version 3.2.0
[debian/amanda] / common-src / match.c
index 6f94ab4d967a452fabb3d1b8685723126a3b323b..826a2867fafceb46bd2e9dbc2b65184e2350b21e 100644 (file)
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: match.c,v 1.10.4.1.4.1.2.4 2002/11/12 18:01:19 martinea Exp $
+ * $Id: match.c,v 1.23 2006/05/25 01:47:12 johnfranks Exp $
  *
  * functions for checking and matching regular expressions
  */
 
 #include "amanda.h"
-#include "regex.h"
+#include "match.h"
+#include <regex.h>
 
-char *validate_regexp(regex)
-char *regex;
+static int match_word(const char *glob, const char *word, const char separator);
+static char *tar_to_regex(const char *glob);
+
+char *
+validate_regexp(
+    const char *       regex)
 {
     regex_t regc;
     int result;
@@ -41,7 +46,7 @@ char *regex;
 
     if ((result = regcomp(&regc, regex,
                          REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
-      regerror(result, &regc, errmsg, sizeof(errmsg));
+      regerror(result, &regc, errmsg, SIZEOF(errmsg));
       return errmsg;
     }
 
@@ -50,25 +55,103 @@ char *regex;
     return NULL;
 }
 
-char *clean_regex(regex)
-char *regex;
+char *
+clean_regex(
+    const char *       str,
+    gboolean           anchor)
+{
+    char *result;
+    int j;
+    size_t i;
+    result = alloc(2*strlen(str)+3);
+
+    j = 0;
+    if (anchor)
+       result[j++] = '^';
+    for(i=0;i<strlen(str);i++) {
+       if(!isalnum((int)str[i]))
+           result[j++]='\\';
+       result[j++]=str[i];
+    }
+    if (anchor)
+       result[j++] = '$';
+    result[j] = '\0';
+    return result;
+}
+
+char *
+make_exact_host_expression(
+    const char *       host)
 {
     char *result;
     int j;
     size_t i;
-    result = alloc(2*strlen(regex)+1);
+    result = alloc(2*strlen(host)+3);
+
+    j = 0;
+    result[j++] = '^';
+    for(i=0;i<strlen(host);i++) {
+       /* quote host expression metcharacters *except* '.'.  Note that
+        * most of these are invalid in a DNS hostname anyway. */
+       switch (host[i]) {
+           case '\\':
+           case '/':
+           case '^':
+           case '$':
+           case '?':
+           case '*':
+           case '[':
+           case ']':
+           result[j++]='\\';
+           /* fall through */
+
+           default:
+           result[j++]=host[i];
+       }
+    }
+    result[j++] = '$';
+    result[j] = '\0';
+    return result;
+}
 
-    for(i=0,j=0;i<strlen(regex);i++) {
-       if(!isalnum((int)regex[i]))
+char *
+make_exact_disk_expression(
+    const char *       disk)
+{
+    char *result;
+    int j;
+    size_t i;
+    result = alloc(2*strlen(disk)+3);
+
+    j = 0;
+    result[j++] = '^';
+    for(i=0;i<strlen(disk);i++) {
+       /* quote disk expression metcharacters *except* '/' */
+       switch (disk[i]) {
+           case '\\':
+           case '.':
+           case '^':
+           case '$':
+           case '?':
+           case '*':
+           case '[':
+           case ']':
            result[j++]='\\';
-       result[j++]=regex[i];
+           /* fall through */
+
+           default:
+           result[j++]=disk[i];
+       }
     }
-    result[j++] = '\0';
+    result[j++] = '$';
+    result[j] = '\0';
     return result;
 }
 
-int match(regex, str)
-char *regex, *str;
+int
+match(
+    const char *       regex,
+    const char *       str)
 {
     regex_t regc;
     int result;
@@ -76,14 +159,44 @@ char *regex, *str;
 
     if((result = regcomp(&regc, regex,
                         REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
-        regerror(result, &regc, errmsg, sizeof(errmsg));
-       error("regex \"%s\": %s", regex, errmsg);
+        regerror(result, &regc, errmsg, SIZEOF(errmsg));
+       error(_("regex \"%s\": %s"), regex, errmsg);
+       /*NOTREACHED*/
+    }
+
+    if((result = regexec(&regc, str, 0, 0, 0)) != 0
+       && result != REG_NOMATCH) {
+        regerror(result, &regc, errmsg, SIZEOF(errmsg));
+       error(_("regex \"%s\": %s"), regex, errmsg);
+       /*NOTREACHED*/
+    }
+
+    regfree(&regc);
+
+    return result == 0;
+}
+
+int
+match_no_newline(
+    const char *       regex,
+    const char *       str)
+{
+    regex_t regc;
+    int result;
+    char errmsg[STR_SIZE];
+
+    if((result = regcomp(&regc, regex,
+                        REG_EXTENDED|REG_NOSUB)) != 0) {
+        regerror(result, &regc, errmsg, SIZEOF(errmsg));
+       error(_("regex \"%s\": %s"), regex, errmsg);
+       /*NOTREACHED*/
     }
 
     if((result = regexec(&regc, str, 0, 0, 0)) != 0
        && result != REG_NOMATCH) {
-        regerror(result, &regc, errmsg, sizeof(errmsg));
-       error("regex \"%s\": %s", regex, errmsg);
+        regerror(result, &regc, errmsg, SIZEOF(errmsg));
+       error(_("regex \"%s\": %s"), regex, errmsg);
+       /*NOTREACHED*/
     }
 
     regfree(&regc);
@@ -91,10 +204,11 @@ char *regex, *str;
     return result == 0;
 }
 
-char *validate_glob(glob)
-char *glob;
+char *
+validate_glob(
+    const char *       glob)
 {
-    char *regex = NULL;
+    char *regex;
     regex_t regc;
     int result;
     static char errmsg[STR_SIZE];
@@ -102,7 +216,7 @@ char *glob;
     regex = glob_to_regex(glob);
     if ((result = regcomp(&regc, regex,
                          REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
-      regerror(result, &regc, errmsg, sizeof(errmsg));
+      regerror(result, &regc, errmsg, SIZEOF(errmsg));
       amfree(regex);
       return errmsg;
     }
@@ -113,10 +227,12 @@ char *glob;
     return NULL;
 }
 
-int match_glob(glob, str)
-char *glob, *str;
+int
+match_glob(
+    const char *       glob,
+    const char *       str)
 {
-    char *regex = NULL;
+    char *regex;
     regex_t regc;
     int result;
     char errmsg[STR_SIZE];
@@ -124,16 +240,16 @@ char *glob, *str;
     regex = glob_to_regex(glob);
     if((result = regcomp(&regc, regex,
                         REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
-        regerror(result, &regc, errmsg, sizeof(errmsg));
-       amfree(regex);
-       error("glob \"%s\" -> regex \"%s\": %s", glob, regex, errmsg);
+        regerror(result, &regc, errmsg, SIZEOF(errmsg));
+       error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
+       /*NOTREACHED*/
     }
 
     if((result = regexec(&regc, str, 0, 0, 0)) != 0
        && result != REG_NOMATCH) {
-        regerror(result, &regc, errmsg, sizeof(errmsg));
-       amfree(regex);
-       error("glob \"%s\" -> regex \"%s\": %s", glob, regex, errmsg);
+        regerror(result, &regc, errmsg, SIZEOF(errmsg));
+       error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
+       /*NOTREACHED*/
     }
 
     regfree(&regc);
@@ -142,8 +258,9 @@ char *glob, *str;
     return result == 0;
 }
 
-char *glob_to_regex(glob)
-char *glob;
+char *
+glob_to_regex(
+    const char *       glob)
 {
     char *regex;
     char *r;
@@ -163,6 +280,7 @@ char *glob;
      *
      *  ?      -> [^/]
      *  *      -> [^/]*
+     *  [...] ->  [...]
      *  [!...] -> [^...]
      *
      * The following are given a leading backslash to protect them
@@ -180,12 +298,12 @@ char *glob;
     last_ch = '\0';
     for (ch = *glob++; ch != '\0'; last_ch = ch, ch = *glob++) {
        if (last_ch == '\\') {
-           *r++ = ch;
+           *r++ = (char)ch;
            ch = '\0';                  /* so last_ch != '\\' next time */
        } else if (last_ch == '[' && ch == '!') {
            *r++ = '^';
        } else if (ch == '\\') {
-           *r++ = ch;
+           *r++ = (char)ch;
        } else if (ch == '*' || ch == '?') {
            *r++ = '[';
            *r++ = '^';
@@ -204,13 +322,132 @@ char *glob;
                   || ch == '$'
                   || ch == '|') {
            *r++ = '\\';
-           *r++ = ch;
+           *r++ = (char)ch;
+       } else {
+           *r++ = (char)ch;
+       }
+    }
+    if (last_ch != '\\') {
+       *r++ = '$';
+    }
+    *r = '\0';
+
+    return regex;
+}
+
+
+int
+match_tar(
+    const char *       glob,
+    const char *       str)
+{
+    char *regex;
+    regex_t regc;
+    int result;
+    char errmsg[STR_SIZE];
+
+    regex = tar_to_regex(glob);
+    if((result = regcomp(&regc, regex,
+                        REG_EXTENDED|REG_NOSUB|REG_NEWLINE)) != 0) {
+        regerror(result, &regc, errmsg, SIZEOF(errmsg));
+       error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
+       /*NOTREACHED*/
+    }
+
+    if((result = regexec(&regc, str, 0, 0, 0)) != 0
+       && result != REG_NOMATCH) {
+        regerror(result, &regc, errmsg, SIZEOF(errmsg));
+       error(_("glob \"%s\" -> regex \"%s\": %s"), glob, regex, errmsg);
+       /*NOTREACHED*/
+    }
+
+    regfree(&regc);
+    amfree(regex);
+
+    return result == 0;
+}
+
+static char *
+tar_to_regex(
+    const char *       glob)
+{
+    char *regex;
+    char *r;
+    size_t len;
+    int ch;
+    int last_ch;
+
+    /*
+     * Allocate an area to convert into.  The worst case is a five to
+     * one expansion.
+     */
+    len = strlen(glob);
+    regex = alloc(1 + len * 5 + 5 + 5);
+
+    /*
+     * Do the conversion:
+     *
+     *  ?      -> [^/]
+     *  *      -> .*
+     *  [...]  -> [...]
+     *  [!...] -> [^...]
+     *
+     * The following are given a leading backslash to protect them
+     * unless they already have a backslash:
+     *
+     *   ( ) { } + . ^ $ |
+     *
+     * The expression must begin and end either at the beginning/end of the string or
+     * at at a pathname separator.
+     *
+     * If the last non-escaped character is \ leave the $ off to cause a syntax
+     * error when the regex is compiled.
+     */
+
+    r = regex;
+    *r++ = '(';
+    *r++ = '^';
+    *r++ = '|';
+    *r++ = '/';
+    *r++ = ')';
+    last_ch = '\0';
+    for (ch = *glob++; ch != '\0'; last_ch = ch, ch = *glob++) {
+       if (last_ch == '\\') {
+           *r++ = (char)ch;
+           ch = '\0';                  /* so last_ch != '\\' next time */
+       } else if (last_ch == '[' && ch == '!') {
+           *r++ = '^';
+       } else if (ch == '\\') {
+           *r++ = (char)ch;
+       } else if (ch == '*') {
+           *r++ = '.';
+           *r++ = '*';
+       } else if (ch == '?') {
+           *r++ = '[';
+           *r++ = '^';
+           *r++ = '/';
+           *r++ = ']';
+       } else if (ch == '('
+                  || ch == ')'
+                  || ch == '{'
+                  || ch == '}'
+                  || ch == '+'
+                  || ch == '.'
+                  || ch == '^'
+                  || ch == '$'
+                  || ch == '|') {
+           *r++ = '\\';
+           *r++ = (char)ch;
        } else {
-           *r++ = ch;
+           *r++ = (char)ch;
        }
     }
     if (last_ch != '\\') {
+       *r++ = '(';
        *r++ = '$';
+       *r++ = '|';
+       *r++ = '/';
+       *r++ = ')';
     }
     *r = '\0';
 
@@ -218,9 +455,11 @@ char *glob;
 }
 
 
-int match_word(glob, word, separator)
-char *glob, *word;
-char separator;
+static int
+match_word(
+    const char *       glob,
+    const char *       word,
+    const char         separator)
 {
     char *regex;
     char *r;
@@ -229,16 +468,51 @@ char separator;
     int  last_ch;
     int  next_ch;
     size_t  lenword;
-    char *nword;
-    char *nglob;
-    char *g, *w;
+    char  *mword, *nword;
+    char  *mglob, *nglob;
+    char *g; 
+    const char *w;
     int  i;
 
     lenword = strlen(word);
     nword = (char *)alloc(lenword + 3);
 
+    if (separator == '/' && lenword > 2 && word[0] == '\\' && word[1] == '\\' && !strchr(word, '/')) {
+       /* Convert all "\" to '/' */
+       mword = (char *)alloc(lenword + 1);
+       r = mword;
+       w = word;
+       while (*w != '\0') {
+           if (*w == '\\') {
+               *r++ = '/';
+               w += 1;
+           } else {
+               *r++ = *w++;
+           }
+       }
+       *r++ = '\0';
+       lenword = strlen(word);
+
+       /* Convert all "\\" to '/' */
+       mglob = (char *)alloc(strlen(glob) + 1);
+       r = mglob;
+       w = glob;
+       while (*w != '\0') {
+           if (*w == '\\' && *(w+1) == '\\') {
+               *r++ = '/';
+               w += 2;
+           } else {
+               *r++ = *w++;
+           }
+       }
+       *r++ = '\0';
+    } else {
+       mword = stralloc(word);
+       mglob = stralloc(glob);
+    }
+
     r = nword;
-    w = word;
+    w = mword;
     if(lenword == 1 && *w == separator) {
        *r++ = separator;
        *r++ = separator;
@@ -257,10 +531,10 @@ char separator;
      * Allocate an area to convert into.  The worst case is a six to
      * one expansion.
      */
-    len = strlen(glob);
+    len = strlen(mglob);
     regex = (char *)alloc(1 + len * 6 + 1 + 1 + 2 + 2);
     r = regex;
-    nglob = stralloc(glob);
+    nglob = stralloc(mglob);
     g = nglob;
 
     if((len == 1 && nglob[0] == separator) ||
@@ -309,12 +583,12 @@ char separator;
        for (ch = *g++; ch != '\0'; last_ch = ch, ch = *g++) {
            next_ch = *g;
            if (last_ch == '\\') {
-               *r++ = ch;
+               *r++ = (char)ch;
                ch = '\0';              /* so last_ch != '\\' next time */
            } else if (last_ch == '[' && ch == '!') {
                *r++ = '^';
            } else if (ch == '\\') {
-               *r++ = ch;
+               *r++ = (char)ch;
            } else if (ch == '*' || ch == '?') {
                if(ch == '*' && next_ch == '*') {
                    *r++ = '.';
@@ -335,7 +609,7 @@ char separator;
                    *r++ = '\\';
                    *r++ = separator;
                }
-               *r++ = ch;
+               *r++ = (char)ch;
            } else if (   ch == '('
                       || ch == ')'
                       || ch == '{'
@@ -346,9 +620,9 @@ char separator;
                       || ch == '$'
                       || ch == '|') {
                *r++ = '\\';
-               *r++ = ch;
+               *r++ = (char)ch;
            } else {
-               *r++ = ch;
+               *r++ = (char)ch;
            }
        }
        if(last_ch != '\\') {
@@ -362,6 +636,8 @@ char separator;
 
     i = match(regex,nword);
 
+    amfree(mword);
+    amfree(mglob);
     amfree(nword);
     amfree(nglob);
     amfree(regex);
@@ -369,93 +645,118 @@ char separator;
 }
 
 
-int match_host(glob, host)
-char *glob, *host;
+int
+match_host(
+    const char *       glob,
+    const char *       host)
 {
     char *lglob, *lhost;
-    char *c, *d;
+    char *c;
+    const char *d;
     int i;
 
     
     lglob = (char *)alloc(strlen(glob)+1);
     c = lglob, d=glob;
     while( *d != '\0')
-       *c++ = tolower(*d++);
+       *c++ = (char)tolower(*d++);
     *c = *d;
 
     lhost = (char *)alloc(strlen(host)+1);
     c = lhost, d=host;
     while( *d != '\0')
-       *c++ = tolower(*d++);
+       *c++ = (char)tolower(*d++);
     *c = *d;
 
-    i = match_word(lglob, lhost, '.');
+    i = match_word(lglob, lhost, (int)'.');
     amfree(lglob);
     amfree(lhost);
     return i;
 }
 
 
-int match_disk(glob, disk)
-char *glob, *disk;
+int
+match_disk(
+    const char *       glob,
+    const char *       disk)
 {
-    int i;
-    i = match_word(glob, disk, '/');
-    return i;
+    return match_word(glob, disk, '/');
 }
 
-int match_datestamp(dateexp, datestamp)
-char *dateexp, *datestamp;
+static int
+alldigits(
+    const char *str)
+{
+    while (*str) {
+       if (!isdigit((int)*(str++)))
+           return 0;
+    }
+    return 1;
+}
+
+int
+match_datestamp(
+    const char *       dateexp,
+    const char *       datestamp)
 {
     char *dash;
     size_t len, len_suffix;
-    int len_prefix;
+    size_t len_prefix;
     char firstdate[100], lastdate[100];
     char mydateexp[100];
     int match_exact;
 
     if(strlen(dateexp) >= 100 || strlen(dateexp) < 1) {
-       error("Illegal datestamp expression %s",dateexp);
+       goto illegal;
     }
    
+    /* strip and ignore an initial "^" */
     if(dateexp[0] == '^') {
-       strncpy(mydateexp, dateexp+1, strlen(dateexp)-1); 
-       mydateexp[strlen(dateexp)-1] = '\0';
+       strncpy(mydateexp, dateexp+1, sizeof(mydateexp)-1);
+       mydateexp[sizeof(mydateexp)-1] = '\0';
     }
     else {
-       strncpy(mydateexp, dateexp, strlen(dateexp));
-       mydateexp[strlen(dateexp)] = '\0';
+       strncpy(mydateexp, dateexp, sizeof(mydateexp)-1);
+       mydateexp[sizeof(mydateexp)-1] = '\0';
     }
 
-    if(mydateexp[strlen(mydateexp)] == '$') {
+    if(mydateexp[strlen(mydateexp)-1] == '$') {
        match_exact = 1;
-       mydateexp[strlen(mydateexp)] = '\0';
+       mydateexp[strlen(mydateexp)-1] = '\0';  /* strip the trailing $ */
     }
     else
        match_exact = 0;
 
+    /* a single dash represents a date range */
     if((dash = strchr(mydateexp,'-'))) {
-       if(match_exact == 1) {
-           error("Illegal datestamp expression %s",dateexp);
+       if(match_exact == 1 || strchr(dash+1, '-')) {
+           goto illegal;
        }
-       len = dash - mydateexp;
-       len_suffix = strlen(dash) - 1;
-       len_prefix = len - len_suffix;
 
-       if(len_prefix < 0) {
-           error("Illegal datestamp expression %s",dateexp);
-       }
+       /* format: XXXYYYY-ZZZZ, indicating dates XXXYYYY to XXXZZZZ */
+
+       len = (size_t)(dash - mydateexp);   /* length of XXXYYYY */
+       len_suffix = strlen(dash) - 1;  /* length of ZZZZ */
+       if (len_suffix > len) goto illegal;
+       len_prefix = len - len_suffix; /* length of XXX */
 
        dash++;
+
        strncpy(firstdate, mydateexp, len);
        firstdate[len] = '\0';
        strncpy(lastdate, mydateexp, len_prefix);
        strncpy(&(lastdate[len_prefix]), dash, len_suffix);
        lastdate[len] = '\0';
+       if (!alldigits(firstdate) || !alldigits(lastdate))
+           goto illegal;
+       if (strncmp(firstdate, lastdate, strlen(firstdate)) > 0)
+           goto illegal;
        return ((strncmp(datestamp, firstdate, strlen(firstdate)) >= 0) &&
                (strncmp(datestamp, lastdate , strlen(lastdate))  <= 0));
     }
     else {
+       if (!alldigits(mydateexp))
+           goto illegal;
        if(match_exact == 1) {
            return (strcmp(datestamp, mydateexp) == 0);
        }
@@ -463,4 +764,71 @@ char *dateexp, *datestamp;
            return (strncmp(datestamp, mydateexp, strlen(mydateexp)) == 0);
        }
     }
+illegal:
+       error(_("Illegal datestamp expression %s"),dateexp);
+       /*NOTREACHED*/
+}
+
+
+int
+match_level(
+    const char *       levelexp,
+    const char *       level)
+{
+    char *dash;
+    long int low, hi, level_i;
+    char mylevelexp[100];
+    int match_exact;
+
+    if(strlen(levelexp) >= 100 || strlen(levelexp) < 1) {
+       error(_("Illegal level expression %s"),levelexp);
+       /*NOTREACHED*/
+    }
+   
+    if(levelexp[0] == '^') {
+       strncpy(mylevelexp, levelexp+1, strlen(levelexp)-1); 
+       mylevelexp[strlen(levelexp)-1] = '\0';
+    }
+    else {
+       strncpy(mylevelexp, levelexp, strlen(levelexp));
+       mylevelexp[strlen(levelexp)] = '\0';
+    }
+
+    if(mylevelexp[strlen(mylevelexp)-1] == '$') {
+       match_exact = 1;
+       mylevelexp[strlen(mylevelexp)-1] = '\0';
+    }
+    else
+       match_exact = 0;
+
+    if((dash = strchr(mylevelexp,'-'))) {
+       if(match_exact == 1) {
+            goto illegal;
+       }
+
+        *dash = '\0';
+        if (!alldigits(mylevelexp) || !alldigits(dash+1)) goto illegal;
+
+        errno = 0;
+        low = strtol(mylevelexp, (char **) NULL, 10);
+        if (errno) goto illegal;
+        hi = strtol(dash+1, (char **) NULL, 10);
+        if (errno) goto illegal;
+        level_i = strtol(level, (char **) NULL, 10);
+        if (errno) goto illegal;
+
+       return ((level_i >= low) && (level_i <= hi));
+    }
+    else {
+       if (!alldigits(mylevelexp)) goto illegal;
+       if(match_exact == 1) {
+           return (strcmp(level, mylevelexp) == 0);
+       }
+       else {
+           return (strncmp(level, mylevelexp, strlen(mylevelexp)) == 0);
+       }
+    }
+illegal:
+    error(_("Illegal level expression %s"),levelexp);
+    /*NOTREACHED*/
 }