[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