version 1.54, 2015/02/27 16:02:10 |
version 1.63, 2015/11/26 07:42:11 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
* purpose with or without fee is hereby granted, provided that the above |
* copyright notice and this permission notice appear in all copies. |
* copyright notice and this permission notice appear in all copies. |
* |
* |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
|
|
#include <sys/types.h> |
#include <sys/types.h> |
|
|
#include <assert.h> |
#include <assert.h> |
|
#if HAVE_ERR |
|
#include <err.h> |
|
#endif |
#include <errno.h> |
#include <errno.h> |
#include <fcntl.h> |
#include <fcntl.h> |
#include <getopt.h> |
#include <getopt.h> |
|
|
#include <string.h> |
#include <string.h> |
#include <unistd.h> |
#include <unistd.h> |
|
|
#if HAVE_OHASH |
|
#include <ohash.h> |
|
#else |
|
#include "compat_ohash.h" |
|
#endif |
|
#include <sqlite3.h> |
#include <sqlite3.h> |
#ifndef SQLITE_DETERMINISTIC |
#ifndef SQLITE_DETERMINISTIC |
#define SQLITE_DETERMINISTIC 0 |
#define SQLITE_DETERMINISTIC 0 |
|
|
|
|
#include "mandoc.h" |
#include "mandoc.h" |
#include "mandoc_aux.h" |
#include "mandoc_aux.h" |
#include "manpath.h" |
#include "mandoc_ohash.h" |
|
#include "manconf.h" |
#include "mansearch.h" |
#include "mansearch.h" |
|
|
extern int mansearch_keymax; |
extern int mansearch_keymax; |
Line 55 extern const char *const mansearch_keynames[]; |
|
Line 54 extern const char *const mansearch_keynames[]; |
|
#define SQL_BIND_TEXT(_db, _s, _i, _v) \ |
#define SQL_BIND_TEXT(_db, _s, _i, _v) \ |
do { if (SQLITE_OK != sqlite3_bind_text \ |
do { if (SQLITE_OK != sqlite3_bind_text \ |
((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \ |
((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \ |
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ |
errx((int)MANDOCLEVEL_SYSERR, "%s", sqlite3_errmsg((_db))); \ |
} while (0) |
} while (0) |
#define SQL_BIND_INT64(_db, _s, _i, _v) \ |
#define SQL_BIND_INT64(_db, _s, _i, _v) \ |
do { if (SQLITE_OK != sqlite3_bind_int64 \ |
do { if (SQLITE_OK != sqlite3_bind_int64 \ |
((_s), (_i)++, (_v))) \ |
((_s), (_i)++, (_v))) \ |
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ |
errx((int)MANDOCLEVEL_SYSERR, "%s", sqlite3_errmsg((_db))); \ |
} while (0) |
} while (0) |
#define SQL_BIND_BLOB(_db, _s, _i, _v) \ |
#define SQL_BIND_BLOB(_db, _s, _i, _v) \ |
do { if (SQLITE_OK != sqlite3_bind_blob \ |
do { if (SQLITE_OK != sqlite3_bind_blob \ |
((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \ |
((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \ |
fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ |
errx((int)MANDOCLEVEL_SYSERR, "%s", sqlite3_errmsg((_db))); \ |
} while (0) |
} while (0) |
|
|
struct expr { |
struct expr { |
Line 92 static void buildnames(const struct mansearch *, |
|
Line 91 static void buildnames(const struct mansearch *, |
|
const char *, int form); |
const char *, int form); |
static char *buildoutput(sqlite3 *, sqlite3_stmt *, |
static char *buildoutput(sqlite3 *, sqlite3_stmt *, |
uint64_t, uint64_t); |
uint64_t, uint64_t); |
static void *hash_alloc(size_t, void *); |
|
static void hash_free(void *, void *); |
|
static void *hash_calloc(size_t, size_t, void *); |
|
static struct expr *exprcomp(const struct mansearch *, |
static struct expr *exprcomp(const struct mansearch *, |
int, char *[]); |
int, char *[]); |
static void exprfree(struct expr *); |
static void exprfree(struct expr *); |
Line 120 mansearch_setup(int start) |
|
Line 116 mansearch_setup(int start) |
|
|
|
if (start) { |
if (start) { |
if (NULL != pagecache) { |
if (NULL != pagecache) { |
fprintf(stderr, "pagecache already enabled\n"); |
warnx("pagecache already enabled"); |
return((int)MANDOCLEVEL_BADARG); |
return (int)MANDOCLEVEL_BADARG; |
} |
} |
|
|
pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES, |
pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES, |
Line 131 mansearch_setup(int start) |
|
Line 127 mansearch_setup(int start) |
|
if (MAP_FAILED == pagecache) { |
if (MAP_FAILED == pagecache) { |
perror("mmap"); |
perror("mmap"); |
pagecache = NULL; |
pagecache = NULL; |
return((int)MANDOCLEVEL_SYSERR); |
return (int)MANDOCLEVEL_SYSERR; |
} |
} |
|
|
c = sqlite3_config(SQLITE_CONFIG_PAGECACHE, |
c = sqlite3_config(SQLITE_CONFIG_PAGECACHE, |
pagecache, PC_PAGESIZE, PC_NUMPAGES); |
pagecache, PC_PAGESIZE, PC_NUMPAGES); |
|
|
if (SQLITE_OK == c) |
if (SQLITE_OK == c) |
return((int)MANDOCLEVEL_OK); |
return (int)MANDOCLEVEL_OK; |
|
|
fprintf(stderr, "pagecache: %s\n", sqlite3_errstr(c)); |
warnx("pagecache: %s", sqlite3_errstr(c)); |
|
|
} else if (NULL == pagecache) { |
} else if (NULL == pagecache) { |
fprintf(stderr, "pagecache missing\n"); |
warnx("pagecache missing"); |
return((int)MANDOCLEVEL_BADARG); |
return (int)MANDOCLEVEL_BADARG; |
} |
} |
|
|
if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) { |
if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) { |
perror("munmap"); |
perror("munmap"); |
pagecache = NULL; |
pagecache = NULL; |
return((int)MANDOCLEVEL_SYSERR); |
return (int)MANDOCLEVEL_SYSERR; |
} |
} |
|
|
pagecache = NULL; |
pagecache = NULL; |
return((int)MANDOCLEVEL_OK); |
return (int)MANDOCLEVEL_OK; |
} |
} |
|
|
int |
int |
Line 163 mansearch(const struct mansearch *search, |
|
Line 159 mansearch(const struct mansearch *search, |
|
int argc, char *argv[], |
int argc, char *argv[], |
struct manpage **res, size_t *sz) |
struct manpage **res, size_t *sz) |
{ |
{ |
int fd, rc, c, indexbit; |
|
int64_t pageid; |
int64_t pageid; |
uint64_t outbit, iterbit; |
uint64_t outbit, iterbit; |
char buf[PATH_MAX]; |
char buf[PATH_MAX]; |
Line 173 mansearch(const struct mansearch *search, |
|
Line 168 mansearch(const struct mansearch *search, |
|
sqlite3 *db; |
sqlite3 *db; |
sqlite3_stmt *s, *s2; |
sqlite3_stmt *s, *s2; |
struct match *mp; |
struct match *mp; |
struct ohash_info info; |
|
struct ohash htab; |
struct ohash htab; |
unsigned int idx; |
unsigned int idx; |
size_t i, j, cur, maxres; |
size_t i, j, cur, maxres; |
|
int c, chdir_status, getcwd_status, indexbit; |
|
|
info.calloc = hash_calloc; |
if (argc == 0 || (e = exprcomp(search, argc, argv)) == NULL) { |
info.alloc = hash_alloc; |
*sz = 0; |
info.free = hash_free; |
return 0; |
info.key_offset = offsetof(struct match, pageid); |
} |
|
|
*sz = cur = maxres = 0; |
cur = maxres = 0; |
sql = NULL; |
|
*res = NULL; |
*res = NULL; |
fd = -1; |
|
e = NULL; |
|
rc = 0; |
|
|
|
if (0 == argc) |
|
goto out; |
|
if (NULL == (e = exprcomp(search, argc, argv))) |
|
goto out; |
|
|
|
outbit = 0; |
|
if (NULL != search->outkey) { |
if (NULL != search->outkey) { |
|
outbit = TYPE_Nd; |
for (indexbit = 0, iterbit = 1; |
for (indexbit = 0, iterbit = 1; |
indexbit < mansearch_keymax; |
indexbit < mansearch_keymax; |
indexbit++, iterbit <<= 1) { |
indexbit++, iterbit <<= 1) { |
Line 206 mansearch(const struct mansearch *search, |
|
Line 192 mansearch(const struct mansearch *search, |
|
break; |
break; |
} |
} |
} |
} |
} |
} else |
|
outbit = 0; |
|
|
/* |
/* |
* Save a descriptor to the current working directory. |
* Remember the original working directory, if possible. |
* Since pathnames in the "paths" variable might be relative, |
* This will be needed if the second or a later directory |
* and we'll be chdir()ing into them, we need to keep a handle |
* is given as a relative path. |
* on our current directory from which to start the chdir(). |
* Do not error out if the current directory is not |
|
* searchable: Maybe it won't be needed after all. |
*/ |
*/ |
|
|
if (NULL == getcwd(buf, PATH_MAX)) { |
if (getcwd(buf, PATH_MAX) == NULL) { |
perror("getcwd"); |
getcwd_status = 0; |
goto out; |
(void)strlcpy(buf, strerror(errno), sizeof(buf)); |
} else if (-1 == (fd = open(buf, O_RDONLY, 0))) { |
} else |
perror(buf); |
getcwd_status = 1; |
goto out; |
|
} |
|
|
|
sql = sql_statement(e); |
sql = sql_statement(e); |
|
|
Line 233 mansearch(const struct mansearch *search, |
|
Line 219 mansearch(const struct mansearch *search, |
|
* scan it for our match expression. |
* scan it for our match expression. |
*/ |
*/ |
|
|
|
chdir_status = 0; |
for (i = 0; i < paths->sz; i++) { |
for (i = 0; i < paths->sz; i++) { |
if (-1 == fchdir(fd)) { |
if (chdir_status && paths->paths[i][0] != '/') { |
perror(buf); |
if ( ! getcwd_status) { |
free(*res); |
warnx("%s: getcwd: %s", paths->paths[i], buf); |
break; |
continue; |
} else if (-1 == chdir(paths->paths[i])) { |
} else if (chdir(buf) == -1) { |
|
perror(buf); |
|
continue; |
|
} |
|
} |
|
if (chdir(paths->paths[i]) == -1) { |
perror(paths->paths[i]); |
perror(paths->paths[i]); |
continue; |
continue; |
} |
} |
|
chdir_status = 1; |
|
|
c = sqlite3_open_v2(MANDOC_DB, &db, |
c = sqlite3_open_v2(MANDOC_DB, &db, |
SQLITE_OPEN_READONLY, NULL); |
SQLITE_OPEN_READONLY, NULL); |
|
|
if (SQLITE_OK != c) { |
if (SQLITE_OK != c) { |
fprintf(stderr, "%s/%s: %s\n", |
warn("%s/%s", paths->paths[i], MANDOC_DB); |
paths->paths[i], MANDOC_DB, strerror(errno)); |
|
sqlite3_close(db); |
sqlite3_close(db); |
continue; |
continue; |
} |
} |
Line 270 mansearch(const struct mansearch *search, |
|
Line 262 mansearch(const struct mansearch *search, |
|
j = 1; |
j = 1; |
c = sqlite3_prepare_v2(db, sql, -1, &s, NULL); |
c = sqlite3_prepare_v2(db, sql, -1, &s, NULL); |
if (SQLITE_OK != c) |
if (SQLITE_OK != c) |
fprintf(stderr, "%s\n", sqlite3_errmsg(db)); |
errx((int)MANDOCLEVEL_SYSERR, |
|
"%s", sqlite3_errmsg(db)); |
|
|
for (ep = e; NULL != ep; ep = ep->next) { |
for (ep = e; NULL != ep; ep = ep->next) { |
if (NULL == ep->substr) { |
if (NULL == ep->substr) { |
Line 281 mansearch(const struct mansearch *search, |
|
Line 274 mansearch(const struct mansearch *search, |
|
SQL_BIND_INT64(db, s, j, ep->bits); |
SQL_BIND_INT64(db, s, j, ep->bits); |
} |
} |
|
|
memset(&htab, 0, sizeof(struct ohash)); |
mandoc_ohash_init(&htab, 4, offsetof(struct match, pageid)); |
ohash_init(&htab, 4, &info); |
|
|
|
/* |
/* |
* Hash each entry on its [unique] document identifier. |
* Hash each entry on its [unique] document identifier. |
Line 312 mansearch(const struct mansearch *search, |
|
Line 304 mansearch(const struct mansearch *search, |
|
} |
} |
|
|
if (SQLITE_DONE != c) |
if (SQLITE_DONE != c) |
fprintf(stderr, "%s\n", sqlite3_errmsg(db)); |
warnx("%s", sqlite3_errmsg(db)); |
|
|
sqlite3_finalize(s); |
sqlite3_finalize(s); |
|
|
Line 321 mansearch(const struct mansearch *search, |
|
Line 313 mansearch(const struct mansearch *search, |
|
"WHERE pageid=? ORDER BY sec, arch, name", |
"WHERE pageid=? ORDER BY sec, arch, name", |
-1, &s, NULL); |
-1, &s, NULL); |
if (SQLITE_OK != c) |
if (SQLITE_OK != c) |
fprintf(stderr, "%s\n", sqlite3_errmsg(db)); |
errx((int)MANDOCLEVEL_SYSERR, |
|
"%s", sqlite3_errmsg(db)); |
|
|
c = sqlite3_prepare_v2(db, |
c = sqlite3_prepare_v2(db, |
"SELECT bits, key, pageid FROM keys " |
"SELECT bits, key, pageid FROM keys " |
"WHERE pageid=? AND bits & ?", |
"WHERE pageid=? AND bits & ?", |
-1, &s2, NULL); |
-1, &s2, NULL); |
if (SQLITE_OK != c) |
if (SQLITE_OK != c) |
fprintf(stderr, "%s\n", sqlite3_errmsg(db)); |
errx((int)MANDOCLEVEL_SYSERR, |
|
"%s", sqlite3_errmsg(db)); |
|
|
for (mp = ohash_first(&htab, &idx); |
for (mp = ohash_first(&htab, &idx); |
NULL != mp; |
NULL != mp; |
Line 369 mansearch(const struct mansearch *search, |
|
Line 363 mansearch(const struct mansearch *search, |
|
break; |
break; |
} |
} |
qsort(*res, cur, sizeof(struct manpage), manpage_compare); |
qsort(*res, cur, sizeof(struct manpage), manpage_compare); |
rc = 1; |
if (chdir_status && getcwd_status && chdir(buf) == -1) |
out: |
perror(buf); |
if (-1 != fd) { |
|
if (-1 == fchdir(fd)) |
|
perror(buf); |
|
close(fd); |
|
} |
|
exprfree(e); |
exprfree(e); |
free(sql); |
free(sql); |
*sz = cur; |
*sz = cur; |
return(rc); |
return 1; |
} |
} |
|
|
void |
void |
Line 403 manpage_compare(const void *vp1, const void *vp2) |
|
Line 392 manpage_compare(const void *vp1, const void *vp2) |
|
|
|
mp1 = vp1; |
mp1 = vp1; |
mp2 = vp2; |
mp2 = vp2; |
return( (diff = mp2->bits - mp1->bits) ? diff : |
return (diff = mp2->bits - mp1->bits) ? diff : |
(diff = mp1->sec - mp2->sec) ? diff : |
(diff = mp1->sec - mp2->sec) ? diff : |
strcasecmp(mp1->names, mp2->names)); |
strcasecmp(mp1->names, mp2->names); |
} |
} |
|
|
static void |
static void |
Line 514 buildnames(const struct mansearch *search, struct manp |
|
Line 503 buildnames(const struct mansearch *search, struct manp |
|
globfree(&globinfo); |
globfree(&globinfo); |
} |
} |
if (c != SQLITE_DONE) |
if (c != SQLITE_DONE) |
fprintf(stderr, "%s\n", sqlite3_errmsg(db)); |
warnx("%s", sqlite3_errmsg(db)); |
sqlite3_reset(s); |
sqlite3_reset(s); |
|
|
/* If none of the files is usable, use the first name. */ |
/* If none of the files is usable, use the first name. */ |
Line 564 buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pag |
|
Line 553 buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pag |
|
output = newoutput; |
output = newoutput; |
} |
} |
if (SQLITE_DONE != c) |
if (SQLITE_DONE != c) |
fprintf(stderr, "%s\n", sqlite3_errmsg(db)); |
warnx("%s", sqlite3_errmsg(db)); |
sqlite3_reset(s); |
sqlite3_reset(s); |
return(output); |
return output; |
} |
} |
|
|
/* |
/* |
Line 661 sql_statement(const struct expr *e) |
|
Line 650 sql_statement(const struct expr *e) |
|
needop = 1; |
needop = 1; |
} |
} |
|
|
return(sql); |
return sql; |
} |
} |
|
|
/* |
/* |
Line 744 exprcomp(const struct mansearch *search, int argc, cha |
|
Line 733 exprcomp(const struct mansearch *search, int argc, cha |
|
toopen = logic = igncase = 0; |
toopen = logic = igncase = 0; |
} |
} |
if ( ! (toopen || logic || igncase || toclose)) |
if ( ! (toopen || logic || igncase || toclose)) |
return(first); |
return first; |
|
|
fail: |
fail: |
if (NULL != first) |
if (NULL != first) |
exprfree(first); |
exprfree(first); |
return(NULL); |
return NULL; |
} |
} |
|
|
static struct expr * |
static struct expr * |
Line 762 exprterm(const struct mansearch *search, char *buf, in |
|
Line 751 exprterm(const struct mansearch *search, char *buf, in |
|
int i, irc; |
int i, irc; |
|
|
if ('\0' == *buf) |
if ('\0' == *buf) |
return(NULL); |
return NULL; |
|
|
e = mandoc_calloc(1, sizeof(struct expr)); |
e = mandoc_calloc(1, sizeof(struct expr)); |
|
|
Line 770 exprterm(const struct mansearch *search, char *buf, in |
|
Line 759 exprterm(const struct mansearch *search, char *buf, in |
|
e->bits = TYPE_Nm; |
e->bits = TYPE_Nm; |
e->substr = buf; |
e->substr = buf; |
e->equal = 1; |
e->equal = 1; |
return(e); |
return e; |
} |
} |
|
|
/* |
/* |
Line 782 exprterm(const struct mansearch *search, char *buf, in |
|
Line 771 exprterm(const struct mansearch *search, char *buf, in |
|
if (search->argmode == ARG_WORD) { |
if (search->argmode == ARG_WORD) { |
e->bits = TYPE_Nm; |
e->bits = TYPE_Nm; |
e->substr = NULL; |
e->substr = NULL; |
|
#if HAVE_REWB_BSD |
mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf); |
mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf); |
|
#elif HAVE_REWB_SYSV |
|
mandoc_asprintf(&val, "\\<%s\\>", buf); |
|
#else |
|
mandoc_asprintf(&val, |
|
"(^|[^a-zA-Z01-9_])%s([^a-zA-Z01-9_]|$)", buf); |
|
#endif |
cs = 0; |
cs = 0; |
} else if ((val = strpbrk(buf, "=~")) == NULL) { |
} else if ((val = strpbrk(buf, "=~")) == NULL) { |
e->bits = TYPE_Nm | TYPE_Nd; |
e->bits = TYPE_Nm | TYPE_Nd; |
Line 806 exprterm(const struct mansearch *search, char *buf, in |
|
Line 802 exprterm(const struct mansearch *search, char *buf, in |
|
free(val); |
free(val); |
if (irc) { |
if (irc) { |
regerror(irc, &e->regexp, errbuf, sizeof(errbuf)); |
regerror(irc, &e->regexp, errbuf, sizeof(errbuf)); |
fprintf(stderr, "regcomp: %s\n", errbuf); |
warnx("regcomp: %s", errbuf); |
free(e); |
free(e); |
return(NULL); |
return NULL; |
} |
} |
} |
} |
|
|
if (e->bits) |
if (e->bits) |
return(e); |
return e; |
|
|
/* |
/* |
* Parse out all possible fields. |
* Parse out all possible fields. |
Line 835 exprterm(const struct mansearch *search, char *buf, in |
|
Line 831 exprterm(const struct mansearch *search, char *buf, in |
|
if (i == mansearch_keymax) { |
if (i == mansearch_keymax) { |
if (strcasecmp(key, "any")) { |
if (strcasecmp(key, "any")) { |
free(e); |
free(e); |
return(NULL); |
return NULL; |
} |
} |
e->bits |= ~0ULL; |
e->bits |= ~0ULL; |
} |
} |
} |
} |
|
|
return(e); |
return e; |
} |
} |
|
|
static void |
static void |
Line 854 exprfree(struct expr *p) |
|
Line 850 exprfree(struct expr *p) |
|
free(p); |
free(p); |
p = pp; |
p = pp; |
} |
} |
} |
|
|
|
static void * |
|
hash_calloc(size_t nmemb, size_t sz, void *arg) |
|
{ |
|
|
|
return(mandoc_calloc(nmemb, sz)); |
|
} |
|
|
|
static void * |
|
hash_alloc(size_t sz, void *arg) |
|
{ |
|
|
|
return(mandoc_malloc(sz)); |
|
} |
|
|
|
static void |
|
hash_free(void *p, void *arg) |
|
{ |
|
|
|
free(p); |
|
} |
} |