version 1.4, 2011/11/14 10:07:06 |
version 1.8, 2011/11/20 15:43:14 |
|
|
#include <fcntl.h> |
#include <fcntl.h> |
#include <regex.h> |
#include <regex.h> |
#include <stdarg.h> |
#include <stdarg.h> |
|
#include <stdint.h> |
#include <stdlib.h> |
#include <stdlib.h> |
#include <string.h> |
#include <string.h> |
|
#include <unistd.h> |
|
|
#ifdef __linux__ |
#ifdef __linux__ |
# include <db_185.h> |
# include <db_185.h> |
|
|
#include "apropos_db.h" |
#include "apropos_db.h" |
#include "mandoc.h" |
#include "mandoc.h" |
|
|
|
struct rec { |
|
struct res res; /* resulting record info */ |
|
/* |
|
* Maintain a binary tree for checking the uniqueness of `rec' |
|
* when adding elements to the results array. |
|
* Since the results array is dynamic, use offset in the array |
|
* instead of a pointer to the structure. |
|
*/ |
|
int lhs; |
|
int rhs; |
|
int matched; /* expression is true */ |
|
int *matches; /* partial truth evaluations */ |
|
}; |
|
|
struct expr { |
struct expr { |
int regex; |
int regex; /* is regex? */ |
int mask; |
int index; /* index in match array */ |
char *v; |
uint64_t mask; /* type-mask */ |
regex_t re; |
int cs; /* is case-sensitive? */ |
|
int and; /* is rhs of logical AND? */ |
|
char *v; /* search value */ |
|
regex_t re; /* compiled re, if regex */ |
|
struct expr *next; /* next in sequence */ |
|
struct expr *subexpr; |
}; |
}; |
|
|
struct type { |
struct type { |
int mask; |
uint64_t mask; |
const char *name; |
const char *name; |
}; |
}; |
|
|
|
struct rectree { |
|
struct rec *node; /* record array for dir tree */ |
|
int len; /* length of record array */ |
|
}; |
|
|
static const struct type types[] = { |
static const struct type types[] = { |
{ TYPE_An, "An" }, |
{ TYPE_An, "An" }, |
|
{ TYPE_Ar, "Ar" }, |
|
{ TYPE_At, "At" }, |
|
{ TYPE_Bsx, "Bsx" }, |
|
{ TYPE_Bx, "Bx" }, |
{ TYPE_Cd, "Cd" }, |
{ TYPE_Cd, "Cd" }, |
|
{ TYPE_Cm, "Cm" }, |
|
{ TYPE_Dv, "Dv" }, |
|
{ TYPE_Dx, "Dx" }, |
|
{ TYPE_Em, "Em" }, |
{ TYPE_Er, "Er" }, |
{ TYPE_Er, "Er" }, |
{ TYPE_Ev, "Ev" }, |
{ TYPE_Ev, "Ev" }, |
|
{ TYPE_Fa, "Fa" }, |
|
{ TYPE_Fl, "Fl" }, |
{ TYPE_Fn, "Fn" }, |
{ TYPE_Fn, "Fn" }, |
{ TYPE_Fn, "Fo" }, |
{ TYPE_Fn, "Fo" }, |
|
{ TYPE_Ft, "Ft" }, |
|
{ TYPE_Fx, "Fx" }, |
|
{ TYPE_Ic, "Ic" }, |
{ TYPE_In, "In" }, |
{ TYPE_In, "In" }, |
|
{ TYPE_Lb, "Lb" }, |
|
{ TYPE_Li, "Li" }, |
|
{ TYPE_Lk, "Lk" }, |
|
{ TYPE_Ms, "Ms" }, |
|
{ TYPE_Mt, "Mt" }, |
{ TYPE_Nd, "Nd" }, |
{ TYPE_Nd, "Nd" }, |
{ TYPE_Nm, "Nm" }, |
{ TYPE_Nm, "Nm" }, |
|
{ TYPE_Nx, "Nx" }, |
|
{ TYPE_Ox, "Ox" }, |
{ TYPE_Pa, "Pa" }, |
{ TYPE_Pa, "Pa" }, |
|
{ TYPE_Rs, "Rs" }, |
|
{ TYPE_Sh, "Sh" }, |
|
{ TYPE_Ss, "Ss" }, |
{ TYPE_St, "St" }, |
{ TYPE_St, "St" }, |
|
{ TYPE_Sy, "Sy" }, |
|
{ TYPE_Tn, "Tn" }, |
{ TYPE_Va, "Va" }, |
{ TYPE_Va, "Va" }, |
{ TYPE_Va, "Vt" }, |
{ TYPE_Va, "Vt" }, |
{ TYPE_Xr, "Xr" }, |
{ TYPE_Xr, "Xr" }, |
Line 64 static const struct type types[] = { |
|
Line 115 static const struct type types[] = { |
|
}; |
}; |
|
|
static DB *btree_open(void); |
static DB *btree_open(void); |
static int btree_read(const DBT *, const struct mchars *, char **); |
static int btree_read(const DBT *, |
static int exprexec(const struct expr *, char *, int); |
const struct mchars *, char **); |
|
static int expreval(const struct expr *, int *); |
|
static void exprexec(const struct expr *, |
|
const char *, uint64_t, struct rec *); |
|
static int exprmark(const struct expr *, |
|
const char *, uint64_t, int *); |
|
static struct expr *exprexpr(int, char *[], int *, int *, size_t *); |
|
static struct expr *exprterm(char *, int); |
static DB *index_open(void); |
static DB *index_open(void); |
static int index_read(const DBT *, const DBT *, |
static int index_read(const DBT *, const DBT *, |
const struct mchars *, struct rec *); |
const struct mchars *, struct rec *); |
static void norm_string(const char *, |
static void norm_string(const char *, |
const struct mchars *, char **); |
const struct mchars *, char **); |
static size_t norm_utf8(unsigned int, char[7]); |
static size_t norm_utf8(unsigned int, char[7]); |
|
static void recfree(struct rec *); |
|
static int single_search(struct rectree *, const struct opts *, |
|
const struct expr *, size_t terms, |
|
struct mchars *); |
|
|
/* |
/* |
* Open the keyword mandoc-db database. |
* Open the keyword mandoc-db database. |
Line 103 btree_read(const DBT *v, const struct mchars *mc, char |
|
Line 165 btree_read(const DBT *v, const struct mchars *mc, char |
|
/* Sanity: are we nil-terminated? */ |
/* Sanity: are we nil-terminated? */ |
|
|
assert(v->size > 0); |
assert(v->size > 0); |
|
|
if ('\0' != ((char *)v->data)[(int)v->size - 1]) |
if ('\0' != ((char *)v->data)[(int)v->size - 1]) |
return(0); |
return(0); |
|
|
Line 300 index_read(const DBT *key, const DBT *val, |
|
Line 363 index_read(const DBT *key, const DBT *val, |
|
left = val->size; |
left = val->size; |
cp = (char *)val->data; |
cp = (char *)val->data; |
|
|
rec->rec = *(recno_t *)key->data; |
rec->res.rec = *(recno_t *)key->data; |
|
|
INDEX_BREAD(rec->file); |
INDEX_BREAD(rec->res.file); |
INDEX_BREAD(rec->cat); |
INDEX_BREAD(rec->res.cat); |
INDEX_BREAD(rec->title); |
INDEX_BREAD(rec->res.title); |
INDEX_BREAD(rec->arch); |
INDEX_BREAD(rec->res.arch); |
INDEX_BREAD(rec->desc); |
INDEX_BREAD(rec->res.desc); |
return(1); |
return(1); |
} |
} |
|
|
/* |
/* |
* Search the mandocdb database for the expression "expr". |
* Search mandocdb databases in argv (size argc) for the expression |
|
* "expr". |
* Filter out by "opts". |
* Filter out by "opts". |
* Call "res" with the results, which may be zero. |
* Call "res" with the results, which may be zero. |
|
* Return 0 if there was a database error, else return 1. |
*/ |
*/ |
void |
int |
apropos_search(const struct opts *opts, const struct expr *expr, |
apropos_search(int argc, char *argv[], const struct opts *opts, |
void *arg, void (*res)(struct rec *, size_t, void *)) |
const struct expr *expr, size_t terms, void *arg, |
|
void (*res)(struct res *, size_t, void *)) |
{ |
{ |
int i, len, root, leaf; |
struct rectree tree; |
|
struct mchars *mc; |
|
struct res *ress; |
|
int i, mlen, rc; |
|
|
|
memset(&tree, 0, sizeof(struct rectree)); |
|
|
|
mc = mchars_alloc(); |
|
|
|
for (rc = 1, i = 0; rc && i < argc; i++) { |
|
/* FIXME: ugly warning: we shouldn't get here! */ |
|
if (chdir(argv[i])) |
|
continue; |
|
rc = single_search(&tree, opts, expr, terms, mc); |
|
/* FIXME: warn and continue... ? */ |
|
} |
|
|
|
/* |
|
* Count the matching files |
|
* and feed them to the output handler. |
|
*/ |
|
|
|
for (mlen = i = 0; i < tree.len; i++) |
|
if (tree.node[i].matched) |
|
mlen++; |
|
|
|
ress = mandoc_malloc(mlen * sizeof(struct res)); |
|
|
|
for (mlen = i = 0; i < tree.len; i++) |
|
if (tree.node[i].matched) |
|
memcpy(&ress[mlen++], &tree.node[i].res, |
|
sizeof(struct res)); |
|
|
|
(*res)(ress, mlen, arg); |
|
free(ress); |
|
|
|
for (i = 0; i < tree.len; i++) |
|
recfree(&tree.node[i]); |
|
|
|
free(tree.node); |
|
mchars_free(mc); |
|
return(rc); |
|
} |
|
|
|
static int |
|
single_search(struct rectree *tree, const struct opts *opts, |
|
const struct expr *expr, size_t terms, |
|
struct mchars *mc) |
|
{ |
|
int root, leaf, ch; |
|
uint64_t mask; |
DBT key, val; |
DBT key, val; |
DB *btree, *idx; |
DB *btree, *idx; |
struct mchars *mc; |
|
int ch; |
|
char *buf; |
char *buf; |
recno_t rec; |
recno_t rec; |
struct rec *recs; |
struct rec *rs; |
struct rec srec; |
struct rec r; |
|
struct db_val *vbuf; |
|
|
root = -1; |
root = -1; |
leaf = -1; |
leaf = -1; |
btree = NULL; |
btree = NULL; |
idx = NULL; |
idx = NULL; |
mc = NULL; |
|
buf = NULL; |
buf = NULL; |
recs = NULL; |
rs = tree->node; |
len = 0; |
|
|
|
memset(&srec, 0, sizeof(struct rec)); |
memset(&r, 0, sizeof(struct rec)); |
|
|
/* XXX: error out with bad regexp? */ |
|
|
|
mc = mchars_alloc(); |
|
|
|
/* XXX: return fact that we've errored? */ |
|
|
|
if (NULL == (btree = btree_open())) |
if (NULL == (btree = btree_open())) |
goto out; |
return(0); |
if (NULL == (idx = index_open())) |
|
goto out; |
|
|
|
|
if (NULL == (idx = index_open())) { |
|
(*btree->close)(btree); |
|
return(0); |
|
} |
|
|
while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) { |
while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) { |
/* |
if (key.size < 2 || sizeof(struct db_val) != val.size) |
* Low-water mark for key and value. |
break; |
* The key must have something in it, and the value must |
|
* have the correct tags/recno mix. |
|
*/ |
|
if (key.size < 2 || 8 != val.size) |
|
break; |
|
if ( ! btree_read(&key, mc, &buf)) |
if ( ! btree_read(&key, mc, &buf)) |
break; |
break; |
|
|
if ( ! exprexec(expr, buf, *(int *)val.data)) |
vbuf = val.data; |
|
rec = vbuf->rec; |
|
mask = vbuf->mask; |
|
|
|
/* |
|
* See if this keyword record matches any of the |
|
* expressions we have stored. |
|
*/ |
|
if ( ! exprmark(expr, buf, mask, NULL)) |
continue; |
continue; |
|
|
memcpy(&rec, val.data + 4, sizeof(recno_t)); |
|
|
|
/* |
/* |
* O(log n) scan for prior records. Since a record |
* O(log n) scan for prior records. Since a record |
* number is unbounded, this has decent performance over |
* number is unbounded, this has decent performance over |
Line 374 apropos_search(const struct opts *opts, const struct e |
|
Line 485 apropos_search(const struct opts *opts, const struct e |
|
*/ |
*/ |
|
|
for (leaf = root; leaf >= 0; ) |
for (leaf = root; leaf >= 0; ) |
if (rec > recs[leaf].rec && recs[leaf].rhs >= 0) |
if (rec > rs[leaf].res.rec && |
leaf = recs[leaf].rhs; |
rs[leaf].rhs >= 0) |
else if (rec < recs[leaf].rec && recs[leaf].lhs >= 0) |
leaf = rs[leaf].rhs; |
leaf = recs[leaf].lhs; |
else if (rec < rs[leaf].res.rec && |
|
rs[leaf].lhs >= 0) |
|
leaf = rs[leaf].lhs; |
else |
else |
break; |
break; |
|
|
if (leaf >= 0 && recs[leaf].rec == rec) |
/* |
|
* If we find a record, see if it has already evaluated |
|
* to true. If it has, great, just keep going. If not, |
|
* try to evaluate it now and continue anyway. |
|
*/ |
|
|
|
if (leaf >= 0 && rs[leaf].res.rec == rec) { |
|
if (0 == rs[leaf].matched) |
|
exprexec(expr, buf, mask, &rs[leaf]); |
continue; |
continue; |
|
} |
|
|
/* |
/* |
* Now we actually extract the manpage's metadata from |
* We have a new file to examine. |
* the index database. |
* Extract the manpage's metadata from the index |
|
* database, then begin partial evaluation. |
*/ |
*/ |
|
|
key.data = &rec; |
key.data = &rec; |
Line 395 apropos_search(const struct opts *opts, const struct e |
|
Line 518 apropos_search(const struct opts *opts, const struct e |
|
if (0 != (*idx->get)(idx, &key, &val, 0)) |
if (0 != (*idx->get)(idx, &key, &val, 0)) |
break; |
break; |
|
|
srec.lhs = srec.rhs = -1; |
r.lhs = r.rhs = -1; |
if ( ! index_read(&key, &val, mc, &srec)) |
if ( ! index_read(&key, &val, mc, &r)) |
break; |
break; |
|
|
if (opts->cat && strcasecmp(opts->cat, srec.cat)) |
/* XXX: this should be elsewhere, I guess? */ |
|
|
|
if (opts->cat && strcasecmp(opts->cat, r.res.cat)) |
continue; |
continue; |
if (opts->arch && strcasecmp(opts->arch, srec.arch)) |
if (opts->arch && strcasecmp(opts->arch, r.res.arch)) |
continue; |
continue; |
|
|
recs = mandoc_realloc |
tree->node = rs = mandoc_realloc |
(recs, (len + 1) * sizeof(struct rec)); |
(rs, (tree->len + 1) * sizeof(struct rec)); |
|
|
memcpy(&recs[len], &srec, sizeof(struct rec)); |
memcpy(&rs[tree->len], &r, sizeof(struct rec)); |
|
rs[tree->len].matches = |
|
mandoc_calloc(terms, sizeof(int)); |
|
|
|
exprexec(expr, buf, mask, &rs[tree->len]); |
/* Append to our tree. */ |
/* Append to our tree. */ |
|
|
if (leaf >= 0) { |
if (leaf >= 0) { |
if (rec > recs[leaf].rec) |
if (rec > rs[leaf].res.rec) |
recs[leaf].rhs = len; |
rs[leaf].rhs = tree->len; |
else |
else |
recs[leaf].lhs = len; |
rs[leaf].lhs = tree->len; |
} else |
} else |
root = len; |
root = tree->len; |
|
|
memset(&srec, 0, sizeof(struct rec)); |
memset(&r, 0, sizeof(struct rec)); |
len++; |
tree->len++; |
} |
} |
|
|
|
(*btree->close)(btree); |
|
(*idx->close)(idx); |
|
|
if (1 == ch) |
free(buf); |
(*res)(recs, len, arg); |
return(1 == ch); |
|
} |
|
|
/* XXX: else? corrupt database error? */ |
static void |
out: |
recfree(struct rec *rec) |
for (i = 0; i < len; i++) { |
{ |
free(recs[i].file); |
|
free(recs[i].cat); |
|
free(recs[i].title); |
|
free(recs[i].arch); |
|
free(recs[i].desc); |
|
} |
|
|
|
free(srec.file); |
free(rec->res.file); |
free(srec.cat); |
free(rec->res.cat); |
free(srec.title); |
free(rec->res.title); |
free(srec.arch); |
free(rec->res.arch); |
free(srec.desc); |
free(rec->res.desc); |
|
|
if (mc) |
free(rec->matches); |
mchars_free(mc); |
|
if (btree) |
|
(*btree->close)(btree); |
|
if (idx) |
|
(*idx->close)(idx); |
|
|
|
free(buf); |
|
free(recs); |
|
} |
} |
|
|
struct expr * |
struct expr * |
exprcomp(char *buf) |
exprcomp(int argc, char *argv[], size_t *tt) |
{ |
{ |
struct expr *p; |
int pos, lvl; |
|
struct expr *e; |
|
|
|
pos = lvl = 0; |
|
*tt = 0; |
|
|
|
e = exprexpr(argc, argv, &pos, &lvl, tt); |
|
|
|
if (0 == lvl && pos >= argc) |
|
return(e); |
|
|
|
exprfree(e); |
|
return(NULL); |
|
} |
|
|
|
/* |
|
* Compile an array of tokens into an expression. |
|
* An informal expression grammar is defined in apropos(1). |
|
* Return NULL if we fail doing so. All memory will be cleaned up. |
|
* Return the root of the expression sequence if alright. |
|
*/ |
|
static struct expr * |
|
exprexpr(int argc, char **argv, int *pos, int *lvl, size_t *tt) |
|
{ |
|
struct expr *e, *first, *next; |
|
int log; |
|
|
|
first = next = NULL; |
|
|
|
for ( ; *pos < argc; (*pos)++) { |
|
e = next; |
|
|
|
/* |
|
* Close out a subexpression. |
|
*/ |
|
|
|
if (NULL != e && 0 == strcmp(")", argv[*pos])) { |
|
if (--(*lvl) < 0) |
|
goto err; |
|
break; |
|
} |
|
|
|
/* |
|
* Small note: if we're just starting, don't let "-a" |
|
* and "-o" be considered logical operators: they're |
|
* just tokens unless pairwise joining, in which case we |
|
* record their existence (or assume "OR"). |
|
*/ |
|
log = 0; |
|
|
|
if (NULL != e && 0 == strcmp("-a", argv[*pos])) |
|
log = 1; |
|
else if (NULL != e && 0 == strcmp("-o", argv[*pos])) |
|
log = 2; |
|
|
|
if (log > 0 && ++(*pos) >= argc) |
|
goto err; |
|
|
|
/* |
|
* Now we parse the term part. This can begin with |
|
* "-i", in which case the expression is case |
|
* insensitive. |
|
*/ |
|
|
|
if (0 == strcmp("(", argv[*pos])) { |
|
++(*pos); |
|
++(*lvl); |
|
next = mandoc_calloc(1, sizeof(struct expr)); |
|
next->cs = 1; |
|
next->subexpr = exprexpr(argc, argv, pos, lvl, tt); |
|
if (NULL == next->subexpr) { |
|
free(next); |
|
next = NULL; |
|
} |
|
} else if (0 == strcmp("-i", argv[*pos])) { |
|
if (++(*pos) >= argc) |
|
goto err; |
|
next = exprterm(argv[*pos], 0); |
|
} else |
|
next = exprterm(argv[*pos], 1); |
|
|
|
if (NULL == next) |
|
goto err; |
|
|
|
next->and = log == 1; |
|
next->index = (int)(*tt)++; |
|
|
|
/* Append to our chain of expressions. */ |
|
|
|
if (NULL == first) { |
|
assert(NULL == e); |
|
first = next; |
|
} else { |
|
assert(NULL != e); |
|
e->next = next; |
|
} |
|
} |
|
|
|
return(first); |
|
err: |
|
exprfree(first); |
|
return(NULL); |
|
} |
|
|
|
/* |
|
* Parse a terminal expression with the grammar as defined in |
|
* apropos(1). |
|
* Return NULL if we fail the parse. |
|
*/ |
|
static struct expr * |
|
exprterm(char *buf, int cs) |
|
{ |
struct expr e; |
struct expr e; |
|
struct expr *p; |
char *key; |
char *key; |
int i, icase; |
int i; |
|
|
if ('\0' == *buf) |
memset(&e, 0, sizeof(struct expr)); |
return(NULL); |
|
|
|
/* |
e.cs = cs; |
* Choose regex or substring match. |
|
*/ |
|
|
|
|
/* Choose regex or substring match. */ |
|
|
if (NULL == (e.v = strpbrk(buf, "=~"))) { |
if (NULL == (e.v = strpbrk(buf, "=~"))) { |
e.regex = 0; |
e.regex = 0; |
e.v = buf; |
e.v = buf; |
Line 476 exprcomp(char *buf) |
|
Line 705 exprcomp(char *buf) |
|
*e.v++ = '\0'; |
*e.v++ = '\0'; |
} |
} |
|
|
/* |
/* Determine the record types to search for. */ |
* Determine the record types to search for. |
|
*/ |
|
|
|
icase = 0; |
|
e.mask = 0; |
e.mask = 0; |
if (buf < e.v) { |
if (buf < e.v) { |
while (NULL != (key = strsep(&buf, ","))) { |
while (NULL != (key = strsep(&buf, ","))) { |
if ('i' == key[0] && '\0' == key[1]) { |
|
icase = REG_ICASE; |
|
continue; |
|
} |
|
i = 0; |
i = 0; |
while (types[i].mask && |
while (types[i].mask && |
strcmp(types[i].name, key)) |
strcmp(types[i].name, key)) |
Line 498 exprcomp(char *buf) |
|
Line 720 exprcomp(char *buf) |
|
if (0 == e.mask) |
if (0 == e.mask) |
e.mask = TYPE_Nm | TYPE_Nd; |
e.mask = TYPE_Nm | TYPE_Nd; |
|
|
if (e.regex && |
if (e.regex) { |
regcomp(&e.re, e.v, REG_EXTENDED | REG_NOSUB | icase)) |
i = REG_EXTENDED | REG_NOSUB | cs ? 0 : REG_ICASE; |
return(NULL); |
if (regcomp(&e.re, e.v, i)) |
|
return(NULL); |
|
} |
|
|
e.v = mandoc_strdup(e.v); |
e.v = mandoc_strdup(e.v); |
|
|
Line 512 exprcomp(char *buf) |
|
Line 736 exprcomp(char *buf) |
|
void |
void |
exprfree(struct expr *p) |
exprfree(struct expr *p) |
{ |
{ |
|
struct expr *pp; |
|
|
|
while (NULL != p) { |
|
if (p->subexpr) |
|
exprfree(p->subexpr); |
|
if (p->regex) |
|
regfree(&p->re); |
|
free(p->v); |
|
pp = p->next; |
|
free(p); |
|
p = pp; |
|
} |
|
} |
|
|
if (NULL == p) |
static int |
return; |
exprmark(const struct expr *p, const char *cp, |
|
uint64_t mask, int *ms) |
|
{ |
|
|
if (p->regex) |
for ( ; p; p = p->next) { |
regfree(&p->re); |
if (p->subexpr) { |
|
if (exprmark(p->subexpr, cp, mask, ms)) |
|
return(1); |
|
continue; |
|
} else if ( ! (mask & p->mask)) |
|
continue; |
|
|
free(p->v); |
if (p->regex) { |
free(p); |
if (regexec(&p->re, cp, 0, NULL, 0)) |
|
continue; |
|
} else if (p->cs) { |
|
if (NULL == strstr(cp, p->v)) |
|
continue; |
|
} else { |
|
if (NULL == strcasestr(cp, p->v)) |
|
continue; |
|
} |
|
|
|
if (NULL == ms) |
|
return(1); |
|
else |
|
ms[p->index] = 1; |
|
} |
|
|
|
return(0); |
} |
} |
|
|
static int |
static int |
exprexec(const struct expr *p, char *cp, int mask) |
expreval(const struct expr *p, int *ms) |
{ |
{ |
|
int match; |
|
|
if ( ! (mask & p->mask)) |
/* |
return(0); |
* AND has precedence over OR. Analysis is left-right, though |
|
* it doesn't matter because there are no side-effects. |
|
* Thus, step through pairwise ANDs and accumulate their Boolean |
|
* evaluation. If we encounter a single true AND collection or |
|
* standalone term, the whole expression is true (by definition |
|
* of OR). |
|
*/ |
|
|
if (p->regex) |
for (match = 0; p && ! match; p = p->next) { |
return(0 == regexec(&p->re, cp, 0, NULL, 0)); |
/* Evaluate a subexpression, if applicable. */ |
else |
if (p->subexpr && ! ms[p->index]) |
return(NULL != strcasestr(cp, p->v)); |
ms[p->index] = expreval(p->subexpr, ms); |
|
|
|
match = ms[p->index]; |
|
for ( ; p->next && p->next->and; p = p->next) { |
|
/* Evaluate a subexpression, if applicable. */ |
|
if (p->next->subexpr && ! ms[p->next->index]) |
|
ms[p->next->index] = |
|
expreval(p->next->subexpr, ms); |
|
match = match && ms[p->next->index]; |
|
} |
|
} |
|
|
|
return(match); |
|
} |
|
|
|
/* |
|
* First, update the array of terms for which this expression evaluates |
|
* to true. |
|
* Second, logically evaluate all terms over the updated array of truth |
|
* values. |
|
* If this evaluates to true, mark the expression as satisfied. |
|
*/ |
|
static void |
|
exprexec(const struct expr *p, const char *cp, |
|
uint64_t mask, struct rec *r) |
|
{ |
|
|
|
assert(0 == r->matched); |
|
exprmark(p, cp, mask, r->matches); |
|
r->matched = expreval(p, r->matches); |
} |
} |