#include "ec_glob.h"
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <regex.h>
struct numpair_s {
long min;
long max;
};
struct ec_glob_re {
char *str;
unsigned len;
unsigned capacity;
};
#ifndef EC_GLOB_STACK_CAPACITY
#define EC_GLOB_STACK_CAPACITY 64
#endif
static void ec_glob_pattern_increase_capacity(
struct ec_glob_re *re) {
unsigned newcap = re->capacity *
2;
char *newmem;
if (re->capacity ==
EC_GLOB_STACK_CAPACITY) {
newmem = malloc(newcap);
if (newmem ==
NULL) abort();
memcpy(newmem, re->str, re->len);
}
else {
newmem = realloc(re->str, newcap);
if (newmem ==
NULL) abort();
}
re->capacity = newcap;
re->str = newmem;
}
#define ec_glob_pattern_ensure_capacity(re, n) \
if (re.len + n > re.capacity) ec_glob_pattern_increase_capacity(&re)
#define ec_glob_cats(re, s) \
ec_glob_pattern_ensure_capacity(re,
sizeof(s) -
1); \
memcpy((re).str + (re).len, s,
sizeof(s) -
1); (re).len +=
sizeof(s)-
1
#define ec_glob_catc(re, c) \
ec_glob_pattern_ensure_capacity(re,
1); (re).str[(re).len++] = (c)
int ec_glob(
const char *pattern,
const char *string) {
char stack[
EC_GLOB_STACK_CAPACITY];
struct ec_glob_re re_pattern = {
stack,
1,
EC_GLOB_STACK_CAPACITY
};
re_pattern.str[
0] =
'^';
unsigned scanidx =
0;
unsigned inputlen = strlen(pattern);
char c;
const unsigned brace_stack_size =
32;
char brace_stack[brace_stack_size];
int depth_brace =
0;
_Bool braces_valid =
1;
for (
unsigned i =
0 ; i < inputlen ; i++) {
if (pattern[i] ==
'\\') {
i++;
}
else if (pattern[i] ==
'{') {
depth_brace++;
}
else if (pattern[i] ==
'}') {
if (depth_brace >
0) {
depth_brace--;
}
else {
braces_valid =
0;
break;
}
}
}
if (depth_brace >
0) {
braces_valid =
0;
depth_brace =
0;
}
const unsigned numrange_max =
32;
unsigned numrange_grp_count =
0;
unsigned numrange_grp_idx[numrange_max];
struct numpair_s numrange_pairs[numrange_max];
regmatch_t numrange_matches[numrange_max];
numrange_grp_idx[
0] =
0;
while (scanidx < inputlen) {
c = pattern[scanidx++];
if (c ==
'\\') {
if (strchr(
"?{}[]*\\-,", pattern[scanidx]) !=
NULL) {
if (strchr(
"?{}[]*\\", pattern[scanidx]) !=
NULL) {
ec_glob_catc(re_pattern,
'\\');
}
c = pattern[scanidx++];
ec_glob_catc(re_pattern, c);
}
else {
ec_glob_cats(re_pattern,
"\\\\");
}
}
else if (c ==
'*') {
if (pattern[scanidx] ==
'*') {
scanidx++;
if (pattern[scanidx] ==
'/' && scanidx >=
3
&& pattern[scanidx -
3] ==
'/') {
scanidx++;
}
ec_glob_cats(re_pattern,
".*");
}
else {
ec_glob_cats(re_pattern,
"[^/]*");
}
}
else if (c ==
'?') {
ec_glob_catc(re_pattern,
'.');
}
else if (c ==
'{') {
if (!braces_valid) {
ec_glob_cats(re_pattern,
"\\{");
continue;
}
depth_brace++;
if (depth_brace > brace_stack_size) {
ec_glob_cats(re_pattern,
"\\{");
continue;
}
_Bool single =
1;
_Bool dotdot = strchr(
"+-0123456789", pattern[scanidx]) !=
NULL;
_Bool dotdot_seen =
0;
for (
unsigned fw = scanidx; fw < inputlen; fw++) {
if (pattern[fw] ==
',') {
single =
0;
dotdot =
0;
break;
}
else if (pattern[fw] ==
'}') {
if (dotdot) {
_Bool ok =
1;
unsigned ngc = numrange_grp_count;
char *chk;
errno =
0;
numrange_pairs[ngc].min = strtol(
&pattern[scanidx], &chk,
10);
ok &= *chk ==
'.' &&
0 == errno;
numrange_pairs[ngc].max = strtol(
strrchr(&pattern[scanidx],
'.')+
1, &chk,
10);
ok &= *chk ==
'}' &&
0 == errno;
if (ok) {
single =
0;
scanidx = fw+
1;
}
else {
dotdot =
0;
}
}
break;
}
else if (dotdot) {
if (pattern[fw] ==
'.') {
if (!dotdot_seen &&
fw +
2 < inputlen && pattern[fw +
1] ==
'.' &&
strchr(
"+-0123456789", pattern[fw +
2]) !=
NULL) {
fw +=
2;
dotdot_seen =
1;
}
else {
dotdot =
0;
}
}
else if (!strchr(
"0123456789", pattern[fw])) {
dotdot =
0;
}
}
}
if (single) {
ec_glob_cats(re_pattern,
"\\{");
brace_stack[depth_brace-
1] =
'}';
}
else {
ec_glob_catc(re_pattern,
'(');
numrange_grp_idx[numrange_grp_count]++;
if (dotdot) {
ec_glob_cats(re_pattern,
"[-+]?[0-9]+)");
numrange_grp_count++;
if (numrange_grp_count < numrange_max) {
numrange_grp_idx[numrange_grp_count] =
numrange_grp_idx[numrange_grp_count -
1];
}
depth_brace--;
}
else {
brace_stack[depth_brace -
1] =
')';
}
}
}
else if (depth_brace >
0 && c ==
'}') {
depth_brace--;
if (depth_brace < brace_stack_size
&& brace_stack[depth_brace] ==
')') {
ec_glob_catc(re_pattern,
')');
}
else {
ec_glob_cats(re_pattern,
"\\}");
}
}
else if (depth_brace >
0 && c ==
',') {
ec_glob_catc(re_pattern,
'|');
}
else if (c ==
'[') {
_Bool valid =
0;
_Bool closing_bracket_literal =
0;
unsigned newidx;
for (
unsigned fw = scanidx ; fw < inputlen ; fw++) {
if (pattern[fw] ==
']') {
if (fw == scanidx) {
closing_bracket_literal =
1;
}
else {
valid =
1;
newidx = fw+
1;
break;
}
}
else if (pattern[fw] ==
'/') {
break;
}
else if (pattern[fw] ==
'\\') {
fw++;
closing_bracket_literal |= pattern[fw] ==
']';
}
}
if (valid) {
if (pattern[scanidx] ==
'!') {
scanidx++;
ec_glob_cats(re_pattern,
"[^");
}
else {
ec_glob_catc(re_pattern,
'[');
}
if (closing_bracket_literal) {
ec_glob_catc(re_pattern,
']');
if (pattern[scanidx] ==
'-') {
scanidx++;
}
}
for (
unsigned fw = scanidx ; ; fw++) {
if (pattern[fw] ==
'\\') {
continue;
}
else if (pattern[fw] ==
']') {
if (fw > scanidx && pattern[fw-
1] !=
'\\') {
break;
}
}
else {
if (strchr(
".(){}[]", pattern[fw]) !=
NULL) {
ec_glob_catc(re_pattern,
'\\');
}
ec_glob_catc(re_pattern, pattern[fw]);
}
}
if (pattern[scanidx-
1] ==
'-') {
ec_glob_cats(re_pattern,
"-]");
}
else {
ec_glob_catc(re_pattern,
']');
}
scanidx = newidx;
}
else {
ec_glob_cats(re_pattern,
"\\[");
}
}
else if (strchr(
".(){}[]", c) !=
NULL) {
ec_glob_catc(re_pattern,
'\\');
ec_glob_catc(re_pattern, c);
}
else {
ec_glob_catc(re_pattern, c);
}
}
ec_glob_catc(re_pattern,
'$');
ec_glob_catc(re_pattern,
'\0');
regex_t re;
int status;
int flags =
REG_EXTENDED;
if (numrange_grp_count ==
0) {
flags |=
REG_NOSUB;
}
if ((status = regcomp(&re, re_pattern.str, flags)) ==
0) {
status = regexec(&re, string, numrange_max, numrange_matches,
0);
for (
unsigned i =
0 ; status ==
0 && i < numrange_grp_count ; i++) {
regmatch_t nm = numrange_matches[numrange_grp_idx[i]];
int nmlen = nm.rm_eo-nm.rm_so;
char *nmatch = malloc(nmlen+
1);
memcpy(nmatch, string+nm.rm_so, nmlen);
nmatch[nmlen] =
'\0';
errno =
0;
char *chk;
long num = strtol(nmatch, &chk,
10);
if (*chk ==
'\0' &&
0 == errno) {
status |= !(numrange_pairs[i].min <= num
&& num <= numrange_pairs[i].max);
}
else {
status =
1;
}
free(nmatch);
}
regfree(&re);
}
if (re_pattern.capacity >
EC_GLOB_STACK_CAPACITY) {
free(re_pattern.str);
}
return status;
}