#include "re.h"
#include "re_memo.h"
#include <ctype.h>
#ifndef RE_DOT_MATCHES_NEWLINE
#define RE_DOT_MATCHES_NEWLINE 0
#endif
#include <string.h>
enum { UNUSED, DOT, BEGIN, END, QUESTIONMARK, STAR, PLUS, CHAR, CHAR_CLASS, INV_CHAR_CLASS, DIGIT, NOT_DIGIT, ALPHA, NOT_ALPHA, WHITESPACE, NOT_WHITESPACE, };
typedef struct {
const regex_t* pat_base;
const unsigned char* text_base;
const unsigned char* ccl_buf;
int* matchlength;
unsigned char* memo;
size_t memo_stride;
} re_match_ctx_t;
static int matchpattern(const regex_t* pattern, const unsigned char* text, re_match_ctx_t* ctx);
static int matchcharclass(unsigned char c, const unsigned char* str);
static int matchstar(regex_t p, const regex_t* pattern, const unsigned char* text, re_match_ctx_t* ctx);
static int matchplus(regex_t p, const regex_t* pattern, const unsigned char* text, re_match_ctx_t* ctx);
static int matchquestion(regex_t p, const regex_t* pattern, const unsigned char* text, re_match_ctx_t* ctx);
static int matchone(regex_t p, unsigned char c, const unsigned char* ccl_buf);
static int matchdigit(unsigned char c);
static int matchalpha(unsigned char c);
static int matchwhitespace(unsigned char c);
static int matchmetachar(unsigned char c, const unsigned char* str);
static int matchrange(unsigned char c, const unsigned char* str);
static int matchdot(unsigned char c);
int re_matchp(re_t pattern, const unsigned char* ccl_buf,
const char* text, int* matchlength,
unsigned char* memo, size_t memo_bytes, size_t memo_stride)
{
const unsigned char* utext = (const unsigned char*)text;
*matchlength = 0;
if (pattern != 0)
{
if (!memo_bytes) memo = NULL;
re_match_ctx_t ctx = { pattern, utext, ccl_buf, matchlength, memo, memo_stride };
if (memo) memset(memo, 0, memo_bytes);
if (pattern[0].type == BEGIN)
{
return ((matchpattern(&pattern[1], utext, &ctx)) ? 0 : -1);
}
else
{
int idx = -1;
do
{
idx += 1;
ctx.text_base = utext;
if (memo) memset(memo, 0, memo_bytes);
if (matchpattern(pattern, utext, &ctx))
{
return idx;
}
}
while (*utext++ != '\0');
}
}
return -1;
}
size_t re_compile(regex_t* re_compiled, size_t size,
unsigned char* ccl_buf, size_t ccl_size,
const char* pattern)
{
size_t ccl_bufidx = 1;
char c;
size_t i = 0;
size_t j = 0;
while (pattern[i] != '\0' && (j+1 < size))
{
c = pattern[i];
switch (c)
{
case '^': { re_compiled[j].type = BEGIN; } break;
case '$': { re_compiled[j].type = END; } break;
case '.': { re_compiled[j].type = DOT; } break;
case '*': { re_compiled[j].type = STAR; } break;
case '+': { re_compiled[j].type = PLUS; } break;
case '?': { re_compiled[j].type = QUESTIONMARK; } break;
case '\\':
{
if (pattern[i+1] != '\0')
{
i += 1;
switch (pattern[i])
{
case 'd': { re_compiled[j].type = DIGIT; } break;
case 'D': { re_compiled[j].type = NOT_DIGIT; } break;
case 'w': { re_compiled[j].type = ALPHA; } break;
case 'W': { re_compiled[j].type = NOT_ALPHA; } break;
case 's': { re_compiled[j].type = WHITESPACE; } break;
case 'S': { re_compiled[j].type = NOT_WHITESPACE; } break;
default:
{
re_compiled[j].type = CHAR;
re_compiled[j].u.ch = pattern[i];
} break;
}
}
else
{
return 0;
}
} break;
case '[':
{
size_t buf_begin = ccl_bufidx;
if (pattern[i+1] == '^')
{
re_compiled[j].type = INV_CHAR_CLASS;
i += 1;
if (pattern[i+1] == 0)
{
return 0;
}
}
else
{
re_compiled[j].type = CHAR_CLASS;
}
while ( (pattern[++i] != ']')
&& (pattern[i] != '\0'))
{
if (pattern[i] == '\\')
{
if (ccl_bufidx + 1 >= ccl_size)
{
return 0;
}
if (pattern[i+1] == 0)
{
return 0;
}
ccl_buf[ccl_bufidx++] = pattern[i++];
}
else if (ccl_bufidx >= ccl_size)
{
return 0;
}
ccl_buf[ccl_bufidx++] = pattern[i];
}
if (ccl_bufidx >= ccl_size)
{
return 0;
}
ccl_buf[ccl_bufidx++] = 0;
re_compiled[j].u.ccl = buf_begin;
} break;
default:
{
re_compiled[j].type = CHAR;
re_compiled[j].u.ch = c;
} break;
}
if (pattern[i] == 0)
{
return 0;
}
i += 1;
j += 1;
}
if (pattern[i] != '\0')
{
return 0;
}
re_compiled[j].type = UNUSED;
return (j + 1) * sizeof(re_compiled[0]);
}
static int matchdigit(unsigned char c)
{
return isdigit(c);
}
static int matchalpha(unsigned char c)
{
return isalpha(c);
}
static int matchwhitespace(unsigned char c)
{
return isspace(c);
}
static int matchalphanum(unsigned char c)
{
return ((c == '_') || matchalpha(c) || matchdigit(c));
}
static int matchrange(unsigned char c, const unsigned char* str)
{
return ( (c != '-')
&& (str[0] != '\0')
&& (str[0] != '-')
&& (str[1] == '-')
&& (str[2] != '\0')
&& ( (c >= str[0])
&& (c <= str[2])));
}
static int matchdot(unsigned char c)
{
#if RE_DOT_MATCHES_NEWLINE
(void)c;
return 1;
#else
return c != '\n' && c != '\r';
#endif
}
static int matchmetachar(unsigned char c, const unsigned char* str)
{
switch (str[0])
{
case 'd': return matchdigit(c);
case 'D': return !matchdigit(c);
case 'w': return matchalphanum(c);
case 'W': return !matchalphanum(c);
case 's': return matchwhitespace(c);
case 'S': return !matchwhitespace(c);
default: return (c == str[0]);
}
}
static int matchcharclass(unsigned char c, const unsigned char* str)
{
do
{
if (matchrange(c, str))
{
return 1;
}
else if (str[0] == '\\')
{
str += 1;
if (matchmetachar(c, str))
{
return 1;
}
}
else if (c == str[0])
{
if (c == '-')
{
return ((str[-1] == '\0') || (str[1] == '\0'));
}
else
{
return 1;
}
}
}
while (*str++ != '\0');
return 0;
}
static int matchone(regex_t p, unsigned char c, const unsigned char* ccl_buf)
{
switch (p.type)
{
case DOT: return matchdot(c);
case CHAR_CLASS: return matchcharclass(c, ccl_buf + p.u.ccl);
case INV_CHAR_CLASS: return !matchcharclass(c, ccl_buf + p.u.ccl);
case DIGIT: return matchdigit(c);
case NOT_DIGIT: return !matchdigit(c);
case ALPHA: return matchalphanum(c);
case NOT_ALPHA: return !matchalphanum(c);
case WHITESPACE: return matchwhitespace(c);
case NOT_WHITESPACE: return !matchwhitespace(c);
case CHAR: return (p.u.ch == c);
default: return 0;
}
}
static int matchstar(regex_t p, const regex_t* pattern, const unsigned char* text, re_match_ctx_t* ctx)
{
int prelen = *ctx->matchlength;
const unsigned char* prepoint = text;
while ((text[0] != '\0') && matchone(p, *text, ctx->ccl_buf))
{
text++;
(*ctx->matchlength)++;
}
int n = (int)(text - prepoint);
do {
if (matchpattern(pattern, &prepoint[n], ctx))
return 1;
(*ctx->matchlength)--;
} while (n-- > 0);
*ctx->matchlength = prelen;
return 0;
}
static int matchplus(regex_t p, const regex_t* pattern, const unsigned char* text, re_match_ctx_t* ctx)
{
const unsigned char* prepoint = text;
while ((text[0] != '\0') && matchone(p, *text, ctx->ccl_buf))
{
text++;
(*ctx->matchlength)++;
}
while (text > prepoint)
{
if (matchpattern(pattern, text--, ctx))
return 1;
(*ctx->matchlength)--;
}
return 0;
}
static int matchquestion(regex_t p, const regex_t* pattern, const unsigned char* text, re_match_ctx_t* ctx)
{
if (*text && matchone(p, *text, ctx->ccl_buf))
{
(*ctx->matchlength)++;
if (matchpattern(pattern, text + 1, ctx))
return 1;
(*ctx->matchlength)--;
}
return matchpattern(pattern, text, ctx);
}
#if 0#else
static int matchpattern(const regex_t* pattern, const unsigned char* text, re_match_ctx_t* ctx)
{
const re_memo_ctx_t memo_ctx = {
ctx->memo,
(size_t)(pattern - ctx->pat_base),
(size_t)(text - ctx->text_base),
ctx->memo_stride,
};
if (memo_ctx.memo
&& memo_ctx.entry_offset < memo_ctx.memo_stride
&& RE_MEMO_GET(memo_ctx))
return 0;
int pre = *ctx->matchlength;
do
{
if (pattern[0].type == UNUSED)
{
return 1;
}
else if (pattern[1].type == QUESTIONMARK)
{
RE_MEMO_FAIL(memo_ctx, ctx, pre, matchquestion(pattern[0], &pattern[2], text, ctx));
}
else if (pattern[1].type == STAR)
{
RE_MEMO_FAIL(memo_ctx, ctx, pre, matchstar(pattern[0], &pattern[2], text, ctx));
}
else if (pattern[1].type == PLUS)
{
RE_MEMO_FAIL(memo_ctx, ctx, pre, matchplus(pattern[0], &pattern[2], text, ctx));
}
else if ((pattern[0].type == END) && pattern[1].type == UNUSED)
{
RE_MEMO_FAIL(memo_ctx, ctx, pre, (text[0] == '\0'));
}
(*ctx->matchlength)++;
}
while ((text[0] != '\0') && matchone(*pattern++, *text++, ctx->ccl_buf));
*ctx->matchlength = pre;
if (memo_ctx.memo && memo_ctx.entry_offset < memo_ctx.memo_stride)
RE_MEMO_SET(memo_ctx);
return 0;
}
#endif