#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include "comparator.h"
#include "tree.h"
#include "sieve.h"
#include "bytecode.h"
#include "xmalloc.h"
typedef int (*compare_t)(const void *, size_t, const void *);
static int rel_eq(const char *text, size_t tlen, const char *pat, void *rock)
{
compare_t compar = (compare_t) rock;
return (compar(text, tlen, pat) == 0);
}
static int rel_ne(const char *text, size_t tlen, const char *pat, void *rock)
{
compare_t compar = (compare_t) rock;
return (compar(text, tlen, pat) != 0);
}
static int rel_gt(const char *text, size_t tlen, const char *pat, void *rock)
{
compare_t compar = (compare_t) rock;
return (compar(text, tlen, pat) > 0);
}
static int rel_ge(const char *text, size_t tlen, const char *pat, void *rock)
{
compare_t compar = (compare_t) rock;
return (compar(text, tlen, pat) >= 0);
}
static int rel_lt(const char *text, size_t tlen, const char *pat, void *rock)
{
compare_t compar = (compare_t) rock;
return (compar(text, tlen, pat) < 0);
}
static int rel_le(const char *text, size_t tlen, const char *pat, void *rock)
{
compare_t compar = (compare_t) rock;
return (compar(text, tlen, pat) <= 0);
}
static int octet_cmp_(const char *text, size_t tlen,
const char *pat, int casemap)
{
size_t plen, sl, i;
int r = 0;
plen = strlen(pat);
sl = tlen < plen ? tlen : plen;
for (i = 0; !r && i < sl; i++) {
r = casemap ? toupper(text[i]) - toupper(pat[i]) : text[i] - pat[i];
}
if (r == 0)
return (tlen - plen);
else
return r;
}
static int octet_cmp(const char *text, size_t tlen, const char *pat)
{
return octet_cmp_(text, tlen, pat, 0);
}
#if 0
int boyer_moore(char *text, char *pat)
{
int i, j;
int M = strlen(pat);
int N = strlen(text);
int skip[256];
for (i = 0; i < 256; i++)
skip[i] = M;
for (i = 0; i < M; i++)
skip[(int) pat[i]] = M-i-1;
i = j = M-1;
do {
if (pat[j] == text[i]) {
i--;
j--;
} else {
if (M-j > skip[(int) text[i]]) {
i = i + M - j;
} else {
i = i + skip[(int) text[i]];
}
j = M-1;
}
} while (!((j < 0) || (i >= N)));
return (i < N) ? 1 : 0;
}
#endif
static int octet_contains_(const char *text, size_t tlen,
const char *pat, int casemap)
{
int N = tlen;
int M = strlen(pat);
int i, j;
i = 0, j = 0;
while ((j < M) && (i < N)) {
if ((text[i] == pat[j]) ||
(casemap && (toupper(text[i]) == toupper(pat[j])))) {
i++; j++;
} else {
i = i - j + 1;
j = 0;
}
}
return (j == M);
}
static int octet_contains(const char *text, size_t tlen, const char *pat,
void *rock __attribute__((unused)))
{
return octet_contains_(text, tlen, pat, 0);
}
static int octet_matches_(const char *text, size_t tlen,
const char *pat, int casemap)
{
const char *p;
const char *t;
char c;
t = text;
p = pat;
for (;;) {
if (*p == '\0') {
return (!tlen);
}
c = *p++;
switch (c) {
case '?':
if (!tlen) {
return 0;
}
t++; tlen--;
break;
case '*':
while (*p == '*' || *p == '?') {
if (*p == '?') {
if (!tlen) {
return 0;
}
t++; tlen--;
}
p++;
}
if (*p == '\0') {
return 1;
}
while (tlen) {
if (octet_matches_(t, tlen, p, casemap)) return 1;
t++; tlen--;
}
case '\\':
p++;
default:
if ((c == *t) || (casemap && (toupper(c) == toupper(*t)))) {
t++; tlen--;
} else {
return 0;
}
}
}
}
static int octet_matches(const char *text, size_t tlen, const char *pat,
void *rock __attribute__((unused)))
{
return octet_matches_(text, tlen, pat, 0);
}
#ifdef ENABLE_REGEX
static int octet_regex(const char *text, size_t tlen, const char *pat,
void *rock __attribute__((unused)))
{
if (!text[tlen]) {
return (!regexec((regex_t *) pat, text, 0, NULL, 0));
}
else {
char *buf = (char *) xstrndup(text, tlen);
int r = !regexec((regex_t *) pat, buf, 0, NULL, 0);
free(buf);
return r;
}
}
#endif
static int ascii_casemap_cmp(const char *text, size_t tlen, const char *pat)
{
return octet_cmp_(text, tlen, pat, 1);
}
static int ascii_casemap_contains(const char *text, size_t tlen,
const char *pat,
void *rock __attribute__((unused)))
{
return octet_contains_(text, tlen, pat, 1);
}
static int ascii_casemap_matches(const char *text, size_t tlen,
const char *pat,
void *rock __attribute__((unused)))
{
return octet_matches_(text, tlen, pat, 1);
}
static int ascii_numeric_cmp(const char *text, size_t tlen, const char *pat)
{
unsigned text_digit_len;
unsigned pat_digit_len;
if (isdigit((int) *pat)) {
if (isdigit((int) *text)) {
for (text_digit_len = 0;
tlen-- && isdigit((int) text[text_digit_len]);
text_digit_len++);
for (pat_digit_len = 0;
isdigit((int) pat[pat_digit_len]);
pat_digit_len++);
if (text_digit_len < pat_digit_len) {
while (pat_digit_len > text_digit_len) {
if ('0' < *pat) {
return (-1);
}
pat++;
pat_digit_len--;
}
} else if (text_digit_len > pat_digit_len) {
while (text_digit_len > pat_digit_len) {
if (*text > '0') {
return 1;
}
text++;
text_digit_len--;
}
}
while (text_digit_len > 0) {
if (*text < *pat) {
return -1;
} else if (*text > *pat) {
return 1;
}
text++;
pat++;
text_digit_len--;
}
return (0);
} else {
return 1;
}
} else if (isdigit((int) *text)) {
return -1;
} else {
return 0;
}
}
static comparator_t *lookup_rel(int relation)
{
comparator_t *ret;
ret = NULL;
switch (relation)
{
case B_EQ:
ret = &rel_eq;
break;
case B_NE:
ret = &rel_ne;
break;
case B_GT:
ret = &rel_gt;
break;
case B_GE:
ret = &rel_ge;
break;
case B_LT:
ret = &rel_lt;
break;
case B_LE:
ret = &rel_le;
}
return ret;
}
comparator_t *lookup_comp(int comp, int mode, int relation,
void **comprock)
{
comparator_t *ret;
ret = NULL;
*comprock = NULL;
#if VERBOSE
printf("comp%d mode%d relat%d \n", comp, mode, relation);
#endif
switch (comp)
{
case B_OCTET:
switch (mode) {
case B_IS:
ret = &rel_eq;
*comprock = (void **) &octet_cmp;
break;
case B_CONTAINS:
ret = &octet_contains;
break;
case B_MATCHES:
ret = &octet_matches;
break;
#ifdef ENABLE_REGEX
case B_REGEX:
ret = &octet_regex;
break;
#endif
case B_VALUE:
ret = lookup_rel(relation);
*comprock = (void **) &octet_cmp;
break;
}
break;
case B_ASCIICASEMAP:
switch (mode) {
case B_IS:
ret = &rel_eq;
*comprock = (void **) &ascii_casemap_cmp;
break;
case B_CONTAINS:
ret = &ascii_casemap_contains;
break;
case B_MATCHES:
ret = &ascii_casemap_matches;
break;
#ifdef ENABLE_REGEX
case B_REGEX:
ret = &octet_regex;
break;
#endif
case B_VALUE:
ret = lookup_rel(relation);
*comprock = (void **) &ascii_casemap_cmp;
break;
}
break;
case B_ASCIINUMERIC:
switch (mode) {
case B_IS:
ret = &rel_eq;
*comprock = (void **) &ascii_numeric_cmp;
break;
case B_COUNT:
case B_VALUE:
ret = lookup_rel(relation);
*comprock = (void **) &ascii_numeric_cmp;
break;
}
break;
}
return ret;
}