Handle #1459 - deprecate mg_globmatch, introduce mg_match()

This commit is contained in:
Sergey Lyubka 2022-01-29 11:03:54 +00:00
parent 768f67ae0a
commit 63b880e624
10 changed files with 184 additions and 102 deletions

View File

@ -2273,13 +2273,17 @@ if (mg_vcmp(str, "Hello, world") == 0) {
}
```
### mg\_globmatch()
### mg\_match()
```c
bool mg_globmatch(const char *pattern, size_t p_len, const char *s, size_t s_len);
bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
```
Check if string `s` (limited to `s_len` symbols) matches glob pattern `pattern`, (limited to `p_len` symbols).
Check if string `str` matches glob pattern `pattern`, and optionally capture
wildcards into the provided array `caps`.
<span class="badge bg-danger">NOTE: </span> If `caps` is not NULL, then it
`caps` array size must be at least the number of wildcard symbols in `pattern`
plus 1. Last cap will be initialized to an empty string.
The glob pattern matching rules are as follows:
- `?` matches any single character
@ -2288,21 +2292,19 @@ The glob pattern matching rules are as follows:
- any other character matches itself
Parameters:
- `pattern` - Pattern to match for
- `p_len` - Pattern length
- `s` - String to match
- `s_len` - String length
- `str` - a string to match
- `pattern` - a pattern to match against
- `caps` - an optional array of captures for wildcard symbols `?`, `*`, '#'
Return value: `true` if matches, `false` otherwise
Usage example:
```c
struct mg_str pattern = mg_str("#, ?????");
struct mg_str s = mg_str("Hello, world");
if (mg_globmatch(pattern.ptr, pattern.len, s.otr, s.len)) {
// Match
// Assume that hm->uri holds /foo/bar. Then we can match the requested URI:
struct mg_str caps[2];
if (mg_match(hm->uri, mg_str("/*/*"))) {
// caps[0] holds `foo`, caps[1] holds `bar`.
}
```

View File

@ -59,7 +59,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
LIST_ADD_HEAD(struct sub, &s_subs, sub);
LOG(LL_INFO,
("SUB %p [%.*s]", c->fd, (int) sub->topic.len, sub->topic.ptr));
// Change '+' to '*' for topic matching using mg_globmatch
// Change '+' to '*' for topic matching using mg_match
for (size_t i = 0; i < sub->topic.len; i++) {
if (sub->topic.ptr[i] == '+') ((char *) sub->topic.ptr)[i] = '*';
}
@ -76,8 +76,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
LOG(LL_INFO, ("PUB %p [%.*s] -> [%.*s]", c->fd, (int) mm->data.len,
mm->data.ptr, (int) mm->topic.len, mm->topic.ptr));
for (struct sub *sub = s_subs; sub != NULL; sub = sub->next) {
if (mg_globmatch(sub->topic.ptr, sub->topic.len, mm->topic.ptr,
mm->topic.len)) {
if (mg_match(mm->topic, sub->topic, NULL)) {
mg_mqtt_pub(sub->c, mm->topic, mm->data, 1, false);
}
}

View File

@ -1760,7 +1760,7 @@ struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
}
bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) {
return mg_globmatch(glob, strlen(glob), hm->uri.ptr, hm->uri.len);
return mg_match(hm->uri, mg_str(glob), NULL);
}
static size_t get_chunk_length(const char *buf, size_t len, size_t *ll) {
@ -3870,6 +3870,65 @@ struct mg_str mg_strstrip(struct mg_str s) {
return s;
}
bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
size_t i = 0, j = 0, ni = 0, nj = 0;
if (caps) caps->ptr = NULL, caps->len = 0;
while (i < p.len || j < s.len) {
if (i < p.len && j < s.len && (p.ptr[i] == '?' || s.ptr[j] == p.ptr[i])) {
if (caps == NULL) {
} else if (p.ptr[i] == '?') {
caps->ptr = &s.ptr[j], caps->len = 1; // Finalize `?` cap
caps++, caps->ptr = NULL, caps->len = 0; // Init next cap
} else if (caps->ptr != NULL && caps->len == 0) {
caps->len = (size_t) (&s.ptr[j] - caps->ptr); // Finalize current cap
caps++, caps->len = 0, caps->ptr = NULL; // Init next cap
}
i++, j++;
} else if (i < p.len && (p.ptr[i] == '*' || p.ptr[i] == '#')) {
if (caps && !caps->ptr) caps->len = 0, caps->ptr = &s.ptr[j]; // Init cap
ni = i++, nj = j + 1;
} else if (nj > 0 && nj <= s.len && (p.ptr[ni] == '#' || s.ptr[j] != '/')) {
i = ni, j = nj;
if (caps && caps->ptr == NULL && caps->len == 0) {
caps--, caps->len = 0; // Restart previous cap
}
} else {
return false;
}
}
if (caps && caps->ptr && caps->len == 0) {
caps->len = (size_t) (&s.ptr[j] - caps->ptr);
}
return true;
}
bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
return mg_match(mg_str_n(s2, n2), mg_str_n(s1, n1), NULL);
}
static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
size_t *klen, size_t *voff, size_t *vlen) {
size_t kvlen, kl;
for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != ',';) kvlen++;
for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++;
if (koff != NULL) *koff = ofs;
if (klen != NULL) *klen = kl;
if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0;
if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0;
ofs += kvlen + 1;
return ofs > n ? n : ofs;
}
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0;
if (s->ptr == NULL || s->len == 0) return 0;
off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen);
if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
*s = mg_str_n(s->ptr + off, s->len - off);
return off > 0;
}
#ifdef MG_ENABLE_LINES
#line 1 "src/timer.c"
#endif
@ -4421,46 +4480,6 @@ void mg_random(void *buf, size_t len) {
}
#endif
bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
size_t i = 0, j = 0, ni = 0, nj = 0;
while (i < n1 || j < n2) {
if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
i++, j++;
} else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
ni = i, nj = j + 1, i++;
} else if (nj > 0 && nj <= n2 && (s1[ni] == '#' || s2[j] != '/')) {
i = ni, j = nj;
} else {
// printf(">>: [%s] [%s] %d %d %d %d\n", s1, s2, i, j, ni, nj);
return false;
}
}
return true;
}
static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
size_t *klen, size_t *voff, size_t *vlen) {
size_t kvlen, kl;
for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != ',';) kvlen++;
for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++;
if (koff != NULL) *koff = ofs;
if (klen != NULL) *klen = kl;
if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0;
if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0;
ofs += kvlen + 1;
return ofs > n ? n : ofs;
}
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0;
if (s->ptr == NULL || s->len == 0) return 0;
off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen);
if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
*s = mg_str_n(s->ptr + off, s->len - off);
return off > 0;
}
uint32_t mg_ntohl(uint32_t net) {
uint8_t data[4] = {0, 0, 0, 0};
memcpy(&data, &net, sizeof(data));

View File

@ -580,6 +580,9 @@ int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
struct mg_str mg_strstrip(struct mg_str s);
struct mg_str mg_strdup(const struct mg_str s);
const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
@ -676,8 +679,6 @@ bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...);
void mg_random(void *buf, size_t len);
bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);

View File

@ -808,7 +808,7 @@ struct mg_str mg_http_get_header_var(struct mg_str s, struct mg_str v) {
}
bool mg_http_match_uri(const struct mg_http_message *hm, const char *glob) {
return mg_globmatch(glob, strlen(glob), hm->uri.ptr, hm->uri.len);
return mg_match(hm->uri, mg_str(glob), NULL);
}
static size_t get_chunk_length(const char *buf, size_t len, size_t *ll) {

View File

@ -86,3 +86,62 @@ struct mg_str mg_strstrip(struct mg_str s) {
while (s.len > 0 && isspace((int) *(s.ptr + s.len - 1))) s.len--;
return s;
}
bool mg_match(struct mg_str s, struct mg_str p, struct mg_str *caps) {
size_t i = 0, j = 0, ni = 0, nj = 0;
if (caps) caps->ptr = NULL, caps->len = 0;
while (i < p.len || j < s.len) {
if (i < p.len && j < s.len && (p.ptr[i] == '?' || s.ptr[j] == p.ptr[i])) {
if (caps == NULL) {
} else if (p.ptr[i] == '?') {
caps->ptr = &s.ptr[j], caps->len = 1; // Finalize `?` cap
caps++, caps->ptr = NULL, caps->len = 0; // Init next cap
} else if (caps->ptr != NULL && caps->len == 0) {
caps->len = (size_t) (&s.ptr[j] - caps->ptr); // Finalize current cap
caps++, caps->len = 0, caps->ptr = NULL; // Init next cap
}
i++, j++;
} else if (i < p.len && (p.ptr[i] == '*' || p.ptr[i] == '#')) {
if (caps && !caps->ptr) caps->len = 0, caps->ptr = &s.ptr[j]; // Init cap
ni = i++, nj = j + 1;
} else if (nj > 0 && nj <= s.len && (p.ptr[ni] == '#' || s.ptr[j] != '/')) {
i = ni, j = nj;
if (caps && caps->ptr == NULL && caps->len == 0) {
caps--, caps->len = 0; // Restart previous cap
}
} else {
return false;
}
}
if (caps && caps->ptr && caps->len == 0) {
caps->len = (size_t) (&s.ptr[j] - caps->ptr);
}
return true;
}
bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
return mg_match(mg_str_n(s2, n2), mg_str_n(s1, n1), NULL);
}
static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
size_t *klen, size_t *voff, size_t *vlen) {
size_t kvlen, kl;
for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != ',';) kvlen++;
for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++;
if (koff != NULL) *koff = ofs;
if (klen != NULL) *klen = kl;
if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0;
if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0;
ofs += kvlen + 1;
return ofs > n ? n : ofs;
}
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0;
if (s->ptr == NULL || s->len == 0) return 0;
off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen);
if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
*s = mg_str_n(s->ptr + off, s->len - off);
return off > 0;
}

View File

@ -27,3 +27,6 @@ int mg_strcmp(const struct mg_str str1, const struct mg_str str2);
struct mg_str mg_strstrip(struct mg_str s);
struct mg_str mg_strdup(const struct mg_str s);
const char *mg_strstr(const struct mg_str haystack, const struct mg_str needle);
bool mg_match(struct mg_str str, struct mg_str pattern, struct mg_str *caps);
bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);

View File

@ -25,46 +25,6 @@ void mg_random(void *buf, size_t len) {
}
#endif
bool mg_globmatch(const char *s1, size_t n1, const char *s2, size_t n2) {
size_t i = 0, j = 0, ni = 0, nj = 0;
while (i < n1 || j < n2) {
if (i < n1 && j < n2 && (s1[i] == '?' || s2[j] == s1[i])) {
i++, j++;
} else if (i < n1 && (s1[i] == '*' || s1[i] == '#')) {
ni = i, nj = j + 1, i++;
} else if (nj > 0 && nj <= n2 && (s1[ni] == '#' || s2[j] != '/')) {
i = ni, j = nj;
} else {
// printf(">>: [%s] [%s] %d %d %d %d\n", s1, s2, i, j, ni, nj);
return false;
}
}
return true;
}
static size_t mg_nce(const char *s, size_t n, size_t ofs, size_t *koff,
size_t *klen, size_t *voff, size_t *vlen) {
size_t kvlen, kl;
for (kvlen = 0; ofs + kvlen < n && s[ofs + kvlen] != ',';) kvlen++;
for (kl = 0; kl < kvlen && s[ofs + kl] != '=';) kl++;
if (koff != NULL) *koff = ofs;
if (klen != NULL) *klen = kl;
if (voff != NULL) *voff = kl < kvlen ? ofs + kl + 1 : 0;
if (vlen != NULL) *vlen = kl < kvlen ? kvlen - kl - 1 : 0;
ofs += kvlen + 1;
return ofs > n ? n : ofs;
}
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v) {
size_t koff = 0, klen = 0, voff = 0, vlen = 0, off = 0;
if (s->ptr == NULL || s->len == 0) return 0;
off = mg_nce(s->ptr, s->len, 0, &koff, &klen, &voff, &vlen);
if (k != NULL) *k = mg_str_n(s->ptr + koff, klen);
if (v != NULL) *v = mg_str_n(s->ptr + voff, vlen);
*s = mg_str_n(s->ptr + off, s->len - off);
return off > 0;
}
uint32_t mg_ntohl(uint32_t net) {
uint8_t data[4] = {0, 0, 0, 0};
memcpy(&data, &net, sizeof(data));

View File

@ -5,8 +5,6 @@
#include "str.h"
void mg_random(void *buf, size_t len);
bool mg_globmatch(const char *pattern, size_t plen, const char *s, size_t n);
bool mg_commalist(struct mg_str *s, struct mg_str *k, struct mg_str *v);
uint16_t mg_ntohs(uint16_t net);
uint32_t mg_ntohl(uint32_t net);
uint32_t mg_crc32(uint32_t crc, const char *buf, size_t len);

View File

@ -48,6 +48,47 @@ static void test_globmatch(void) {
ASSERT(mg_globmatch("#.shtml", 7, "./ssi/index.shtml", 17) == 1);
ASSERT(mg_globmatch("#aa#bb#", 7, "caabba", 6) == 1);
ASSERT(mg_globmatch("#aa#bb#", 7, "caabxa", 6) == 0);
ASSERT(mg_globmatch("a*b*c", 5, "a__b_c", 6) == 1);
{
struct mg_str caps[3];
ASSERT(mg_match(mg_str("//a.c"), mg_str("#.c"), NULL) == true);
ASSERT(mg_match(mg_str("a"), mg_str("#"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str("a")) == 0);
ASSERT(mg_match(mg_str("//a.c"), mg_str("#.c"), caps) == true);
ASSERT(mg_match(mg_str("a_b_c_"), mg_str("a*b*c"), caps) == false);
ASSERT(mg_match(mg_str("a__b_c"), mg_str("a*b*c"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str("__")) == 0);
ASSERT(mg_strcmp(caps[1], mg_str("_")) == 0);
ASSERT(mg_match(mg_str("a_b_c__c"), mg_str("a*b*c"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str("_")) == 0);
ASSERT(mg_strcmp(caps[1], mg_str("_c__")) == 0);
ASSERT(mg_match(mg_str("a_xb_.c__c"), mg_str("a*b*c"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str("_x")) == 0);
ASSERT(mg_strcmp(caps[1], mg_str("_.c__")) == 0);
ASSERT(mg_match(mg_str("a"), mg_str("#a"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str("")) == 0);
ASSERT(mg_match(mg_str(".aa..b...b"), mg_str("#a#b"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str(".")) == 0);
ASSERT(mg_strcmp(caps[1], mg_str("a..b...")) == 0);
ASSERT(mg_strcmp(caps[2], mg_str("")) == 0);
ASSERT(mg_match(mg_str("/foo/bar"), mg_str("/*/*"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str("foo")) == 0);
ASSERT(mg_strcmp(caps[1], mg_str("bar")) == 0);
ASSERT(mg_strcmp(caps[2], mg_str("")) == 0);
ASSERT(mg_match(mg_str("/foo/"), mg_str("/*/*"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str("foo")) == 0);
ASSERT(mg_strcmp(caps[1], mg_str("")) == 0);
ASSERT(mg_strcmp(caps[2], mg_str("")) == 0);
ASSERT(mg_match(mg_str("abc"), mg_str("?#"), caps) == true);
ASSERT(mg_strcmp(caps[0], mg_str("a")) == 0);
ASSERT(mg_strcmp(caps[1], mg_str("bc")) == 0);
ASSERT(mg_strcmp(caps[2], mg_str("")) == 0);
}
}
static void test_commalist(void) {
@ -1655,6 +1696,7 @@ static void test_get_header_var(void) {
int main(void) {
mg_log_set("3");
test_globmatch();
test_get_header_var();
test_rewrites();
test_check_ip_acl();
@ -1674,7 +1716,6 @@ int main(void) {
test_iobuf();
test_commalist();
test_base64();
test_globmatch();
test_http_get_var();
test_tls();
test_ws();