Imported Upstream version 3.2.0
[debian/amanda] / common-src / match.c
index f9ce31ded98bf71d8e8f78bc72b60636fe62957c..826a2867fafceb46bd2e9dbc2b65184e2350b21e 100644 (file)
  */
 
 #include "amanda.h"
+#include "match.h"
 #include <regex.h>
 
 static int match_word(const char *glob, const char *word, const char separator);
+static char *tar_to_regex(const char *glob);
 
 char *
 validate_regexp(
@@ -55,18 +57,93 @@ validate_regexp(
 
 char *
 clean_regex(
-    const char *       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 */
 
-    for(i=0,j=0;i<strlen(regex);i++) {
-       if(!isalnum((int)regex[i]))
+           default:
+           result[j++]=host[i];
+       }
+    }
+    result[j++] = '$';
+    result[j] = '\0';
+    return result;
+}
+
+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++] = '$';
     result[j] = '\0';
     return result;
 }
@@ -99,6 +176,34 @@ match(
     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);
+       /*NOTREACHED*/
+    }
+
+    regfree(&regc);
+
+    return result == 0;
+}
+
 char *
 validate_glob(
     const char *       glob)
@@ -175,6 +280,7 @@ glob_to_regex(
      *
      *  ?      -> [^/]
      *  *      -> [^/]*
+     *  [...] ->  [...]
      *  [!...] -> [^...]
      *
      * The following are given a leading backslash to protect them
@@ -261,7 +367,7 @@ match_tar(
     return result == 0;
 }
 
-char *
+static char *
 tar_to_regex(
     const char *       glob)
 {
@@ -276,13 +382,14 @@ tar_to_regex(
      * one expansion.
      */
     len = strlen(glob);
-    regex = alloc(1 + len * 5 + 1 + 1);
+    regex = alloc(1 + len * 5 + 5 + 5);
 
     /*
      * Do the conversion:
      *
      *  ?      -> [^/]
      *  *      -> .*
+     *  [...]  -> [...]
      *  [!...] -> [^...]
      *
      * The following are given a leading backslash to protect them
@@ -290,13 +397,19 @@ tar_to_regex(
      *
      *   ( ) { } + . ^ $ |
      *
-     * Put a leading ^ and trailing $ around the result.  If the last
-     * non-escaped character is \ leave the $ off to cause a syntax
+     * 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 == '\\') {
@@ -330,7 +443,11 @@ tar_to_regex(
        }
     }
     if (last_ch != '\\') {
+       *r++ = '(';
        *r++ = '$';
+       *r++ = '|';
+       *r++ = '/';
+       *r++ = ')';
     }
     *r = '\0';
 
@@ -351,8 +468,8 @@ match_word(
     int  last_ch;
     int  next_ch;
     size_t  lenword;
-    char *nword;
-    char *nglob;
+    char  *mword, *nword;
+    char  *mglob, *nglob;
     char *g; 
     const char *w;
     int  i;
@@ -360,8 +477,42 @@ match_word(
     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;
@@ -380,10 +531,10 @@ match_word(
      * 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) ||
@@ -485,6 +636,8 @@ match_word(
 
     i = match(regex,nword);
 
+    amfree(mword);
+    amfree(mglob);
     amfree(nword);
     amfree(nglob);
     amfree(regex);
@@ -530,6 +683,17 @@ match_disk(
     return match_word(glob, disk, '/');
 }
 
+static int
+alldigits(
+    const char *str)
+{
+    while (*str) {
+       if (!isdigit((int)*(str++)))
+           return 0;
+    }
+    return 1;
+}
+
 int
 match_datestamp(
     const char *       dateexp,
@@ -543,45 +707,56 @@ match_datestamp(
     int match_exact;
 
     if(strlen(dateexp) >= 100 || strlen(dateexp) < 1) {
-       error(_("Illegal datestamp expression %s"),dateexp);
-       /*NOTREACHED*/
+       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);
-           /*NOTREACHED*/
+       if(match_exact == 1 || strchr(dash+1, '-')) {
+           goto illegal;
        }
-       len = (size_t)(dash - mydateexp);
-       len_suffix = strlen(dash) - 1;
-       len_prefix = len - len_suffix;
+
+       /* 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);
        }
@@ -589,6 +764,9 @@ match_datestamp(
            return (strncmp(datestamp, mydateexp, strlen(mydateexp)) == 0);
        }
     }
+illegal:
+       error(_("Illegal datestamp expression %s"),dateexp);
+       /*NOTREACHED*/
 }
 
 
@@ -598,9 +776,7 @@ match_level(
     const char *       level)
 {
     char *dash;
-    size_t len, len_suffix;
-    size_t len_prefix;
-    char lowend[100], highend[100];
+    long int low, hi, level_i;
     char mylevelexp[100];
     int match_exact;
 
@@ -618,32 +794,33 @@ match_level(
        mylevelexp[strlen(levelexp)] = '\0';
     }
 
-    if(mylevelexp[strlen(mylevelexp)] == '$') {
+    if(mylevelexp[strlen(mylevelexp)-1] == '$') {
        match_exact = 1;
-       mylevelexp[strlen(mylevelexp)] = '\0';
+       mylevelexp[strlen(mylevelexp)-1] = '\0';
     }
     else
        match_exact = 0;
 
     if((dash = strchr(mylevelexp,'-'))) {
        if(match_exact == 1) {
-           error(_("Illegal level expression %s"),levelexp);
-           /*NOTREACHED*/
+            goto illegal;
        }
-       len = (size_t)(dash - mylevelexp);
-       len_suffix = strlen(dash) - 1;
-       len_prefix = len - len_suffix;
 
-       dash++;
-       strncpy(lowend, mylevelexp, len);
-       lowend[len] = '\0';
-       strncpy(highend, mylevelexp, len_prefix);
-       strncpy(&(highend[len_prefix]), dash, len_suffix);
-       highend[len] = '\0';
-       return ((strncmp(level, lowend, strlen(lowend)) >= 0) &&
-               (strncmp(level, highend , strlen(highend))  <= 0));
+        *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);
        }
@@ -651,4 +828,7 @@ match_level(
            return (strncmp(level, mylevelexp, strlen(mylevelexp)) == 0);
        }
     }
+illegal:
+    error(_("Illegal level expression %s"),levelexp);
+    /*NOTREACHED*/
 }