|
|
|
@ -144,6 +144,7 @@ struct ast_hint {
|
|
|
|
|
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 @@ static void pbx_destroy(struct ast_pbx *p)
|
|
|
|
|
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 @@ static int extension_close(char *pattern, char *data, int needmore)
|
|
|
|
|
(!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;
|
|
|
|
|