[Asterisk-cvs] asterisk pbx.c,1.49,1.50

markster at lists.digium.com markster at lists.digium.com
Sat Sep 13 15:19:06 CDT 2003


Update of /usr/cvsroot/asterisk
In directory mongoose.digium.com:/tmp/cvs-serv28210

Modified Files:
	pbx.c 
Log Message:
Add extended Matching functionality (bug #97)


Index: pbx.c
===================================================================
RCS file: /usr/cvsroot/asterisk/pbx.c,v
retrieving revision 1.49
retrieving revision 1.50
diff -u -d -r1.49 -r1.50
--- pbx.c	12 Sep 2003 16:51:35 -0000	1.49
+++ pbx.c	13 Sep 2003 20:20:14 -0000	1.50
@@ -144,6 +144,7 @@
     struct ast_hint *next;
 };
 
+int ast_extension_patmatch(const char *pattern, const char *data);
 
 static int pbx_builtin_prefix(struct ast_channel *, void *);
 static int pbx_builtin_suffix(struct ast_channel *, void *);
@@ -491,86 +492,357 @@
 	free(p);
 }
 
-#define EXTENSION_MATCH_CORE(data,pattern,match) {\
-	/* All patterns begin with _ */\
-	if (pattern[0] != '_') \
-		return 0;\
-	/* Start optimistic */\
-	match=1;\
-	pattern++;\
-	while(match && *data && *pattern && (*pattern != '/')) {\
-		switch(toupper(*pattern)) {\
-		case '[': \
-		{\
-			int i,border=0;\
-			char *where;\
-			match=0;\
-			pattern++;\
-			where=strchr(pattern,']');\
-			if (where)\
-				border=(int)(where-pattern);\
-			if (!where || border > strlen(pattern)) {\
-				ast_log(LOG_WARNING, "Wrong usage of [] in the extension\n");\
-				return match;\
-			}\
-			for (i=0; i<border; i++) {\
-				int res=0;\
-				if (i+2<border)\
-					if (pattern[i+1]=='-') {\
-						if (*data >= pattern[i] && *data <= pattern[i+2]) {\
-							res=1;\
-						} else {\
-							i+=2;\
-							continue;\
-						}\
-					}\
-				if (res==1 || *data==pattern[i]) {\
-					match = 1;\
-					break;\
-				}\
-			}\
-			pattern+=border;\
-			break;\
-		}\
-		case 'N':\
-			if ((*data < '2') || (*data > '9'))\
-				match=0;\
-			break;\
-		case 'X':\
-			if ((*data < '0') || (*data > '9'))\
-				match = 0;\
-			break;\
-		case 'Z':\
-			if ((*data < '1') || (*data > '9'))\
-				match = 0;\
-			break;\
-		case '.':\
-			/* Must match */\
-			return 1;\
-		case ' ':\
-		case '-':\
-			/* Ignore these characters */\
-			data--;\
-			break;\
-		default:\
-			if (*data != *pattern)\
-				match =0;\
-		}\
-		data++;\
-		pattern++;\
-	}\
+int  patmatch_groupcounter = 0;
+char patmatch_group[80] = "";
+
+/* Derived from code by Steffen Offermann 1991, public domain
+   http://www.cs.umu.se/~isak/Snippets/xstrcmp.c
+
+ * a regex must start with "_"
+ * regex patterns are case-insensitive except characters inside []
+ * "." matches zero or more characters (as in * in glob)
+ * character ranges as in [0-9a-zA-Z]
+ * X,Z,N match 0-9,1-9,2-9 resp.
+ 
+ new additional features:
+ * "?" matches any character
+ * negation as in [^0] ("any char but 0")
+               or [^a-z]
+ * explicit quantifiers as in X{2,4} ("from 2 to 4 digits"),
+                           or X{2,}  ("at least 2 digits"),
+                           or X{2}   ("exactly 2 digits"),
+ * regex-style quantifiers like ?, + and * are supported by 
+   "{}" grouping.
+     ? <=> {0,1}
+     + <=> {1,}
+     * <=> {0,}
+ * grouping as in N(1X){1,2}  ("one or two sequences of 1X")
+ * capturing (dependent on AST_PBX_MATCH_CAPTURE)
+   With () grouped matches are stored in subsequent numbered global
+   variables, starting with $1, $2 and so on.
+ * alternation as in (01|0|99) ("01 or 0 or 99")
+ */
+int ast_extension_patmatch(const char *pattern, char *data) 
+{
+    int i,border=0;
+    char *where;
+    static char prev = '\0';
+    static char groupdata[80] = "";
+    static char *group = patmatch_group;
+    int groupcounter = patmatch_groupcounter;
+
+    if (option_debug)
+	ast_log(LOG_DEBUG, " >>> \"%s\" =~ /%s/\n", data, pattern);
+    switch (toupper(*pattern))
+	{
+	case '\0':
+	    if (option_debug)
+		ast_log(LOG_DEBUG, " !>>> \"%s\" => %s\n", data, !*data ? "OK" : "FAIL");
+	    return !*data;
+	    
+	case ' ':
+	case '-':
+	    /* Ignore these characters in the pattern */
+	    return *data && ast_extension_patmatch(pattern+1, data);
+
+	case '.' : /* wildcard as '*' in glob(). Match any sequence of characters. 0 or more */
+	    prev = *pattern;
+	    if (! *(pattern+1) ) 
+		return 1; /* return *data; => match one or more */
+	    else
+		return ast_extension_patmatch(pattern+1, data) || (*data && ast_extension_patmatch(pattern, data+1));
+
+	/* wildcard character: Match any char */
+	case '?' :
+	    prev = *pattern;
+	    return *data && ast_extension_patmatch(pattern+1, data+1);
+
+	case 'X': /* 0-9 */
+	    prev = *pattern;
+	    return ((*data >= '0') && (*data <= '9')) && ast_extension_patmatch(pattern+1, data+1);
+	    
+	case 'Z': /* 1-9 */
+	    prev = *pattern;
+	    return ((*data >= '1') && (*data <= '9')) && ast_extension_patmatch(pattern+1, data+1);
+	    
+	case 'N': /* 2-9 */
+	    prev = *pattern;
+	    return ((*data >= '2') && (*data <= '9')) && ast_extension_patmatch(pattern+1, data+1);
+
+	case '{': /* quantifier {n[,m]} */
+	  {
+	    char *comma;
+	    int cpos;
+	    where=strchr(pattern,'}');
+	    if (where) {
+		border=(int)(where-pattern);
+		comma = strchr(pattern,',');
+	    }
+	    if (!where || border > strlen(pattern)) {
+		ast_log(LOG_WARNING, "Wrong %s pattern usage\n", pattern);
+		return 0;
+	    } else {
+		char tmp[8];
+		int from, to;
+		if (comma)
+		    cpos = (int)(comma-pattern);
+		else 
+		    cpos = border;
+		strncpy(tmp,pattern+1,cpos-1);
+		tmp[cpos-1] = '\0';
+		from = atoi(tmp);
+		if (comma) {
+		    if (border-cpos > 1) { /* {f,t} */
+			strncpy(tmp,comma+1,border-cpos);
+			tmp[border-cpos+1] = '\0';
+			to = atoi(tmp);
+		    } else { /* {f,} */
+			to = strlen(data); /* may fail if after the group are more pattern chars */
+			if (*(pattern+border+1)) {
+			    to = to - strlen(pattern+border+1) + 1;
+			}
+		    }
+		} else {     /* {f} */
+		    if (from == 0) {
+			ast_log(LOG_WARNING, "Invalid {0} pattern quantifier %s\n", pattern);
+			return 0;
+		    }
+		    to = from;
+		}
+		if (from < 0 || to <= 0 || to < from) {
+		    ast_log(LOG_WARNING, "Invalid pattern quantifier %s\n", pattern);
+		    return 0;
+		}
+
+		if (*group) { 	/* check for repeated pattern{n,m} in previous group */
+		    int i;
+		    for (i=0; i< strlen(group); i++) {
+			data--;
+		    }
+		    if (option_debug)
+			ast_log(LOG_DEBUG, ">>> check for repeated pattern{%d,%d} of group '%s' in data '%s'\n", from, to, group, data);
+		    strcat(group,".");
+		} else {
+		    if (option_debug)
+			ast_log(LOG_DEBUG, ">>> check for repeated pattern{%d,%d} in previous character '%c'\n", from, to, prev);
+		    data--;
+		    group[0] = prev;
+		    group[1] = '.';
+		    group[2] = '\0';
+		}
+		*tmp = prev;
+		for (i=to; i>=from; i--) {
+		    if (ast_extension_patmatch_repeated(group,data,i)) break;
+		}
+		prev = *tmp;
+		if (i >= from || !from) { /* if found */
+		    if (option_debug)
+			ast_log(LOG_DEBUG, " >>>> found '%s' in data '%s' after %d runs\n", group, data, i);
+		    char name[16];
+		    data = data + (i * (strlen(group)- 1)) - 1;
+		    int l = strlen(groupdata) - strlen(data);
+		    /* data = data-i+from-1; */		/* possible failure here! */
+		    if (prev == ')') {			/* grouping => capture */
+			*(group+strlen(group)-1) = '\0';
+			groupdata[l+1] = '\0';
+			if (option_debug)
+			    ast_log(LOG_DEBUG, "  >>>>> end of group '%s', data: %s\n", group, groupdata);
+			/* capture the found data in variables $1, $2, ... */
+#ifdef AST_PBX_MATCH_CAPTURE
+			sprintf(name,"%d",++groupcounter);
+			pbx_builtin_setvar_helper(NULL,name,groupdata);
+			if (option_verbose > 2)
+			    ast_log(VERBOSE_PREFIX_3 "global variable $%s set to '%s'\n", name, groupdata);
+#endif
+		    }
+		}
+		*group = '\0';
+		prev = '\0';
+		if (i >= from) {        /* found: continue */
+		    if (option_debug)
+			ast_log(LOG_DEBUG, " >>>> found in round %d from %d\n", i, to);
+		    if (*data) {
+			if (*(pattern+border+1)) /* if the tail check fails, try the other rounds */
+			    if (ast_extension_patmatch(pattern+border+1, data+1))
+				return 1;
+			    else return (ast_extension_patmatch_repeated(group, data, i--) && 
+					 ast_extension_patmatch(pattern+border+1, data+i));
+			else
+			    return ast_extension_patmatch(pattern+border+1, data+1);
+		    }
+		    else 
+			return 1;
+		} else if (from == 0) { /* not found, but special case from=0: no match needed */
+		    if (option_debug)
+			ast_log(LOG_DEBUG, " >>>> not found, but no match needed and data exhausted\n");
+		    if (*data)
+			return ast_extension_patmatch(pattern+border+1, data+1);
+		    else 
+			return 1;
+		} else                /* not found */
+		    return 0;
+	    }
+	  }
+	  /* unreachable code */
+	    
+	case '(': /* grouping */
+	    prev = *pattern;
+	    if (*group) {
+		ast_log(LOG_WARNING, "Unexpected subgroup ( in pattern %s\n", pattern);
+		return 0;
+	    }
+	    where=strchr(pattern,')');
+	    if (where)
+		border=(int)(where-pattern);
+	    if (!where || border > strlen(pattern)) {
+		ast_log(LOG_WARNING, "Wrong (%s) pattern usage\n", pattern);
+		return 0;
+	    }
+	    strncpy(group,pattern+1,border-1);
+	    group[border-1] = '\0';
+	    strcpy(groupdata,data);
+	    if (option_debug)
+		ast_log(LOG_DEBUG, ">>> group '%s' stored, data: '%s'\n", group, groupdata);
+	    if (strchr(pattern,'|')) { /* alternations */
+		char *s, *scopy, *sep, *sepcopy;
+		s = scopy = (char *) malloc(strlen(pattern));
+		sepcopy   = (char *) malloc(strlen(pattern));
+		strcpy(s,group);
+		while (sep = strsep(&s,"|")) {
+		    strcpy(sepcopy,sep);
+		    strcat(sepcopy,pattern+border+1);
+		    if (option_debug)
+			ast_log(LOG_DEBUG, "  >>>> alternative '%s' =~ /%s/\n", sepcopy, data);
+		    if (ast_extension_patmatch(sepcopy, data)) break;
+		    if (!*data) { 
+			sep = NULL; break; 
+		    }
+		}
+		free(scopy);
+		if (sep) { /* found */
+		    free(sepcopy);
+		    return 1;
+		} else {
+		    free(sepcopy);
+		    return 0;
+		}
+	    } else {
+		return ast_extension_patmatch(pattern+1, data);
+	    }
+
+	case ')': /* group end */
+	    prev = *pattern;
+	    if (!*group) {
+		ast_log(LOG_WARNING, "Unexpected ) in pattern %s\n", pattern);
+		return 0;
+	    } else {
+		if (pattern[1] != '{') { /* capture without quantifiers */
+		    char name[16];
+		    int l = strlen(groupdata) - strlen(data);
+		    groupdata[l-1] = '\0';
+		    *(group+strlen(group)-1) = '\0';
+		    if (option_debug)
+			ast_log(LOG_DEBUG, ">>> end of group '%s', data: %s\n", group, groupdata);
+#ifdef AST_PBX_MATCH_CAPTURE
+		    /* capture the found data in variables $1, $2, ... */
+		    sprintf(name,"%d",++groupcounter);
+		    pbx_builtin_setvar_helper(NULL,name,groupdata);
+		    ast_log(VERBOSE_PREFIX_3 "global variable $%s set to '%s'\n", name, groupdata);
+#endif
+		    *group = '\0';
+		}
+	    }
+	    return ast_extension_patmatch(pattern+1, data);
+
+	case '|': /* alternation */
+	    if (!*group) {
+		ast_log(LOG_WARNING, "Need group for | in %s\n", pattern);
+		return 0;
+	    }
+
+	case '[': /* Character ranges: [0-9a-zA-Z] */
+	    prev = *pattern;
+	    pattern++;
+	    where=strchr(pattern,']');
+	    if (where)
+		border=(int)(where-pattern);
+	    if (!where || border > strlen(pattern)) {
+		ast_log(LOG_WARNING, "Wrong [%s] pattern usage\n", pattern);
+		return 0;
+	    }
+	    if (*pattern == '^') { /* Negation like [^...] */
+		for (i=1; i<border; i++) {
+		    if (*data==pattern[i])
+			return 0;
+		    else if ((pattern[i+1]=='-') && (i+2<border)) {
+			if (*data >= pattern[i] && *data <= pattern[i+2]) {
+			    return 0;
+			} else {
+			    i+=2;
+			    continue;
+			}
+		    }
+		}
+		return ast_extension_patmatch(where+1, data+1);
+	    } else {
+		for (i=0; i<border; i++) {
+		    if (i+2<border) {
+			if (*data==pattern[i])
+			    return ast_extension_patmatch(where+1, data+1);
+			else if (pattern[i+1]=='-') {
+			    if (*data >= pattern[i] && *data <= pattern[i+2]) {
+				return ast_extension_patmatch(where+1, data+1);
+			    } else {
+				i+=2;
+				continue;
+			    }
+			}
+		    }
+		}
+	    }
+	    break;
+	    
+	default  :
+	    prev = *pattern;
+	    return (toupper(*pattern) == toupper(*data)) && ast_extension_patmatch(pattern+1, data+1);
+	}
+    return 0;
+}
+
+/* try exactly num repetitions, from high to from */
+int ast_extension_patmatch_repeated(const char *pattern, char *data, const int num) 
+{
+    int i;
+    ast_log(LOG_DEBUG, "  >>> try %d repetitions of '%s' in data '%s'\n", num, pattern, data);
+    if (num <= 0) return 0;
+    for (i=1; i<=num; i++) {
+	ast_log(LOG_DEBUG, "  >>>> round %d with data %s\n", i, data);
+	if (!ast_extension_patmatch(pattern, data)) return 0;
+	data = data + strlen(pattern) - 1;
+    }
+    return 1;
 }
 
 int ast_extension_match(char *pattern, char *data)
 {
 	int match;
-	/* If they're the same return */
-	if (!strcmp(pattern, data))
-		return 1;
-	EXTENSION_MATCH_CORE(data,pattern,match);
-	/* Must be at the end of both */
-	if (*data || (*pattern && (*pattern != '/')))
-		match = 0;
+	patmatch_groupcounter = 0;
+	*patmatch_group = '\0';
+	if (!*pattern) {
+	    ast_log(LOG_WARNING, "ast_extension_match: empty pattern\n");
+	    return 0;
+	}
+	if (!*data) {
+	    ast_log(LOG_WARNING, "ast_extension_match: empty data\n");
+	    return 0;
+	}
+	if (pattern[0] != '_') {
+	    match = (strcmp(pattern, data) == 0);
+	    ast_log(LOG_DEBUG, "ast_extension_match %s == /%s/ => %d\n", data, pattern, match);
+	} else {
+	    match = ast_extension_patmatch(pattern+1,data);
+	    ast_log(LOG_DEBUG, "ast_extension_match %s =~ /%s/ => %d\n", data, pattern+1, match);
+	}
 	return match;
 }
 
@@ -586,7 +858,9 @@
 		(!needmore || (strlen(pattern) > strlen(data)))) {
 		return 1;
 	}
-	EXTENSION_MATCH_CORE(data,pattern,match);
+	if (pattern[0] == '_') {
+	    match = ast_extension_patmatch(pattern+1,data);
+	}
 	/* If there's more or we don't care about more, return non-zero, otlherwise it's a miss */
 	if (!needmore || *pattern) {
 		return match;




More information about the svn-commits mailing list