From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from krisdoz.my.domain (schwarze@localhost [127.0.0.1]) by krisdoz.my.domain (8.14.3/8.14.3) with ESMTP id pADAC5jx005747 for ; Sun, 13 Nov 2011 05:12:05 -0500 (EST) Received: (from schwarze@localhost) by krisdoz.my.domain (8.14.3/8.14.3/Submit) id pADAC55t029480; Sun, 13 Nov 2011 05:12:05 -0500 (EST) Date: Sun, 13 Nov 2011 05:12:05 -0500 (EST) Message-Id: <201111131012.pADAC55t029480@krisdoz.my.domain> X-Mailinglist: mdocml-source Reply-To: source@mdocml.bsd.lv MIME-Version: 1.0 From: schwarze@mdocml.bsd.lv To: source@mdocml.bsd.lv Subject: mdocml: Less misleading file names; ok kristaps@. X-Mailer: activitymail 1.26, http://search.cpan.org/dist/activitymail/ Content-Type: text/plain; charset=utf-8 Log Message: ----------- Less misleading file names; ok kristaps@. Modified Files: -------------- mdocml: Makefile apropos.c cgi.c Added Files: ----------- mdocml: apropos_db.c apropos_db.h Removed Files: ------------- mdocml: apropos.h db.c Revision Data ------------- --- /dev/null +++ apropos_db.c @@ -0,0 +1,541 @@ +/* $Id: apropos_db.c,v 1.1 2011/11/13 10:12:05 schwarze Exp $ */ +/* + * Copyright (c) 2011 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#include +#include +#include +#include +#include +#include + +#ifdef __linux__ +# include +#else +# include +#endif + +#include "apropos_db.h" +#include "mandoc.h" + +enum match { + MATCH_REGEX, + MATCH_REGEXCASE, + MATCH_STR, + MATCH_STRCASE +}; + +struct expr { + enum match match; + int mask; + char *v; + regex_t re; +}; + +struct type { + int mask; + const char *name; +}; + +static const struct type types[] = { + { TYPE_NAME, "name" }, + { TYPE_FUNCTION, "func" }, + { TYPE_UTILITY, "utility" }, + { TYPE_INCLUDES, "incl" }, + { TYPE_VARIABLE, "var" }, + { TYPE_STANDARD, "stand" }, + { TYPE_AUTHOR, "auth" }, + { TYPE_CONFIG, "conf" }, + { TYPE_DESC, "desc" }, + { TYPE_XREF, "xref" }, + { TYPE_PATH, "path" }, + { TYPE_ENV, "env" }, + { TYPE_ERR, "err" }, + { INT_MAX, "all" }, + { 0, NULL } +}; + +static DB *btree_open(void); +static int btree_read(const DBT *, const struct mchars *, char **); +static int exprexec(const struct expr *, char *); +static DB *index_open(void); +static int index_read(const DBT *, const DBT *, + const struct mchars *, struct rec *); +static void norm_string(const char *, + const struct mchars *, char **); +static size_t norm_utf8(unsigned int, char[7]); + +/* + * Open the keyword mandoc-db database. + */ +static DB * +btree_open(void) +{ + BTREEINFO info; + DB *db; + + memset(&info, 0, sizeof(BTREEINFO)); + info.flags = R_DUP; + + db = dbopen("mandoc.db", O_RDONLY, 0, DB_BTREE, &info); + if (NULL != db) + return(db); + + return(NULL); +} + +/* + * Read a keyword from the database and normalise it. + * Return 0 if the database is insane, else 1. + */ +static int +btree_read(const DBT *v, const struct mchars *mc, char **buf) +{ + + /* Sanity: are we nil-terminated? */ + + assert(v->size > 0); + if ('\0' != ((char *)v->data)[(int)v->size - 1]) + return(0); + + norm_string((char *)v->data, mc, buf); + 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. + * 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; + + rc = 0; + + if (cp <= 0x0000007F) { + rc = 1; + out[0] = (char)cp; + } else if (cp <= 0x000007FF) { + rc = 2; + out[0] = (cp >> 6 & 31) | 192; + out[1] = (cp & 63) | 128; + } else if (cp <= 0x0000FFFF) { + rc = 3; + out[0] = (cp >> 12 & 15) | 224; + out[1] = (cp >> 6 & 63) | 128; + out[2] = (cp & 63) | 128; + } else if (cp <= 0x001FFFFF) { + rc = 4; + out[0] = (cp >> 18 & 7) | 240; + out[1] = (cp >> 12 & 63) | 128; + out[2] = (cp >> 6 & 63) | 128; + out[3] = (cp & 63) | 128; + } else if (cp <= 0x03FFFFFF) { + rc = 5; + out[0] = (cp >> 24 & 3) | 248; + out[1] = (cp >> 18 & 63) | 128; + out[2] = (cp >> 12 & 63) | 128; + out[3] = (cp >> 6 & 63) | 128; + out[4] = (cp & 63) | 128; + } else if (cp <= 0x7FFFFFFF) { + rc = 6; + out[0] = (cp >> 30 & 1) | 252; + out[1] = (cp >> 24 & 63) | 128; + out[2] = (cp >> 18 & 63) | 128; + out[3] = (cp >> 12 & 63) | 128; + out[4] = (cp >> 6 & 63) | 128; + out[5] = (cp & 63) | 128; + } else + return(0); + + out[rc] = '\0'; + return(rc); +} + +/* + * Normalise strings from the index and database. + * These strings are escaped as defined by mandoc_char(7) along with + * other goop in mandoc.h (e.g., soft hyphens). + * This function normalises these into a nice UTF-8 string. + * Returns 0 if the database is fucked. + */ +static void +norm_string(const char *val, const struct mchars *mc, char **buf) +{ + size_t sz, bsz; + char utfbuf[7]; + const char *seq, *cpp; + int len, u, pos; + enum mandoc_esc esc; + static const char res[] = { '\\', '\t', + ASCII_NBRSP, ASCII_HYPH, '\0' }; + + /* Pre-allocate by the length of the input */ + + bsz = strlen(val) + 1; + *buf = mandoc_realloc(*buf, bsz); + pos = 0; + + while ('\0' != *val) { + /* + * Halt on the first escape sequence. + * This also halts on the end of string, in which case + * we just copy, fallthrough, and exit the loop. + */ + if ((sz = strcspn(val, res)) > 0) { + memcpy(&(*buf)[pos], val, sz); + pos += (int)sz; + val += (int)sz; + } + + if (ASCII_HYPH == *val) { + (*buf)[pos++] = '-'; + val++; + continue; + } else if ('\t' == *val || ASCII_NBRSP == *val) { + (*buf)[pos++] = ' '; + val++; + continue; + } else if ('\\' != *val) + break; + + /* Read past the slash. */ + + val++; + u = 0; + + /* + * Parse the escape sequence and see if it's a + * predefined character or special character. + */ + + esc = mandoc_escape(&val, &seq, &len); + if (ESCAPE_ERROR == esc) + break; + + /* + * XXX - this just does UTF-8, but we need to know + * beforehand whether we should do text substitution. + */ + + switch (esc) { + case (ESCAPE_SPECIAL): + if (0 != (u = mchars_spec2cp(mc, seq, len))) + break; + /* FALLTHROUGH */ + default: + continue; + } + + /* + * If we have a Unicode codepoint, try to convert that + * to a UTF-8 byte string. + */ + + cpp = utfbuf; + if (0 == (sz = norm_utf8(u, utfbuf))) + continue; + + /* Copy the rendered glyph into the stream. */ + + sz = strlen(cpp); + bsz += sz; + + *buf = mandoc_realloc(*buf, bsz); + + memcpy(&(*buf)[pos], cpp, sz); + pos += (int)sz; + } + + (*buf)[pos] = '\0'; +} + +/* + * Open the filename-index mandoc-db database. + * Returns NULL if opening failed. + */ +static DB * +index_open(void) +{ + DB *db; + + db = dbopen("mandoc.index", O_RDONLY, 0, DB_RECNO, NULL); + if (NULL != db) + return(db); + + return(NULL); +} + +/* + * Safely unpack from an index file record into the structure. + * 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) +{ + size_t left; + char *np, *cp; + +#define INDEX_BREAD(_dst) \ + do { \ + if (NULL == (np = memchr(cp, '\0', left))) \ + return(0); \ + norm_string(cp, mc, &(_dst)); \ + left -= (np - cp) + 1; \ + cp = np + 1; \ + } while (/* CONSTCOND */ 0) + + left = val->size; + cp = (char *)val->data; + + rec->rec = *(recno_t *)key->data; + + 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". + * Filter out by "opts". + * Call "res" with the results, which may be zero. + */ +void +apropos_search(const struct opts *opts, const struct expr *expr, + void *arg, void (*res)(struct rec *, size_t, void *)) +{ + int i, len, root, leaf; + DBT key, val; + DB *btree, *idx; + struct mchars *mc; + int ch; + char *buf; + recno_t rec; + struct rec *recs; + struct rec srec; + + root = -1; + leaf = -1; + btree = NULL; + idx = NULL; + mc = NULL; + buf = NULL; + recs = NULL; + len = 0; + + memset(&srec, 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())) + goto out; + if (NULL == (idx = index_open())) + goto out; + + while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) { + /* + * Low-water mark for key and value. + * 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)) + break; + + if ( ! exprexec(expr, buf)) + continue; + + memcpy(&rec, val.data + 4, sizeof(recno_t)); + + /* + * O(log n) scan for prior records. Since a record + * number is unbounded, this has decent performance over + * a complex hash function. + */ + + for (leaf = root; leaf >= 0; ) + if (rec > recs[leaf].rec && recs[leaf].rhs >= 0) + leaf = recs[leaf].rhs; + else if (rec < recs[leaf].rec && recs[leaf].lhs >= 0) + leaf = recs[leaf].lhs; + else + break; + + if (leaf >= 0 && recs[leaf].rec == rec) + continue; + + /* + * Now we actually extract the manpage's metadata from + * the index database. + */ + + key.data = &rec; + key.size = sizeof(recno_t); + + if (0 != (*idx->get)(idx, &key, &val, 0)) + break; + + srec.lhs = srec.rhs = -1; + if ( ! index_read(&key, &val, mc, &srec)) + break; + + if (opts->cat && strcasecmp(opts->cat, srec.cat)) + continue; + if (opts->arch && strcasecmp(opts->arch, srec.arch)) + continue; + + recs = mandoc_realloc + (recs, (len + 1) * sizeof(struct rec)); + + memcpy(&recs[len], &srec, sizeof(struct rec)); + + /* Append to our tree. */ + + if (leaf >= 0) { + if (rec > recs[leaf].rec) + recs[leaf].rhs = len; + else + recs[leaf].lhs = len; + } else + root = len; + + memset(&srec, 0, sizeof(struct rec)); + len++; + } + + if (1 == ch) + (*res)(recs, len, arg); + + /* XXX: else? corrupt database error? */ +out: + 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(srec.cat); + free(srec.title); + free(srec.arch); + free(srec.desc); + + if (mc) + mchars_free(mc); + if (btree) + (*btree->close)(btree); + if (idx) + (*idx->close)(idx); + + free(buf); + free(recs); +} + +struct expr * +exprcomp(int cs, char *argv[], int argc) +{ + struct expr *p; + struct expr e; + int i, pos, ch; + + pos = 0; + + if (pos > argc) + return(NULL); + + for (i = 0; 0 != types[i].mask; i++) + if (0 == strcmp(types[i].name, argv[pos])) + break; + + if (0 == (e.mask = types[i].mask)) + return(NULL); + + if (++pos > argc--) + return(NULL); + + if ('-' != *argv[pos]) + e.match = cs ? MATCH_STRCASE : MATCH_STR; + else if (0 == strcmp("-eq", argv[pos])) + e.match = cs ? MATCH_STRCASE : MATCH_STR; + else if (0 == strcmp("-ieq", argv[pos])) + e.match = MATCH_STRCASE; + else if (0 == strcmp("-re", argv[pos])) + e.match = cs ? MATCH_REGEXCASE : MATCH_REGEX; + else if (0 == strcmp("-ire", argv[pos])) + e.match = MATCH_REGEXCASE; + else + return(NULL); + + if ('-' == *argv[pos]) + pos++; + + if (pos > argc--) + return(NULL); + + e.v = mandoc_strdup(argv[pos]); + + if (MATCH_REGEX == e.match || MATCH_REGEXCASE == e.match) { + ch = REG_EXTENDED | REG_NOSUB; + if (MATCH_REGEXCASE == e.match) + ch |= REG_ICASE; + if (regcomp(&e.re, e.v, ch)) + return(NULL); + } + + p = mandoc_calloc(1, sizeof(struct expr)); + memcpy(p, &e, sizeof(struct expr)); + return(p); +} + +void +exprfree(struct expr *p) +{ + + if (NULL == p) + return; + + if (MATCH_REGEX == p->match) + regfree(&p->re); + + free(p->v); + free(p); +} + +static int +exprexec(const struct expr *p, char *cp) +{ + + if (MATCH_STR == p->match) + return(0 == strcmp(p->v, cp)); + else if (MATCH_STRCASE == p->match) + return(0 == strcasecmp(p->v, cp)); + + assert(MATCH_REGEX == p->match); + return(0 == regexec(&p->re, cp, 0, NULL, 0)); +} --- /dev/null +++ apropos_db.h @@ -0,0 +1,71 @@ +/* $Id: apropos_db.h,v 1.1 2011/11/13 10:12:05 schwarze Exp $ */ +/* + * Copyright (c) 2011 Kristaps Dzonsons + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#ifndef APROPOS_H +#define APROPOS_H + +#define TYPE_NAME 0x01 +#define TYPE_FUNCTION 0x02 +#define TYPE_UTILITY 0x04 +#define TYPE_INCLUDES 0x08 +#define TYPE_VARIABLE 0x10 +#define TYPE_STANDARD 0x20 +#define TYPE_AUTHOR 0x40 +#define TYPE_CONFIG 0x80 +#define TYPE_DESC 0x100 +#define TYPE_XREF 0x200 +#define TYPE_PATH 0x400 +#define TYPE_ENV 0x800 +#define TYPE_ERR 0x1000 + +struct rec { + char *file; /* file in file-system */ + char *cat; /* category (3p, 3, etc.) */ + char *title; /* title (FOO, etc.) */ + char *arch; /* arch (or empty string) */ + char *desc; /* description (from Nd) */ + unsigned int rec; /* record in index */ + /* + * By the time the apropos_search() callback is called, these + * are superfluous. + * 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; +}; + +struct opts { + const char *arch; /* restrict to architecture */ + const char *cat; /* restrict to manual section */ +}; + +__BEGIN_DECLS + +struct expr; + +void apropos_search(const struct opts *, + const struct expr *, void *, + void (*)(struct rec *, size_t, void *)); + +struct expr *exprcomp(int, char *[], int); +void exprfree(struct expr *); + +__END_DECLS + +#endif /*!APROPOS_H*/ --- apropos.h +++ /dev/null @@ -1,71 +0,0 @@ -/* $Id: apropos.h,v 1.2 2011/11/09 22:05:56 kristaps Exp $ */ -/* - * Copyright (c) 2011 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#ifndef APROPOS_H -#define APROPOS_H - -#define TYPE_NAME 0x01 -#define TYPE_FUNCTION 0x02 -#define TYPE_UTILITY 0x04 -#define TYPE_INCLUDES 0x08 -#define TYPE_VARIABLE 0x10 -#define TYPE_STANDARD 0x20 -#define TYPE_AUTHOR 0x40 -#define TYPE_CONFIG 0x80 -#define TYPE_DESC 0x100 -#define TYPE_XREF 0x200 -#define TYPE_PATH 0x400 -#define TYPE_ENV 0x800 -#define TYPE_ERR 0x1000 - -struct rec { - char *file; /* file in file-system */ - char *cat; /* category (3p, 3, etc.) */ - char *title; /* title (FOO, etc.) */ - char *arch; /* arch (or empty string) */ - char *desc; /* description (from Nd) */ - unsigned int rec; /* record in index */ - /* - * By the time the apropos_search() callback is called, these - * are superfluous. - * 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; -}; - -struct opts { - const char *arch; /* restrict to architecture */ - const char *cat; /* restrict to manual section */ -}; - -__BEGIN_DECLS - -struct expr; - -void apropos_search(const struct opts *, - const struct expr *, void *, - void (*)(struct rec *, size_t, void *)); - -struct expr *exprcomp(int, char *[], int); -void exprfree(struct expr *); - -__END_DECLS - -#endif /*!APROPOS_H*/ Index: cgi.c =================================================================== RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/cgi.c,v retrieving revision 1.2 retrieving revision 1.3 diff -Lcgi.c -Lcgi.c -u -p -r1.2 -r1.3 --- cgi.c +++ cgi.c @@ -7,7 +7,7 @@ #include #include -#include "apropos.h" +#include "apropos_db.h" #include "mandoc.h" /* Index: apropos.c =================================================================== RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/apropos.c,v retrieving revision 1.10 retrieving revision 1.11 diff -Lapropos.c -Lapropos.c -u -p -r1.10 -r1.11 --- apropos.c +++ apropos.c @@ -21,7 +21,7 @@ #include #include -#include "apropos.h" +#include "apropos_db.h" #include "mandoc.h" static int cmp(const void *, const void *); Index: Makefile =================================================================== RCS file: /usr/vhosts/mdocml.bsd.lv/cvs/mdocml/Makefile,v retrieving revision 1.376 retrieving revision 1.377 diff -LMakefile -LMakefile -u -p -r1.376 -r1.377 --- Makefile +++ Makefile @@ -278,15 +278,15 @@ PRECONV_LNS = preconv.ln $(PRECONV_OBJS) $(PRECONV_LNS): config.h -APROPOS_OBJS = apropos.o db.o -APROPOS_LNS = apropos.ln db.ln +APROPOS_OBJS = apropos.o apropos_db.o +APROPOS_LNS = apropos.ln apropos_db.ln -$(APROPOS_OBJS) $(APROPOS_LNS): config.h mandoc.h apropos.h +$(APROPOS_OBJS) $(APROPOS_LNS): config.h mandoc.h apropos_db.h -CGI_OBJS = cgi.o db.o -CGI_LNS = cgi.ln db.ln +CGI_OBJS = cgi.o apropos_db.o +CGI_LNS = cgi.ln apropos_db.ln -$(CGI_OBJS) $(CGI_LNS): config.h mandoc.h apropos.h +$(CGI_OBJS) $(CGI_LNS): config.h mandoc.h apropos_db.h DEMANDOC_OBJS = demandoc.o DEMANDOC_LNS = demandoc.ln --- db.c +++ /dev/null @@ -1,541 +0,0 @@ -/* $Id: db.c,v 1.3 2011/11/09 23:31:36 kristaps Exp $ */ -/* - * Copyright (c) 2011 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#include -#include -#include -#include -#include -#include - -#ifdef __linux__ -# include -#else -# include -#endif - -#include "apropos.h" -#include "mandoc.h" - -enum match { - MATCH_REGEX, - MATCH_REGEXCASE, - MATCH_STR, - MATCH_STRCASE -}; - -struct expr { - enum match match; - int mask; - char *v; - regex_t re; -}; - -struct type { - int mask; - const char *name; -}; - -static const struct type types[] = { - { TYPE_NAME, "name" }, - { TYPE_FUNCTION, "func" }, - { TYPE_UTILITY, "utility" }, - { TYPE_INCLUDES, "incl" }, - { TYPE_VARIABLE, "var" }, - { TYPE_STANDARD, "stand" }, - { TYPE_AUTHOR, "auth" }, - { TYPE_CONFIG, "conf" }, - { TYPE_DESC, "desc" }, - { TYPE_XREF, "xref" }, - { TYPE_PATH, "path" }, - { TYPE_ENV, "env" }, - { TYPE_ERR, "err" }, - { INT_MAX, "all" }, - { 0, NULL } -}; - -static DB *btree_open(void); -static int btree_read(const DBT *, const struct mchars *, char **); -static int exprexec(const struct expr *, char *); -static DB *index_open(void); -static int index_read(const DBT *, const DBT *, - const struct mchars *, struct rec *); -static void norm_string(const char *, - const struct mchars *, char **); -static size_t norm_utf8(unsigned int, char[7]); - -/* - * Open the keyword mandoc-db database. - */ -static DB * -btree_open(void) -{ - BTREEINFO info; - DB *db; - - memset(&info, 0, sizeof(BTREEINFO)); - info.flags = R_DUP; - - db = dbopen("mandoc.db", O_RDONLY, 0, DB_BTREE, &info); - if (NULL != db) - return(db); - - return(NULL); -} - -/* - * Read a keyword from the database and normalise it. - * Return 0 if the database is insane, else 1. - */ -static int -btree_read(const DBT *v, const struct mchars *mc, char **buf) -{ - - /* Sanity: are we nil-terminated? */ - - assert(v->size > 0); - if ('\0' != ((char *)v->data)[(int)v->size - 1]) - return(0); - - norm_string((char *)v->data, mc, buf); - 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. - * 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; - - rc = 0; - - if (cp <= 0x0000007F) { - rc = 1; - out[0] = (char)cp; - } else if (cp <= 0x000007FF) { - rc = 2; - out[0] = (cp >> 6 & 31) | 192; - out[1] = (cp & 63) | 128; - } else if (cp <= 0x0000FFFF) { - rc = 3; - out[0] = (cp >> 12 & 15) | 224; - out[1] = (cp >> 6 & 63) | 128; - out[2] = (cp & 63) | 128; - } else if (cp <= 0x001FFFFF) { - rc = 4; - out[0] = (cp >> 18 & 7) | 240; - out[1] = (cp >> 12 & 63) | 128; - out[2] = (cp >> 6 & 63) | 128; - out[3] = (cp & 63) | 128; - } else if (cp <= 0x03FFFFFF) { - rc = 5; - out[0] = (cp >> 24 & 3) | 248; - out[1] = (cp >> 18 & 63) | 128; - out[2] = (cp >> 12 & 63) | 128; - out[3] = (cp >> 6 & 63) | 128; - out[4] = (cp & 63) | 128; - } else if (cp <= 0x7FFFFFFF) { - rc = 6; - out[0] = (cp >> 30 & 1) | 252; - out[1] = (cp >> 24 & 63) | 128; - out[2] = (cp >> 18 & 63) | 128; - out[3] = (cp >> 12 & 63) | 128; - out[4] = (cp >> 6 & 63) | 128; - out[5] = (cp & 63) | 128; - } else - return(0); - - out[rc] = '\0'; - return(rc); -} - -/* - * Normalise strings from the index and database. - * These strings are escaped as defined by mandoc_char(7) along with - * other goop in mandoc.h (e.g., soft hyphens). - * This function normalises these into a nice UTF-8 string. - * Returns 0 if the database is fucked. - */ -static void -norm_string(const char *val, const struct mchars *mc, char **buf) -{ - size_t sz, bsz; - char utfbuf[7]; - const char *seq, *cpp; - int len, u, pos; - enum mandoc_esc esc; - static const char res[] = { '\\', '\t', - ASCII_NBRSP, ASCII_HYPH, '\0' }; - - /* Pre-allocate by the length of the input */ - - bsz = strlen(val) + 1; - *buf = mandoc_realloc(*buf, bsz); - pos = 0; - - while ('\0' != *val) { - /* - * Halt on the first escape sequence. - * This also halts on the end of string, in which case - * we just copy, fallthrough, and exit the loop. - */ - if ((sz = strcspn(val, res)) > 0) { - memcpy(&(*buf)[pos], val, sz); - pos += (int)sz; - val += (int)sz; - } - - if (ASCII_HYPH == *val) { - (*buf)[pos++] = '-'; - val++; - continue; - } else if ('\t' == *val || ASCII_NBRSP == *val) { - (*buf)[pos++] = ' '; - val++; - continue; - } else if ('\\' != *val) - break; - - /* Read past the slash. */ - - val++; - u = 0; - - /* - * Parse the escape sequence and see if it's a - * predefined character or special character. - */ - - esc = mandoc_escape(&val, &seq, &len); - if (ESCAPE_ERROR == esc) - break; - - /* - * XXX - this just does UTF-8, but we need to know - * beforehand whether we should do text substitution. - */ - - switch (esc) { - case (ESCAPE_SPECIAL): - if (0 != (u = mchars_spec2cp(mc, seq, len))) - break; - /* FALLTHROUGH */ - default: - continue; - } - - /* - * If we have a Unicode codepoint, try to convert that - * to a UTF-8 byte string. - */ - - cpp = utfbuf; - if (0 == (sz = norm_utf8(u, utfbuf))) - continue; - - /* Copy the rendered glyph into the stream. */ - - sz = strlen(cpp); - bsz += sz; - - *buf = mandoc_realloc(*buf, bsz); - - memcpy(&(*buf)[pos], cpp, sz); - pos += (int)sz; - } - - (*buf)[pos] = '\0'; -} - -/* - * Open the filename-index mandoc-db database. - * Returns NULL if opening failed. - */ -static DB * -index_open(void) -{ - DB *db; - - db = dbopen("mandoc.index", O_RDONLY, 0, DB_RECNO, NULL); - if (NULL != db) - return(db); - - return(NULL); -} - -/* - * Safely unpack from an index file record into the structure. - * 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) -{ - size_t left; - char *np, *cp; - -#define INDEX_BREAD(_dst) \ - do { \ - if (NULL == (np = memchr(cp, '\0', left))) \ - return(0); \ - norm_string(cp, mc, &(_dst)); \ - left -= (np - cp) + 1; \ - cp = np + 1; \ - } while (/* CONSTCOND */ 0) - - left = val->size; - cp = (char *)val->data; - - rec->rec = *(recno_t *)key->data; - - 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". - * Filter out by "opts". - * Call "res" with the results, which may be zero. - */ -void -apropos_search(const struct opts *opts, const struct expr *expr, - void *arg, void (*res)(struct rec *, size_t, void *)) -{ - int i, len, root, leaf; - DBT key, val; - DB *btree, *idx; - struct mchars *mc; - int ch; - char *buf; - recno_t rec; - struct rec *recs; - struct rec srec; - - root = -1; - leaf = -1; - btree = NULL; - idx = NULL; - mc = NULL; - buf = NULL; - recs = NULL; - len = 0; - - memset(&srec, 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())) - goto out; - if (NULL == (idx = index_open())) - goto out; - - while (0 == (ch = (*btree->seq)(btree, &key, &val, R_NEXT))) { - /* - * Low-water mark for key and value. - * 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)) - break; - - if ( ! exprexec(expr, buf)) - continue; - - memcpy(&rec, val.data + 4, sizeof(recno_t)); - - /* - * O(log n) scan for prior records. Since a record - * number is unbounded, this has decent performance over - * a complex hash function. - */ - - for (leaf = root; leaf >= 0; ) - if (rec > recs[leaf].rec && recs[leaf].rhs >= 0) - leaf = recs[leaf].rhs; - else if (rec < recs[leaf].rec && recs[leaf].lhs >= 0) - leaf = recs[leaf].lhs; - else - break; - - if (leaf >= 0 && recs[leaf].rec == rec) - continue; - - /* - * Now we actually extract the manpage's metadata from - * the index database. - */ - - key.data = &rec; - key.size = sizeof(recno_t); - - if (0 != (*idx->get)(idx, &key, &val, 0)) - break; - - srec.lhs = srec.rhs = -1; - if ( ! index_read(&key, &val, mc, &srec)) - break; - - if (opts->cat && strcasecmp(opts->cat, srec.cat)) - continue; - if (opts->arch && strcasecmp(opts->arch, srec.arch)) - continue; - - recs = mandoc_realloc - (recs, (len + 1) * sizeof(struct rec)); - - memcpy(&recs[len], &srec, sizeof(struct rec)); - - /* Append to our tree. */ - - if (leaf >= 0) { - if (rec > recs[leaf].rec) - recs[leaf].rhs = len; - else - recs[leaf].lhs = len; - } else - root = len; - - memset(&srec, 0, sizeof(struct rec)); - len++; - } - - if (1 == ch) - (*res)(recs, len, arg); - - /* XXX: else? corrupt database error? */ -out: - 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(srec.cat); - free(srec.title); - free(srec.arch); - free(srec.desc); - - if (mc) - mchars_free(mc); - if (btree) - (*btree->close)(btree); - if (idx) - (*idx->close)(idx); - - free(buf); - free(recs); -} - -struct expr * -exprcomp(int cs, char *argv[], int argc) -{ - struct expr *p; - struct expr e; - int i, pos, ch; - - pos = 0; - - if (pos > argc) - return(NULL); - - for (i = 0; 0 != types[i].mask; i++) - if (0 == strcmp(types[i].name, argv[pos])) - break; - - if (0 == (e.mask = types[i].mask)) - return(NULL); - - if (++pos > argc--) - return(NULL); - - if ('-' != *argv[pos]) - e.match = cs ? MATCH_STRCASE : MATCH_STR; - else if (0 == strcmp("-eq", argv[pos])) - e.match = cs ? MATCH_STRCASE : MATCH_STR; - else if (0 == strcmp("-ieq", argv[pos])) - e.match = MATCH_STRCASE; - else if (0 == strcmp("-re", argv[pos])) - e.match = cs ? MATCH_REGEXCASE : MATCH_REGEX; - else if (0 == strcmp("-ire", argv[pos])) - e.match = MATCH_REGEXCASE; - else - return(NULL); - - if ('-' == *argv[pos]) - pos++; - - if (pos > argc--) - return(NULL); - - e.v = mandoc_strdup(argv[pos]); - - if (MATCH_REGEX == e.match || MATCH_REGEXCASE == e.match) { - ch = REG_EXTENDED | REG_NOSUB; - if (MATCH_REGEXCASE == e.match) - ch |= REG_ICASE; - if (regcomp(&e.re, e.v, ch)) - return(NULL); - } - - p = mandoc_calloc(1, sizeof(struct expr)); - memcpy(p, &e, sizeof(struct expr)); - return(p); -} - -void -exprfree(struct expr *p) -{ - - if (NULL == p) - return; - - if (MATCH_REGEX == p->match) - regfree(&p->re); - - free(p->v); - free(p); -} - -static int -exprexec(const struct expr *p, char *cp) -{ - - if (MATCH_STR == p->match) - return(0 == strcmp(p->v, cp)); - else if (MATCH_STRCASE == p->match) - return(0 == strcasecmp(p->v, cp)); - - assert(MATCH_REGEX == p->match); - return(0 == regexec(&p->re, cp, 0, NULL, 0)); -} -- To unsubscribe send an email to source+unsubscribe@mdocml.bsd.lv