BUG/MEDIUM: patterns: last fix was still not enough
Last fix did address the issue for inlined patterns, but it was not
enough because the flags are lost as well when updating patterns
dynamically over the CLI.
Also if the same file was used once with -i and another time without
-i, their references would have been merged and both would have used
the same matching method.
It's appear that the patterns have two types of flags. The first
ones are relative to the pattern matching, and the second are
relative to the pattern storage. The pattern matching flags are
the same for all the patterns of one expression. Now they are
stored in the expression. The storage flags are information
returned by the pattern mathing function. This information is
relative to each entry and is stored in the "struct pattern".
Now, the expression matching flags are forwarded to the parse
and index functions. These flags are stored during the
configuration parsing, and they are used during the parse and
index actions.
This issue was introduced in dev23 with the major pattern rework,
and is a continuation of commit a631fc8 ("BUG/MAJOR: patterns: -i
and -n are ignored for inlined patterns"). No backport is needed.
diff --git a/include/proto/pattern.h b/include/proto/pattern.h
index 22da6d5..40e87b8 100644
--- a/include/proto/pattern.h
+++ b/include/proto/pattern.h
@@ -30,7 +30,7 @@
/* pattern management function arrays */
extern char *pat_match_names[PAT_MATCH_NUM];
-extern int (*pat_parse_fcts[PAT_MATCH_NUM])(const char *, struct pattern *, char **);
+extern int (*pat_parse_fcts[PAT_MATCH_NUM])(const char *, struct pattern *, int, char **);
extern int (*pat_index_fcts[PAT_MATCH_NUM])(struct pattern_expr *, struct pattern *, char **);
extern void (*pat_delete_fcts[PAT_MATCH_NUM])(struct pattern_expr *, struct pat_ref_elt *);
extern void (*pat_prune_fcts[PAT_MATCH_NUM])(struct pattern_expr *);
@@ -101,34 +101,34 @@
/* ignore the current line */
-int pat_parse_nothing(const char *text, struct pattern *pattern, char **err);
+int pat_parse_nothing(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse an integer. It is put both in min and max. */
-int pat_parse_int(const char *text, struct pattern *pattern, char **err);
+int pat_parse_int(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse an version. It is put both in min and max. */
-int pat_parse_dotted_ver(const char *text, struct pattern *pattern, char **err);
+int pat_parse_dotted_ver(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse a range of integers delimited by either ':' or '-'. If only one
* integer is read, it is set as both min and max.
*/
-int pat_parse_range(const char *text, struct pattern *pattern, char **err);
+int pat_parse_range(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse a string. It is allocated and duplicated. */
-int pat_parse_str(const char *text, struct pattern *pattern, char **err);
+int pat_parse_str(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse a hexa binary definition. It is allocated and duplicated. */
-int pat_parse_bin(const char *text, struct pattern *pattern, char **err);
+int pat_parse_bin(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse a regex. It is allocated. */
-int pat_parse_reg(const char *text, struct pattern *pattern, char **err);
+int pat_parse_reg(const char *text, struct pattern *pattern, int mflags, char **err);
/* Parse an IP address and an optional mask in the form addr[/mask].
* The addr may either be an IPv4 address or a hostname. The mask
* may either be a dotted mask or a number of bits. Returns 1 if OK,
* otherwise 0.
*/
-int pat_parse_ip(const char *text, struct pattern *pattern, char **err);
+int pat_parse_ip(const char *text, struct pattern *pattern, int mflags, char **err);
/* NB: For two strings to be identical, it is required that their lengths match */
struct pattern *pat_match_str(struct sample *smp, struct pattern_expr *expr, int fill);
@@ -183,7 +183,7 @@
struct pat_ref *pat_ref_newid(int unique_id, const char *display, unsigned int flags);
struct pat_ref_elt *pat_ref_find_elt(struct pat_ref *ref, const char *key);
int pat_ref_append(struct pat_ref *ref, char *pattern, char *sample, int line);
-int pat_ref_add(struct pat_ref *ref, const char *pattern, const char *sample, int patflags, char **err);
+int pat_ref_add(struct pat_ref *ref, const char *pattern, const char *sample, char **err);
int pat_ref_set(struct pat_ref *ref, const char *pattern, const char *sample, char **err);
int pat_ref_set_by_id(struct pat_ref *ref, struct pat_ref_elt *refelt, const char *value, char **err);
int pat_ref_delete(struct pat_ref *ref, const char *key);
diff --git a/include/types/acl.h b/include/types/acl.h
index c5c2824..80b2c39 100644
--- a/include/types/acl.h
+++ b/include/types/acl.h
@@ -93,7 +93,7 @@
const char *kw;
char *fetch_kw;
int match_type; /* Contain PAT_MATCH_* */
- int (*parse)(const char *text, struct pattern *pattern, char **err);
+ int (*parse)(const char *text, struct pattern *pattern, int flags, char **err);
int (*index)(struct pattern_expr *expr, struct pattern *pattern, char **err);
void (*delete)(struct pattern_expr *expr, struct pat_ref_elt *);
void (*prune)(struct pattern_expr *expr);
diff --git a/include/types/pattern.h b/include/types/pattern.h
index a20d6d3..492cdd3 100644
--- a/include/types/pattern.h
+++ b/include/types/pattern.h
@@ -61,13 +61,17 @@
PAT_MATCH = 3, /* sample matched at least one pattern */
};
-/* possible flags for expressions or patterns */
+/* possible flags for patterns matching or parsing */
enum {
- PAT_F_IGNORE_CASE = 1 << 0, /* ignore case */
- PAT_F_TREE = 1 << 1, /* some patterns are arranged in a tree */
- PAT_F_NO_DNS = 1 << 2, /* dont perform any DNS requests */
+ PAT_MF_IGNORE_CASE = 1 << 0, /* ignore case */
+ PAT_MF_NO_DNS = 1 << 1, /* dont perform any DNS requests */
};
+/* possible flags for patterns storage */
+enum {
+ PAT_SF_TREE = 1 << 0, /* some patterns are arranged in a tree */
+};
+
/* ACL match methods */
enum {
PAT_MATCH_FOUND, /* just ensure that fetch found the sample */
@@ -163,7 +167,7 @@
struct my_regex *reg; /* a compiled regex */
} ptr; /* indirect values, allocated */
int len; /* data length when required */
- int flags; /* expr or pattern flags. */
+ int sflags; /* flags relative to the storage method. */
struct sample_storage *smp; /* used to store a pointer to sample value associated
with the match. It is used with maps */
struct pat_ref_elt *ref;
@@ -191,6 +195,7 @@
struct list patterns; /* list of acl_patterns */
struct eb_root pattern_tree; /* may be used for lookup in large datasets */
struct eb_root pattern_tree_2; /* may be used for different types */
+ int mflags; /* flags relative to the parsing or matching method. */
};
/* This is a list of expression. A struct pattern_expr can be used by
@@ -205,7 +210,7 @@
/* This struct contain a list of pattern expr */
struct pattern_head {
- int (*parse)(const char *text, struct pattern *pattern, char **err);
+ int (*parse)(const char *text, struct pattern *pattern, int flags, char **err);
int (*parse_smp)(const char *text, struct sample_storage *smp);
int (*index)(struct pattern_expr *, struct pattern *, char **);
void (*delete)(struct pattern_expr *, struct pat_ref_elt *);
diff --git a/src/acl.c b/src/acl.c
index 93cf31c..10632a7 100644
--- a/src/acl.c
+++ b/src/acl.c
@@ -445,9 +445,9 @@
unique_id = -1;
while (**args == '-') {
if ((*args)[1] == 'i')
- patflags |= PAT_F_IGNORE_CASE;
+ patflags |= PAT_MF_IGNORE_CASE;
else if ((*args)[1] == 'n')
- patflags |= PAT_F_NO_DNS;
+ patflags |= PAT_MF_NO_DNS;
else if ((*args)[1] == 'u') {
unique_id = strtol(args[1], &error, 10);
if (*error != '\0') {
@@ -534,6 +534,9 @@
if (!pattern_expr)
goto out_free_expr;
+ /* Copy the pattern matching and indexing flags. */
+ pattern_expr->mflags = patflags;
+
/* now parse all patterns */
while (**args) {
arg = *args;
@@ -659,7 +662,7 @@
/* Add sample to the reference, and try to compile it fior each pattern
* using this value.
*/
- if (!pat_ref_add(ref, arg, NULL, patflags, err))
+ if (!pat_ref_add(ref, arg, NULL, err))
goto out_free_expr;
args++;
}
diff --git a/src/dumpstats.c b/src/dumpstats.c
index 7cf4d84..5222534 100644
--- a/src/dumpstats.c
+++ b/src/dumpstats.c
@@ -2055,9 +2055,9 @@
/* Add value. */
err = NULL;
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
- ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], 0, &err);
+ ret = pat_ref_add(appctx->ctx.map.ref, args[3], args[4], &err);
else
- ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, 0, &err);
+ ret = pat_ref_add(appctx->ctx.map.ref, args[3], NULL, &err);
if (!ret) {
if (err)
memprintf(&err, "%s.\n", err);
@@ -5012,6 +5012,12 @@
else
chunk_appendf(&trash, "type=%s", pat_match_names[match_method]);
+ /* case sensitive */
+ if (appctx->ctx.map.expr->mflags & PAT_MF_IGNORE_CASE)
+ chunk_appendf(&trash, ", case=insensitive");
+ else
+ chunk_appendf(&trash, ", case=sensitive");
+
/* Display no match, and set default value */
if (!pat) {
if (appctx->ctx.map.display_flags == PAT_REF_MAP)
@@ -5029,17 +5035,11 @@
chunk_appendf(&trash, ", match=yes");
/* display index mode */
- if (pat->flags & PAT_F_TREE)
+ if (pat->sflags & PAT_SF_TREE)
chunk_appendf(&trash, ", idx=tree");
else
chunk_appendf(&trash, ", idx=list");
- /* case sensitive */
- if (pat->flags & PAT_F_IGNORE_CASE)
- chunk_appendf(&trash, ", case=insensitive");
- else
- chunk_appendf(&trash, ", case=sensitive");
-
/* display pattern */
if (appctx->ctx.map.display_flags == PAT_REF_MAP) {
if (pat->ref && pat->ref->pattern)
diff --git a/src/map.c b/src/map.c
index 570937c..a4fd54b 100644
--- a/src/map.c
+++ b/src/map.c
@@ -153,7 +153,7 @@
}
/* Load map. */
- if (!pattern_read_from_file(&desc->pat, PAT_REF_MAP, arg[0].data.str.str, PAT_F_NO_DNS,
+ if (!pattern_read_from_file(&desc->pat, PAT_REF_MAP, arg[0].data.str.str, PAT_MF_NO_DNS,
1, err, file, line))
return 0;
diff --git a/src/pattern.c b/src/pattern.c
index e741b20..80dbaab 100644
--- a/src/pattern.c
+++ b/src/pattern.c
@@ -41,7 +41,7 @@
[PAT_MATCH_REG] = "reg",
};
-int (*pat_parse_fcts[PAT_MATCH_NUM])(const char *, struct pattern *, char **) = {
+int (*pat_parse_fcts[PAT_MATCH_NUM])(const char *, struct pattern *, int, char **) = {
[PAT_MATCH_FOUND] = pat_parse_nothing,
[PAT_MATCH_BOOL] = pat_parse_nothing,
[PAT_MATCH_INT] = pat_parse_int,
@@ -194,13 +194,13 @@
*/
/* ignore the current line */
-int pat_parse_nothing(const char *text, struct pattern *pattern, char **err)
+int pat_parse_nothing(const char *text, struct pattern *pattern, int mflags, char **err)
{
return 1;
}
/* Parse a string. It is allocated and duplicated. */
-int pat_parse_str(const char *text, struct pattern *pattern, char **err)
+int pat_parse_str(const char *text, struct pattern *pattern, int mflags, char **err)
{
pattern->type = SMP_T_STR;
pattern->ptr.str = (char *)text;
@@ -209,7 +209,7 @@
}
/* Parse a binary written in hexa. It is allocated. */
-int pat_parse_bin(const char *text, struct pattern *pattern, char **err)
+int pat_parse_bin(const char *text, struct pattern *pattern, int mflags, char **err)
{
struct chunk *trash;
@@ -221,7 +221,7 @@
}
/* Parse a regex. It is allocated. */
-int pat_parse_reg(const char *text, struct pattern *pattern, char **err)
+int pat_parse_reg(const char *text, struct pattern *pattern, int mflags, char **err)
{
struct chunk *trash;
@@ -252,7 +252,7 @@
* non-zero on success.
*
*/
-int pat_parse_int(const char *text, struct pattern *pattern, char **err)
+int pat_parse_int(const char *text, struct pattern *pattern, int mflags, char **err)
{
const char *ptr = text;
@@ -337,7 +337,7 @@
* acl valid_ssl ssl_req_proto 3.0-3.1
*
*/
-int pat_parse_dotted_ver(const char *text, struct pattern *pattern, char **err)
+int pat_parse_dotted_ver(const char *text, struct pattern *pattern, int mflags, char **err)
{
const char *ptr = text;
@@ -404,9 +404,9 @@
* may either be a dotted mask or a number of bits. Returns 1 if OK,
* otherwise 0. NOTE: IP address patterns are typed (IPV4/IPV6).
*/
-int pat_parse_ip(const char *text, struct pattern *pattern, char **err)
+int pat_parse_ip(const char *text, struct pattern *pattern, int mflags, char **err)
{
- if (str2net(text, !(pattern->flags & PAT_F_NO_DNS) && (global.mode & MODE_STARTING),
+ if (str2net(text, !(mflags & PAT_MF_NO_DNS) && (global.mode & MODE_STARTING),
&pattern->val.ipv4.addr, &pattern->val.ipv4.mask)) {
pattern->type = SMP_T_IPV4;
return 1;
@@ -438,7 +438,6 @@
if (fill) {
static_pattern.smp = NULL;
static_pattern.ref = NULL;
- static_pattern.flags = 0;
static_pattern.type = 0;
static_pattern.ptr.str = NULL;
}
@@ -474,7 +473,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_STR;
static_pattern.ptr.str = (char *)elt->node.key;
}
@@ -489,7 +488,7 @@
if (pattern->len != smp->data.str.len)
continue;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0) ||
(!icase && strncmp(pattern->ptr.str, smp->data.str.str, smp->data.str.len) == 0))
return pattern;
@@ -549,7 +548,7 @@
if (pattern->len > smp->data.str.len)
continue;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0) ||
(!icase && strncmp(pattern->ptr.str, smp->data.str.str, pattern->len) != 0))
continue;
@@ -572,7 +571,7 @@
if (pattern->len > smp->data.str.len)
continue;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if ((icase && strncasecmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0) ||
(!icase && strncmp(pattern->ptr.str, smp->data.str.str + smp->data.str.len - pattern->len, pattern->len) != 0))
continue;
@@ -600,7 +599,7 @@
continue;
end = smp->data.str.str + smp->data.str.len - pattern->len;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if (icase) {
for (c = smp->data.str.str; c <= end; c++) {
if (tolower(*c) != tolower(*pattern->ptr.str))
@@ -626,7 +625,7 @@
* provided as an unsigned int made by make_4delim() and match up to 4 different
* delimiters. Delimiters are stripped at the beginning and end of the pattern.
*/
-static int match_word(struct sample *smp, struct pattern *pattern, unsigned int delimiters)
+static int match_word(struct sample *smp, struct pattern *pattern, int mflags, unsigned int delimiters)
{
int may_match, icase;
char *c, *end;
@@ -648,7 +647,7 @@
return PAT_NOMATCH;
may_match = 1;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = mflags & PAT_MF_IGNORE_CASE;
end = smp->data.str.str + smp->data.str.len - pl;
for (c = smp->data.str.str; c <= end; c++) {
if (is_delimiter(*c, delimiters)) {
@@ -686,7 +685,7 @@
list_for_each_entry(lst, &expr->patterns, list) {
pattern = &lst->pat;
- if (match_word(smp, pattern, make_4delim('/', '?', '?', '?')))
+ if (match_word(smp, pattern, expr->mflags, make_4delim('/', '?', '?', '?')))
return pattern;
}
return NULL;
@@ -703,7 +702,7 @@
list_for_each_entry(lst, &expr->patterns, list) {
pattern = &lst->pat;
- if (match_word(smp, pattern, make_4delim('/', '?', '.', ':')))
+ if (match_word(smp, pattern, expr->mflags, make_4delim('/', '?', '.', ':')))
return pattern;
}
return NULL;
@@ -761,7 +760,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_IPV4;
memcpy(&static_pattern.val.ipv4.addr.s_addr, elt->node.key, 4);
if (!cidr2dotted(elt->node.node.pfx, &static_pattern.val.ipv4.mask))
@@ -783,7 +782,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_IPV6;
memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16);
static_pattern.val.ipv6.mask = elt->node.node.pfx;
@@ -803,7 +802,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_IPV6;
memcpy(&static_pattern.val.ipv6.addr, elt->node.key, 16);
static_pattern.val.ipv6.mask = elt->node.node.pfx;
@@ -837,7 +836,7 @@
elt = ebmb_entry(node, struct pattern_tree, node);
static_pattern.smp = elt->smp;
static_pattern.ref = elt->ref;
- static_pattern.flags = PAT_F_TREE;
+ static_pattern.sflags = PAT_SF_TREE;
static_pattern.type = SMP_T_IPV4;
memcpy(&static_pattern.val.ipv4.addr.s_addr, elt->node.key, 4);
if (!cidr2dotted(elt->node.node.pfx, &static_pattern.val.ipv4.mask))
@@ -1049,7 +1048,7 @@
}
/* compile regex */
- if (!regex_comp(pat->ptr.str, patl->pat.ptr.reg, !(patl->pat.flags & PAT_F_IGNORE_CASE), 0, err)) {
+ if (!regex_comp(pat->ptr.str, patl->pat.ptr.reg, !(expr->mflags & PAT_MF_IGNORE_CASE), 0, err)) {
free(patl);
free(patl->pat.ptr.reg);
return 0;
@@ -1143,7 +1142,7 @@
}
/* If the flag PAT_F_IGNORE_CASE is set, we cannot use trees */
- if (pat->flags & PAT_F_IGNORE_CASE)
+ if (expr->mflags & PAT_MF_IGNORE_CASE)
return pat_idx_list_str(expr, pat, err);
/* Process the key len */
@@ -1670,12 +1669,11 @@
/* initialise pattern */
memset(&pattern, 0, sizeof(pattern));
- pattern.flags = patflags;
pattern.smp = smp;
pattern.ref = elt;
/* parse pattern */
- if (!expr->pat_head->parse(elt->pattern, &pattern, err)) {
+ if (!expr->pat_head->parse(elt->pattern, &pattern, expr->mflags, err)) {
free(smp);
return 0;
}
@@ -1697,7 +1695,7 @@
*/
int pat_ref_add(struct pat_ref *ref,
const char *pattern, const char *sample,
- int patflags, char **err)
+ char **err)
{
struct pat_ref_elt *elt;
struct pattern_expr *expr;
@@ -1732,7 +1730,7 @@
LIST_ADDQ(&ref->head, &elt->list);
list_for_each_entry(expr, &ref->pat, list) {
- if (!pat_ref_push(elt, expr, patflags, err)) {
+ if (!pat_ref_push(elt, expr, 0, err)) {
/* If the insertion fails, try to delete all the added entries. */
pat_ref_delete_by_id(ref, elt);
return 0;
@@ -2108,10 +2106,11 @@
* doesn't exists, create it.
*/
expr = pattern_lookup_expr(head, ref);
- if (!expr) {
+ if (!expr || (expr->mflags != patflags)) {
expr = pattern_new_expr(head, ref, err);
if (!expr)
return 0;
+ expr->mflags = patflags;
}
/* Load reference content in the pattern expression. */
@@ -2142,7 +2141,7 @@
if (fill) {
static_pattern.smp = NULL;
static_pattern.ref = NULL;
- static_pattern.flags = 0;
+ static_pattern.sflags = 0;
static_pattern.type = SMP_T_UINT;
static_pattern.val.i = 1;
}
diff --git a/src/proto_http.c b/src/proto_http.c
index b7f6edf..27693ac 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -3260,7 +3260,7 @@
/* perform update */
/* add entry only if it does not already exist */
if (pat_ref_find_elt(ref, key) == NULL)
- pat_ref_add(ref, key, NULL, 0, NULL);
+ pat_ref_add(ref, key, NULL, NULL);
break;
}
@@ -3295,7 +3295,7 @@
pat_ref_set(ref, key, value, NULL);
else
/* insert a new entry */
- pat_ref_add(ref, key, value, 0, NULL);
+ pat_ref_add(ref, key, value, NULL);
break;
}
@@ -3441,7 +3441,7 @@
/* perform update */
/* check if the entry already exists */
if (pat_ref_find_elt(ref, key) == NULL)
- pat_ref_add(ref, key, NULL, 0, NULL);
+ pat_ref_add(ref, key, NULL, NULL);
break;
}
@@ -3476,7 +3476,7 @@
pat_ref_set(ref, key, value, NULL);
else
/* insert a new entry */
- pat_ref_add(ref, key, value, 0, NULL);
+ pat_ref_add(ref, key, value, NULL);
break;
}
@@ -9590,7 +9590,7 @@
* We use the pre-parsed method if it is known, and store its number as an
* integer. If it is unknown, we use the pointer and the length.
*/
-static int pat_parse_meth(const char *text, struct pattern *pattern, char **err)
+static int pat_parse_meth(const char *text, struct pattern *pattern, int mflags, char **err)
{
int len, meth;
struct chunk *trash;
@@ -9670,7 +9670,7 @@
if (pattern->len != smp->data.meth.str.len)
continue;
- icase = pattern->flags & PAT_F_IGNORE_CASE;
+ icase = expr->mflags & PAT_MF_IGNORE_CASE;
if ((icase && strncasecmp(pattern->ptr.str, smp->data.meth.str.str, smp->data.meth.str.len) != 0) ||
(!icase && strncmp(pattern->ptr.str, smp->data.meth.str.str, smp->data.meth.str.len) != 0))
return pattern;