=================================================================== RCS file: /cvs/mandoc/Attic/apropos_db.c,v retrieving revision 1.7 retrieving revision 1.32 diff -u -p -r1.7 -r1.32 --- mandoc/Attic/apropos_db.c 2011/11/20 12:46:53 1.7 +++ mandoc/Attic/apropos_db.c 2012/03/25 00:48:47 1.32 @@ -1,6 +1,6 @@ -/* $Id: apropos_db.c,v 1.7 2011/11/20 12:46:53 kristaps Exp $ */ +/* $Id: apropos_db.c,v 1.32 2012/03/25 00:48:47 kristaps Exp $ */ /* - * Copyright (c) 2011 Kristaps Dzonsons + * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2011 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any @@ -15,6 +15,12 @@ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + #include #include #include @@ -22,9 +28,14 @@ #include #include #include +#include -#ifdef __linux__ +#if defined(__linux__) +# include # include +#elif defined(__APPLE__) +# include +# include #else # include #endif @@ -33,25 +44,20 @@ #include "apropos_db.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 */ -}; +#define RESFREE(_x) \ + do { \ + free((_x)->file); \ + free((_x)->cat); \ + free((_x)->title); \ + free((_x)->arch); \ + free((_x)->desc); \ + free((_x)->matches); \ + } while (/*CONSTCOND*/0) struct expr { int regex; /* is regex? */ int index; /* index in match array */ uint64_t mask; /* type-mask */ - int cs; /* is case-sensitive? */ int and; /* is rhs of logical AND? */ char *v; /* search value */ regex_t re; /* compiled re, if regex */ @@ -64,6 +70,11 @@ struct type { const char *name; }; +struct rectree { + struct res *node; /* record array for dir tree */ + int len; /* length of record array */ +}; + static const struct type types[] = { { TYPE_An, "An" }, { TYPE_Ar, "Ar" }, @@ -104,27 +115,30 @@ static const struct type types[] = { { TYPE_Va, "Va" }, { TYPE_Va, "Vt" }, { TYPE_Xr, "Xr" }, - { INT_MAX, "any" }, + { UINT64_MAX, "any" }, { 0, NULL } }; static DB *btree_open(void); -static int btree_read(const DBT *, - const struct mchars *, char **); +static int btree_read(const DBT *, const DBT *, + const struct mchars *, + uint64_t *, recno_t *, 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 *, +static void exprexec(const struct expr *, + const char *, uint64_t, struct res *); +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 int index_read(const DBT *, const DBT *, - const struct mchars *, struct rec *); +static int index_read(const DBT *, const DBT *, int, + const struct mchars *, struct res *); static void norm_string(const char *, const struct mchars *, char **); 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 *, int); /* * Open the keyword mandoc-db database. @@ -136,10 +150,11 @@ btree_open(void) DB *db; memset(&info, 0, sizeof(BTREEINFO)); + info.lorder = 4321; info.flags = R_DUP; db = dbopen(MANDOC_DB, O_RDONLY, 0, DB_BTREE, &info); - if (NULL != db) + if (NULL != db) return(db); return(NULL); @@ -150,30 +165,36 @@ btree_open(void) * Return 0 if the database is insane, else 1. */ static int -btree_read(const DBT *v, const struct mchars *mc, char **buf) +btree_read(const DBT *k, const DBT *v, const struct mchars *mc, + uint64_t *mask, recno_t *rec, char **buf) { + uint64_t vbuf[2]; - /* Sanity: are we nil-terminated? */ + /* Are our sizes sane? */ + if (k->size < 2 || sizeof(vbuf) != v->size) + return(0); - assert(v->size > 0); - - if ('\0' != ((char *)v->data)[(int)v->size - 1]) + /* Is our string nil-terminated? */ + if ('\0' != ((const char *)k->data)[(int)k->size - 1]) return(0); - norm_string((char *)v->data, mc, buf); + norm_string((const char *)k->data, mc, buf); + memcpy(vbuf, v->data, v->size); + *mask = betoh64(vbuf[0]); + *rec = betoh64(vbuf[1]); return(1); } /* * Take a Unicode codepoint and produce its UTF-8 encoding. * This isn't the best way to do this, but it works. - * The magic numbers are from the UTF-8 packaging. + * The magic numbers are from the UTF-8 packaging. * They're not as scary as they seem: read the UTF-8 spec for details. */ static size_t norm_utf8(unsigned int cp, char out[7]) { - size_t rc; + int rc; rc = 0; @@ -214,7 +235,7 @@ norm_utf8(unsigned int cp, char out[7]) return(0); out[rc] = '\0'; - return(rc); + return((size_t)rc); } /* @@ -232,7 +253,7 @@ norm_string(const char *val, const struct mchars *mc, const char *seq, *cpp; int len, u, pos; enum mandoc_esc esc; - static const char res[] = { '\\', '\t', + static const char res[] = { '\\', '\t', ASCII_NBRSP, ASCII_HYPH, '\0' }; /* Pre-allocate by the length of the input */ @@ -278,7 +299,7 @@ norm_string(const char *val, const struct mchars *mc, if (ESCAPE_ERROR == esc) break; - /* + /* * XXX - this just does UTF-8, but we need to know * beforehand whether we should do text substitution. */ @@ -336,11 +357,12 @@ index_open(void) * Returns 1 if an entry was unpacked, 0 if the database is insane. */ static int -index_read(const DBT *key, const DBT *val, - const struct mchars *mc, struct rec *rec) +index_read(const DBT *key, const DBT *val, int index, + const struct mchars *mc, struct res *rec) { size_t left; char *np, *cp; + char type; #define INDEX_BREAD(_dst) \ do { \ @@ -351,71 +373,114 @@ index_read(const DBT *key, const DBT *val, cp = np + 1; \ } while (/* CONSTCOND */ 0) - left = val->size; - cp = (char *)val->data; + if (0 == (left = val->size)) + return(0); - rec->res.rec = *(recno_t *)key->data; + cp = val->data; + assert(sizeof(recno_t) == key->size); + memcpy(&rec->rec, key->data, key->size); + rec->volume = index; - INDEX_BREAD(rec->res.file); - INDEX_BREAD(rec->res.cat); - INDEX_BREAD(rec->res.title); - INDEX_BREAD(rec->res.arch); - INDEX_BREAD(rec->res.desc); + if ('d' == (type = *cp++)) + rec->type = RESTYPE_MDOC; + else if ('a' == type) + rec->type = RESTYPE_MAN; + else if ('c' == type) + rec->type = RESTYPE_CAT; + else + return(0); + + left--; + INDEX_BREAD(rec->file); + INDEX_BREAD(rec->cat); + INDEX_BREAD(rec->title); + INDEX_BREAD(rec->arch); + INDEX_BREAD(rec->desc); return(1); } /* - * Search the mandocdb database for the expression "expr". + * Search mandocdb databases in paths for expression "expr". * Filter out by "opts". * Call "res" with the results, which may be zero. * Return 0 if there was a database error, else return 1. */ int -apropos_search(const struct opts *opts, const struct expr *expr, - size_t terms, void *arg, +apropos_search(int pathsz, char **paths, const struct opts *opts, + const struct expr *expr, size_t terms, void *arg, + size_t *sz, struct res **resp, void (*res)(struct res *, size_t, void *)) { - int i, rsz, root, leaf, mlen, rc, ch; - uint64_t mask; + struct rectree tree; + struct mchars *mc; + int i, rc; + + memset(&tree, 0, sizeof(struct rectree)); + + rc = 0; + mc = mchars_alloc(); + *sz = 0; + *resp = NULL; + + /* + * Main loop. Change into the directory containing manpage + * databases. Run our expession over each database in the set. + */ + + for (i = 0; i < pathsz; i++) { + assert('/' == paths[i][0]); + if (chdir(paths[i])) + continue; + if (single_search(&tree, opts, expr, terms, mc, i)) + continue; + + resfree(tree.node, tree.len); + mchars_free(mc); + return(0); + } + + (*res)(tree.node, tree.len, arg); + *sz = tree.len; + *resp = tree.node; + mchars_free(mc); + return(1); +} + +static int +single_search(struct rectree *tree, const struct opts *opts, + const struct expr *expr, size_t terms, + struct mchars *mc, int vol) +{ + int root, leaf, ch; DBT key, val; DB *btree, *idx; - struct mchars *mc; char *buf; + struct res *rs; + struct res r; + uint64_t mask; recno_t rec; - struct rec *rs; - struct res *ress; - struct rec r; - struct db_val *vbuf; - rc = 0; root = -1; leaf = -1; btree = NULL; idx = NULL; - mc = NULL; buf = NULL; - rs = NULL; - rsz = 0; + rs = tree->node; - memset(&r, 0, sizeof(struct rec)); + memset(&r, 0, sizeof(struct res)); - mc = mchars_alloc(); + if (NULL == (btree = btree_open())) + return(1); - if (NULL == (btree = btree_open())) - goto out; - if (NULL == (idx = index_open())) - goto out; + if (NULL == (idx = index_open())) { + (*btree->close)(btree); + return(1); + } while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) { - if (key.size < 2 || sizeof(struct db_val) != val.size) - break; - if ( ! btree_read(&key, mc, &buf)) + if ( ! btree_read(&key, &val, mc, &mask, &rec, &buf)) break; - vbuf = val.data; - rec = vbuf->rec; - mask = vbuf->mask; - /* * See if this keyword record matches any of the * expressions we have stored. @@ -430,13 +495,13 @@ apropos_search(const struct opts *opts, const struct e */ for (leaf = root; leaf >= 0; ) - if (rec > rs[leaf].res.rec && + if (rec > rs[leaf].rec && rs[leaf].rhs >= 0) leaf = rs[leaf].rhs; - else if (rec < rs[leaf].res.rec && + else if (rec < rs[leaf].rec && rs[leaf].lhs >= 0) leaf = rs[leaf].lhs; - else + else break; /* @@ -445,7 +510,7 @@ apropos_search(const struct opts *opts, const struct e * try to evaluate it now and continue anyway. */ - if (leaf >= 0 && rs[leaf].res.rec == rec) { + if (leaf >= 0 && rs[leaf].rec == rec) { if (0 == rs[leaf].matched) exprexec(expr, buf, mask, &rs[leaf]); continue; @@ -464,87 +529,102 @@ apropos_search(const struct opts *opts, const struct e break; r.lhs = r.rhs = -1; - if ( ! index_read(&key, &val, mc, &r)) + if ( ! index_read(&key, &val, vol, mc, &r)) break; /* XXX: this should be elsewhere, I guess? */ - if (opts->cat && strcasecmp(opts->cat, r.res.cat)) + if (opts->cat && strcasecmp(opts->cat, r.cat)) continue; - if (opts->arch && strcasecmp(opts->arch, r.res.arch)) - continue; - rs = mandoc_realloc - (rs, (rsz + 1) * sizeof(struct rec)); + if (opts->arch && *r.arch) + if (strcasecmp(opts->arch, r.arch)) + continue; - memcpy(&rs[rsz], &r, sizeof(struct rec)); - rs[rsz].matches = mandoc_calloc(terms, sizeof(int)); + tree->node = rs = mandoc_realloc + (rs, (tree->len + 1) * sizeof(struct res)); - exprexec(expr, buf, mask, &rs[rsz]); + memcpy(&rs[tree->len], &r, sizeof(struct res)); + memset(&r, 0, sizeof(struct res)); + rs[tree->len].matches = + mandoc_calloc(terms, sizeof(int)); + + exprexec(expr, buf, mask, &rs[tree->len]); + /* Append to our tree. */ if (leaf >= 0) { - if (rec > rs[leaf].res.rec) - rs[leaf].rhs = rsz; + if (rec > rs[leaf].rec) + rs[leaf].rhs = tree->len; else - rs[leaf].lhs = rsz; + rs[leaf].lhs = tree->len; } else - root = rsz; - - memset(&r, 0, sizeof(struct rec)); - rsz++; - } - - /* - * If we haven't encountered any database errors, then construct - * an array of results and push them to the caller. - */ + root = tree->len; - if (1 == ch) { - for (mlen = i = 0; i < rsz; i++) - if (rs[i].matched) - mlen++; - ress = mandoc_malloc(mlen * sizeof(struct res)); - for (mlen = i = 0; i < rsz; i++) - if (rs[i].matched) - memcpy(&ress[mlen++], &rs[i].res, - sizeof(struct res)); - (*res)(ress, mlen, arg); - free(ress); - rc = 1; + tree->len++; } -out: - for (i = 0; i < rsz; i++) - recfree(&rs[i]); + (*btree->close)(btree); + (*idx->close)(idx); - recfree(&r); + free(buf); + RESFREE(&r); + return(1 == ch); +} - if (mc) - mchars_free(mc); - if (btree) - (*btree->close)(btree); - if (idx) - (*idx->close)(idx); +void +resfree(struct res *rec, size_t sz) +{ + size_t i; - free(buf); - free(rs); - return(rc); + for (i = 0; i < sz; i++) + RESFREE(&rec[i]); + free(rec); } -static void -recfree(struct rec *rec) +/* + * Compile a list of straight-up terms. + * The arguments are re-written into ~[[:<:]]term[[:>:]], or "term" + * surrounded by word boundaries, then pumped through exprterm(). + * Terms are case-insensitive. + * This emulates whatis(1) behaviour. + */ +struct expr * +termcomp(int argc, char *argv[], size_t *tt) { + char *buf; + int pos; + struct expr *e, *next; + size_t sz; - free(rec->res.file); - free(rec->res.cat); - free(rec->res.title); - free(rec->res.arch); - free(rec->res.desc); + buf = NULL; + e = NULL; + *tt = 0; - free(rec->matches); + for (pos = argc - 1; pos >= 0; pos--) { + sz = strlen(argv[pos]) + 18; + buf = mandoc_realloc(buf, sz); + strlcpy(buf, "Nm~[[:<:]]", sz); + strlcat(buf, argv[pos], sz); + strlcat(buf, "[[:>:]]", sz); + if (NULL == (next = exprterm(buf, 0))) { + free(buf); + exprfree(e); + return(NULL); + } + next->next = e; + e = next; + (*tt)++; + } + + free(buf); + return(e); } +/* + * Compile a sequence of logical expressions. + * See apropos.1 for a grammar of this sequence. + */ struct expr * exprcomp(int argc, char *argv[], size_t *tt) { @@ -599,7 +679,7 @@ exprexpr(int argc, char *argv[], int *pos, int *lvl, s log = 0; if (NULL != e && 0 == strcmp("-a", argv[*pos])) - log = 1; + log = 1; else if (NULL != e && 0 == strcmp("-o", argv[*pos])) log = 2; @@ -616,7 +696,6 @@ exprexpr(int argc, char *argv[], int *pos, int *lvl, s ++(*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); @@ -667,8 +746,6 @@ exprterm(char *buf, int cs) memset(&e, 0, sizeof(struct expr)); - e.cs = cs; - /* Choose regex or substring match. */ if (NULL == (e.v = strpbrk(buf, "=~"))) { @@ -695,7 +772,7 @@ exprterm(char *buf, int cs) e.mask = TYPE_Nm | TYPE_Nd; if (e.regex) { - i = REG_EXTENDED | REG_NOSUB | cs ? 0 : REG_ICASE; + i = REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE); if (regcomp(&e.re, e.v, i)) return(NULL); } @@ -711,7 +788,7 @@ void exprfree(struct expr *p) { struct expr *pp; - + while (NULL != p) { if (p->subexpr) exprfree(p->subexpr); @@ -725,7 +802,7 @@ exprfree(struct expr *p) } static int -exprmark(const struct expr *p, const char *cp, +exprmark(const struct expr *p, const char *cp, uint64_t mask, int *ms) { @@ -740,13 +817,8 @@ exprmark(const struct expr *p, const char *cp, if (p->regex) { 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; - } + } else if (NULL == strcasestr(cp, p->v)) + continue; if (NULL == ms) return(1); @@ -780,7 +852,7 @@ expreval(const struct expr *p, int *ms) 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] = + ms[p->next->index] = expreval(p->next->subexpr, ms); match = match && ms[p->next->index]; } @@ -797,11 +869,11 @@ expreval(const struct expr *p, int *ms) * 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) +exprexec(const struct expr *e, const char *cp, + uint64_t mask, struct res *r) { assert(0 == r->matched); - exprmark(p, cp, mask, r->matches); - r->matched = expreval(p, r->matches); + exprmark(e, cp, mask, r->matches); + r->matched = expreval(e, r->matches); }