=================================================================== RCS file: /cvs/mandoc/mansearch.c,v retrieving revision 1.32 retrieving revision 1.41 diff -u -p -r1.32 -r1.41 --- mandoc/mansearch.c 2014/04/16 21:36:18 1.32 +++ mandoc/mansearch.c 2014/08/05 14:43:10 1.41 @@ -1,4 +1,4 @@ -/* $Id: mansearch.c,v 1.32 2014/04/16 21:36:18 schwarze Exp $ */ +/* $Id: mansearch.c,v 1.41 2014/08/05 14:43:10 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons * Copyright (c) 2013, 2014 Ingo Schwarze @@ -38,6 +38,9 @@ #include "compat_ohash.h" #endif #include +#ifndef SQLITE_DETERMINISTIC +#define SQLITE_DETERMINISTIC 0 +#endif #include "mandoc.h" #include "mandoc_aux.h" @@ -64,13 +67,14 @@ extern const char *const mansearch_keynames[]; } while (0) struct expr { - uint64_t bits; /* type-mask */ - const char *substr; /* to search for, if applicable */ regex_t regexp; /* compiled regexp, if applicable */ + const char *substr; /* to search for, if applicable */ + struct expr *next; /* next in sequence */ + uint64_t bits; /* type-mask */ + int equal; /* equality, not subsring match */ int open; /* opening parentheses before */ int and; /* logical AND before */ int close; /* closing parentheses after */ - struct expr *next; /* next in sequence */ }; struct match { @@ -85,14 +89,15 @@ static void buildnames(struct manpage *, sqlite3 *, static char *buildoutput(sqlite3 *, sqlite3_stmt *, uint64_t, uint64_t); static void *hash_alloc(size_t, void *); -static void hash_free(void *, size_t, void *); -static void *hash_halloc(size_t, void *); -static struct expr *exprcomp(const struct mansearch *, +static void hash_free(void *, void *); +static void *hash_calloc(size_t, size_t, void *); +static struct expr *exprcomp(const struct mansearch *, int, char *[]); static void exprfree(struct expr *); static struct expr *exprspec(struct expr *, uint64_t, const char *, const char *); static struct expr *exprterm(const struct mansearch *, char *, int); +static int manpage_compare(const void *, const void *); static void sql_append(char **sql, size_t *sz, const char *newstr, int count); static void sql_match(sqlite3_context *context, @@ -101,6 +106,7 @@ static void sql_regexp(sqlite3_context *context, int argc, sqlite3_value **argv); static char *sql_statement(const struct expr *); + int mansearch_setup(int start) { @@ -170,11 +176,9 @@ mansearch(const struct mansearch *search, unsigned int idx; size_t i, j, cur, maxres; - memset(&info, 0, sizeof(struct ohash_info)); - - info.halloc = hash_halloc; + info.calloc = hash_calloc; info.alloc = hash_alloc; - info.hfree = hash_free; + info.free = hash_free; info.key_offset = offsetof(struct match, pageid); *sz = cur = maxres = 0; @@ -235,11 +239,10 @@ mansearch(const struct mansearch *search, } else if (-1 == chdir(paths->paths[i])) { perror(paths->paths[i]); continue; - } + } - c = sqlite3_open_v2 - (MANDOC_DB, &db, - SQLITE_OPEN_READONLY, NULL); + c = sqlite3_open_v2(MANDOC_DB, &db, + SQLITE_OPEN_READONLY, NULL); if (SQLITE_OK != c) { perror(MANDOC_DB); @@ -288,9 +291,9 @@ mansearch(const struct mansearch *search, */ while (SQLITE_ROW == (c = sqlite3_step(s))) { pageid = sqlite3_column_int64(s, 2); - idx = ohash_lookup_memory - (&htab, (char *)&pageid, - sizeof(uint64_t), (uint32_t)pageid); + idx = ohash_lookup_memory(&htab, + (char *)&pageid, sizeof(uint64_t), + (uint32_t)pageid); if (NULL != ohash_find(&htab, idx)) continue; @@ -299,7 +302,7 @@ mansearch(const struct mansearch *search, mp->pageid = pageid; mp->form = sqlite3_column_int(s, 1); if (TYPE_Nd == outbit) - mp->desc = mandoc_strdup( + mp->desc = mandoc_strdup((const char *) sqlite3_column_text(s, 0)); ohash_insert(&htab, idx, mp); } @@ -309,15 +312,16 @@ mansearch(const struct mansearch *search, sqlite3_finalize(s); - c = sqlite3_prepare_v2(db, - "SELECT * FROM mlinks WHERE pageid=?" - " ORDER BY sec, arch, name", + c = sqlite3_prepare_v2(db, + "SELECT sec, arch, name, pageid FROM mlinks " + "WHERE pageid=? ORDER BY sec, arch, name", -1, &s, NULL); if (SQLITE_OK != c) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); c = sqlite3_prepare_v2(db, - "SELECT * FROM keys WHERE pageid=? AND bits & ?", + "SELECT bits, key, pageid FROM keys " + "WHERE pageid=? AND bits & ?", -1, &s2, NULL); if (SQLITE_OK != c) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); @@ -327,10 +331,11 @@ mansearch(const struct mansearch *search, mp = ohash_next(&htab, &idx)) { if (cur + 1 > maxres) { maxres += 1024; - *res = mandoc_realloc - (*res, maxres * sizeof(struct manpage)); + *res = mandoc_reallocarray(*res, + maxres, sizeof(struct manpage)); } mpage = *res + cur; + mpage->sec = 10; mpage->form = mp->form; buildnames(mpage, db, s, mp->pageid, paths->paths[i], mp->form); @@ -347,6 +352,7 @@ mansearch(const struct mansearch *search, sqlite3_close(db); ohash_delete(&htab); } + qsort(*res, cur, sizeof(struct manpage), manpage_compare); rc = 1; out: if (-1 != fd) { @@ -360,6 +366,18 @@ out: return(rc); } +static int +manpage_compare(const void *vp1, const void *vp2) +{ + const struct manpage *mp1, *mp2; + int diff; + + mp1 = vp1; + mp2 = vp2; + diff = mp1->sec - mp2->sec; + return(diff ? diff : strcasecmp(mp1->names, mp2->names)); +} + static void buildnames(struct manpage *mpage, sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, const char *path, int form) @@ -388,10 +406,15 @@ buildnames(struct manpage *mpage, sqlite3 *db, sqlite3 /* Fetch the next name. */ - sec = sqlite3_column_text(s, 0); - arch = sqlite3_column_text(s, 1); - name = sqlite3_column_text(s, 2); + sec = (const char *)sqlite3_column_text(s, 0); + arch = (const char *)sqlite3_column_text(s, 1); + name = (const char *)sqlite3_column_text(s, 2); + /* Remember the first section found. */ + + if (9 < mpage->sec && '1' <= *sec && '9' >= *sec) + mpage->sec = (*sec - '1') + 1; + /* If the section changed, append the old one. */ if (NULL != prevsec && @@ -474,7 +497,7 @@ buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pag oldoutput = output; sep1 = " # "; } - data = sqlite3_column_text(s, 1); + data = (const char *)sqlite3_column_text(s, 1); mandoc_asprintf(&newoutput, "%s%s%s", oldoutput, sep1, data); free(output); @@ -542,7 +565,8 @@ sql_statement(const struct expr *e) size_t sz; int needop; - sql = mandoc_strdup("SELECT * FROM mpages WHERE "); + sql = mandoc_strdup( + "SELECT desc, form, pageid FROM mpages WHERE "); sz = strlen(sql); for (needop = 0; NULL != e; e = e->next) { @@ -561,6 +585,9 @@ sql_statement(const struct expr *e) ? (NULL == e->substr ? "pageid IN (SELECT pageid FROM names " "WHERE name REGEXP ?)" + : e->equal + ? "pageid IN (SELECT pageid FROM names " + "WHERE name = ?)" : "pageid IN (SELECT pageid FROM names " "WHERE name MATCH ?)") : (NULL == e->substr @@ -590,7 +617,7 @@ exprcomp(const struct mansearch *search, int argc, cha first = cur = NULL; logic = igncase = toclose = 0; - toopen = 1; + toopen = NULL != search->sec || NULL != search->arch; for (i = 0; i < argc; i++) { if (0 == strcmp("(", argv[i])) { @@ -659,9 +686,12 @@ exprcomp(const struct mansearch *search, int argc, cha if (toopen || logic || igncase || toclose) goto fail; - cur->close++; - cur = exprspec(cur, TYPE_arch, search->arch, "^(%s|any)$"); - exprspec(cur, TYPE_sec, search->sec, "^%s$"); + if (NULL != search->sec || NULL != search->arch) + cur->close++; + if (NULL != search->arch) + cur = exprspec(cur, TYPE_arch, search->arch, "^(%s|any)$"); + if (NULL != search->sec) + exprspec(cur, TYPE_sec, search->sec, "^%s$"); return(first); @@ -679,9 +709,6 @@ exprspec(struct expr *cur, uint64_t key, const char *v char *cp; int irc; - if (NULL == value) - return(cur); - mandoc_asprintf(&cp, format, value); cur->next = mandoc_calloc(1, sizeof(struct expr)); cur = cur->next; @@ -702,7 +729,7 @@ exprterm(const struct mansearch *search, char *buf, in { char errbuf[BUFSIZ]; struct expr *e; - char *key, *v; + char *key, *val; uint64_t iterbit; int i, irc; @@ -711,41 +738,65 @@ exprterm(const struct mansearch *search, char *buf, in e = mandoc_calloc(1, sizeof(struct expr)); - /*"whatis" mode uses an opaque string and default fields. */ - - if (MANSEARCH_WHATIS & search->flags) { - e->substr = buf; + if (MANSEARCH_MAN & search->flags) { e->bits = search->deftype; + e->substr = buf; + e->equal = 1; return(e); } /* - * If no =~ is specified, search with equality over names and - * descriptions. - * If =~ begins the phrase, use name and description fields. + * Look for an '=' or '~' operator, + * unless forced to some fixed macro keys. */ - if (NULL == (v = strpbrk(buf, "=~"))) { - e->substr = buf; + if (MANSEARCH_WHATIS & search->flags) + val = NULL; + else + val = strpbrk(buf, "=~"); + + if (NULL == val) { e->bits = search->deftype; - return(e); - } else if (v == buf) - e->bits = search->deftype; + e->substr = buf; - if ('~' == *v++) { + /* + * Found an operator. + * Regexp search is requested by !e->substr. + */ + + } else { + if (val == buf) + e->bits = search->deftype; + if ('=' == *val) + e->substr = val + 1; + *val++ = '\0'; if (NULL != strstr(buf, "arch")) cs = 0; - if (0 != (irc = regcomp(&e->regexp, v, - REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE)))) { + } + + /* Compile regular expressions. */ + + if (MANSEARCH_WHATIS & search->flags) { + e->substr = NULL; + mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf); + } + + if (NULL == e->substr) { + irc = regcomp(&e->regexp, val, + REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE)); + if (MANSEARCH_WHATIS & search->flags) + free(val); + if (irc) { regerror(irc, &e->regexp, errbuf, sizeof(errbuf)); fprintf(stderr, "regcomp: %s\n", errbuf); free(e); return(NULL); } - } else - e->substr = v; - v[-1] = '\0'; + } + if (e->bits) + return(e); + /* * Parse out all possible fields. * If the field doesn't resolve, bail. @@ -788,10 +839,10 @@ exprfree(struct expr *p) } static void * -hash_halloc(size_t sz, void *arg) +hash_calloc(size_t nmemb, size_t sz, void *arg) { - return(mandoc_calloc(sz, 1)); + return(mandoc_calloc(nmemb, sz)); } static void * @@ -802,7 +853,7 @@ hash_alloc(size_t sz, void *arg) } static void -hash_free(void *p, size_t sz, void *arg) +hash_free(void *p, void *arg) { free(p);