=================================================================== RCS file: /cvs/mandoc/mansearch.c,v retrieving revision 1.53 retrieving revision 1.61 diff -u -p -r1.53 -r1.61 --- mandoc/mansearch.c 2015/01/20 18:21:18 1.53 +++ mandoc/mansearch.c 2015/11/06 21:19:09 1.61 @@ -1,4 +1,4 @@ -/* $Id: mansearch.c,v 1.53 2015/01/20 18:21:18 schwarze Exp $ */ +/* $Id: mansearch.c,v 1.61 2015/11/06 21:19:09 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons * Copyright (c) 2013, 2014, 2015 Ingo Schwarze @@ -7,9 +7,9 @@ * 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 + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES * 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 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF @@ -21,9 +21,11 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -33,11 +35,6 @@ #include #include -#if HAVE_OHASH -#include -#else -#include "compat_ohash.h" -#endif #include #ifndef SQLITE_DETERMINISTIC #define SQLITE_DETERMINISTIC 0 @@ -45,7 +42,8 @@ #include "mandoc.h" #include "mandoc_aux.h" -#include "manpath.h" +#include "mandoc_ohash.h" +#include "manconf.h" #include "mansearch.h" extern int mansearch_keymax; @@ -54,17 +52,17 @@ extern const char *const mansearch_keynames[]; #define SQL_BIND_TEXT(_db, _s, _i, _v) \ do { if (SQLITE_OK != sqlite3_bind_text \ ((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \ - fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ + warnx("%s", sqlite3_errmsg((_db))); \ } while (0) #define SQL_BIND_INT64(_db, _s, _i, _v) \ do { if (SQLITE_OK != sqlite3_bind_int64 \ ((_s), (_i)++, (_v))) \ - fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ + warnx("%s", sqlite3_errmsg((_db))); \ } while (0) #define SQL_BIND_BLOB(_db, _s, _i, _v) \ do { if (SQLITE_OK != sqlite3_bind_blob \ ((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \ - fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ + warnx("%s", sqlite3_errmsg((_db))); \ } while (0) struct expr { @@ -91,9 +89,6 @@ static void buildnames(const struct mansearch *, const char *, int form); static char *buildoutput(sqlite3 *, sqlite3_stmt *, 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 *, int, char *[]); static void exprfree(struct expr *); @@ -119,8 +114,8 @@ mansearch_setup(int start) if (start) { if (NULL != pagecache) { - fprintf(stderr, "pagecache already enabled\n"); - return((int)MANDOCLEVEL_BADARG); + warnx("pagecache already enabled"); + return (int)MANDOCLEVEL_BADARG; } pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES, @@ -130,30 +125,30 @@ mansearch_setup(int start) if (MAP_FAILED == pagecache) { perror("mmap"); pagecache = NULL; - return((int)MANDOCLEVEL_SYSERR); + return (int)MANDOCLEVEL_SYSERR; } c = sqlite3_config(SQLITE_CONFIG_PAGECACHE, pagecache, PC_PAGESIZE, PC_NUMPAGES); 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) { - fprintf(stderr, "pagecache missing\n"); - return((int)MANDOCLEVEL_BADARG); + warnx("pagecache missing"); + return (int)MANDOCLEVEL_BADARG; } if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) { perror("munmap"); pagecache = NULL; - return((int)MANDOCLEVEL_SYSERR); + return (int)MANDOCLEVEL_SYSERR; } pagecache = NULL; - return((int)MANDOCLEVEL_OK); + return (int)MANDOCLEVEL_OK; } int @@ -162,7 +157,6 @@ mansearch(const struct mansearch *search, int argc, char *argv[], struct manpage **res, size_t *sz) { - int fd, rc, c, indexbit; int64_t pageid; uint64_t outbit, iterbit; char buf[PATH_MAX]; @@ -172,30 +166,21 @@ mansearch(const struct mansearch *search, sqlite3 *db; sqlite3_stmt *s, *s2; struct match *mp; - struct ohash_info info; struct ohash htab; unsigned int idx; size_t i, j, cur, maxres; + int c, chdir_status, getcwd_status, indexbit; - info.calloc = hash_calloc; - info.alloc = hash_alloc; - info.free = hash_free; - info.key_offset = offsetof(struct match, pageid); + if (argc == 0 || (e = exprcomp(search, argc, argv)) == NULL) { + *sz = 0; + return 0; + } - *sz = cur = maxres = 0; - sql = NULL; + cur = maxres = 0; *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) { + outbit = TYPE_Nd; for (indexbit = 0, iterbit = 1; indexbit < mansearch_keymax; indexbit++, iterbit <<= 1) { @@ -205,22 +190,22 @@ mansearch(const struct mansearch *search, break; } } - } + } else + outbit = 0; /* - * Save a descriptor to the current working directory. - * Since pathnames in the "paths" variable might be relative, - * and we'll be chdir()ing into them, we need to keep a handle - * on our current directory from which to start the chdir(). + * Remember the original working directory, if possible. + * This will be needed if the second or a later directory + * is given as a relative path. + * 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)) { - perror("getcwd"); - goto out; - } else if (-1 == (fd = open(buf, O_RDONLY, 0))) { - perror(buf); - goto out; - } + if (getcwd(buf, PATH_MAX) == NULL) { + getcwd_status = 0; + (void)strlcpy(buf, strerror(errno), sizeof(buf)); + } else + getcwd_status = 1; sql = sql_statement(e); @@ -232,22 +217,28 @@ mansearch(const struct mansearch *search, * scan it for our match expression. */ + chdir_status = 0; for (i = 0; i < paths->sz; i++) { - if (-1 == fchdir(fd)) { - perror(buf); - free(*res); - break; - } else if (-1 == chdir(paths->paths[i])) { + if (chdir_status && paths->paths[i][0] != '/') { + if ( ! getcwd_status) { + warnx("%s: getcwd: %s", paths->paths[i], buf); + continue; + } else if (chdir(buf) == -1) { + perror(buf); + continue; + } + } + if (chdir(paths->paths[i]) == -1) { perror(paths->paths[i]); continue; } + chdir_status = 1; c = sqlite3_open_v2(MANDOC_DB, &db, SQLITE_OPEN_READONLY, NULL); if (SQLITE_OK != c) { - fprintf(stderr, "%s/%s: %s\n", - paths->paths[i], MANDOC_DB, strerror(errno)); + warn("%s/%s", paths->paths[i], MANDOC_DB); sqlite3_close(db); continue; } @@ -269,7 +260,7 @@ mansearch(const struct mansearch *search, j = 1; c = sqlite3_prepare_v2(db, sql, -1, &s, NULL); if (SQLITE_OK != c) - fprintf(stderr, "%s\n", sqlite3_errmsg(db)); + warnx("%s", sqlite3_errmsg(db)); for (ep = e; NULL != ep; ep = ep->next) { if (NULL == ep->substr) { @@ -280,8 +271,7 @@ mansearch(const struct mansearch *search, SQL_BIND_INT64(db, s, j, ep->bits); } - memset(&htab, 0, sizeof(struct ohash)); - ohash_init(&htab, 4, &info); + mandoc_ohash_init(&htab, 4, offsetof(struct match, pageid)); /* * Hash each entry on its [unique] document identifier. @@ -311,7 +301,7 @@ mansearch(const struct mansearch *search, } if (SQLITE_DONE != c) - fprintf(stderr, "%s\n", sqlite3_errmsg(db)); + warnx("%s", sqlite3_errmsg(db)); sqlite3_finalize(s); @@ -320,14 +310,14 @@ mansearch(const struct mansearch *search, "WHERE pageid=? ORDER BY sec, arch, name", -1, &s, NULL); if (SQLITE_OK != c) - fprintf(stderr, "%s\n", sqlite3_errmsg(db)); + warnx("%s", sqlite3_errmsg(db)); c = sqlite3_prepare_v2(db, "SELECT bits, key, pageid FROM keys " "WHERE pageid=? AND bits & ?", -1, &s2, NULL); if (SQLITE_OK != c) - fprintf(stderr, "%s\n", sqlite3_errmsg(db)); + warnx("%s", sqlite3_errmsg(db)); for (mp = ohash_first(&htab, &idx); NULL != mp; @@ -368,17 +358,12 @@ mansearch(const struct mansearch *search, break; } qsort(*res, cur, sizeof(struct manpage), manpage_compare); - rc = 1; -out: - if (-1 != fd) { - if (-1 == fchdir(fd)) - perror(buf); - close(fd); - } + if (chdir_status && getcwd_status && chdir(buf) == -1) + perror(buf); exprfree(e); free(sql); *sz = cur; - return(rc); + return 1; } void @@ -402,9 +387,9 @@ manpage_compare(const void *vp1, const void *vp2) mp1 = vp1; mp2 = vp2; - return( (diff = mp2->bits - mp1->bits) ? diff : - (diff = mp1->sec - mp2->sec) ? diff : - strcasecmp(mp1->names, mp2->names)); + return (diff = mp2->bits - mp1->bits) ? diff : + (diff = mp1->sec - mp2->sec) ? diff : + strcasecmp(mp1->names, mp2->names); } static void @@ -412,14 +397,15 @@ buildnames(const struct mansearch *search, struct manp sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, const char *path, int form) { - char *newnames, *prevsec, *prevarch; + glob_t globinfo; + char *firstname, *newnames, *prevsec, *prevarch; const char *oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec; size_t i; - int c; + int c, globres; mpage->file = NULL; mpage->names = NULL; - prevsec = prevarch = NULL; + firstname = prevsec = prevarch = NULL; i = 1; SQL_BIND_INT64(db, s, i, pageid); while (SQLITE_ROW == (c = sqlite3_step(s))) { @@ -494,11 +480,34 @@ buildnames(const struct mansearch *search, struct manp sep2 = *arch == '\0' ? "" : "/"; mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s", path, sep1, sec, sep2, arch, name, fsec); + if (access(mpage->file, R_OK) != -1) + continue; + + /* Handle unusual file name extensions. */ + + if (firstname == NULL) + firstname = mpage->file; + else + free(mpage->file); + mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.*", + path, sep1, sec, sep2, arch, name); + globres = glob(mpage->file, 0, NULL, &globinfo); + free(mpage->file); + mpage->file = globres ? NULL : + mandoc_strdup(*globinfo.gl_pathv); + globfree(&globinfo); } if (c != SQLITE_DONE) - fprintf(stderr, "%s\n", sqlite3_errmsg(db)); + warnx("%s", sqlite3_errmsg(db)); sqlite3_reset(s); + /* If none of the files is usable, use the first name. */ + + if (mpage->file == NULL) + mpage->file = firstname; + else if (mpage->file != firstname) + free(firstname); + /* Append one final section to the names. */ if (prevsec != NULL) { @@ -539,9 +548,9 @@ buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pag output = newoutput; } if (SQLITE_DONE != c) - fprintf(stderr, "%s\n", sqlite3_errmsg(db)); + warnx("%s", sqlite3_errmsg(db)); sqlite3_reset(s); - return(output); + return output; } /* @@ -636,7 +645,7 @@ sql_statement(const struct expr *e) needop = 1; } - return(sql); + return sql; } /* @@ -719,12 +728,12 @@ exprcomp(const struct mansearch *search, int argc, cha toopen = logic = igncase = 0; } if ( ! (toopen || logic || igncase || toclose)) - return(first); + return first; fail: if (NULL != first) exprfree(first); - return(NULL); + return NULL; } static struct expr * @@ -737,7 +746,7 @@ exprterm(const struct mansearch *search, char *buf, in int i, irc; if ('\0' == *buf) - return(NULL); + return NULL; e = mandoc_calloc(1, sizeof(struct expr)); @@ -745,7 +754,7 @@ exprterm(const struct mansearch *search, char *buf, in e->bits = TYPE_Nm; e->substr = buf; e->equal = 1; - return(e); + return e; } /* @@ -757,7 +766,14 @@ exprterm(const struct mansearch *search, char *buf, in if (search->argmode == ARG_WORD) { e->bits = TYPE_Nm; e->substr = NULL; +#if HAVE_REWB_BSD 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; } else if ((val = strpbrk(buf, "=~")) == NULL) { e->bits = TYPE_Nm | TYPE_Nd; @@ -781,14 +797,14 @@ exprterm(const struct mansearch *search, char *buf, in free(val); if (irc) { regerror(irc, &e->regexp, errbuf, sizeof(errbuf)); - fprintf(stderr, "regcomp: %s\n", errbuf); + warnx("regcomp: %s", errbuf); free(e); - return(NULL); + return NULL; } } if (e->bits) - return(e); + return e; /* * Parse out all possible fields. @@ -810,13 +826,13 @@ exprterm(const struct mansearch *search, char *buf, in if (i == mansearch_keymax) { if (strcasecmp(key, "any")) { free(e); - return(NULL); + return NULL; } e->bits |= ~0ULL; } } - return(e); + return e; } static void @@ -829,25 +845,4 @@ exprfree(struct expr *p) free(p); 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); }