mansearch.c revision 1.44
1/*	$OpenBSD: mansearch.c,v 1.44 2015/04/01 12:48:00 schwarze Exp $ */
2/*
3 * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/mman.h>
20#include <sys/types.h>
21
22#include <assert.h>
23#include <errno.h>
24#include <fcntl.h>
25#include <getopt.h>
26#include <glob.h>
27#include <limits.h>
28#include <regex.h>
29#include <stdio.h>
30#include <stdint.h>
31#include <stddef.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35
36#include <ohash.h>
37#include <sqlite3.h>
38
39#include "mandoc.h"
40#include "mandoc_aux.h"
41#include "manconf.h"
42#include "mansearch.h"
43
44extern int mansearch_keymax;
45extern const char *const mansearch_keynames[];
46
47#define	SQL_BIND_TEXT(_db, _s, _i, _v) \
48	do { if (SQLITE_OK != sqlite3_bind_text \
49		((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \
50		fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
51	} while (0)
52#define	SQL_BIND_INT64(_db, _s, _i, _v) \
53	do { if (SQLITE_OK != sqlite3_bind_int64 \
54		((_s), (_i)++, (_v))) \
55		fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
56	} while (0)
57#define	SQL_BIND_BLOB(_db, _s, _i, _v) \
58	do { if (SQLITE_OK != sqlite3_bind_blob \
59		((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \
60		fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \
61	} while (0)
62
63struct	expr {
64	regex_t		 regexp;  /* compiled regexp, if applicable */
65	const char	*substr;  /* to search for, if applicable */
66	struct expr	*next;    /* next in sequence */
67	uint64_t	 bits;    /* type-mask */
68	int		 equal;   /* equality, not subsring match */
69	int		 open;    /* opening parentheses before */
70	int		 and;	  /* logical AND before */
71	int		 close;   /* closing parentheses after */
72};
73
74struct	match {
75	uint64_t	 pageid; /* identifier in database */
76	uint64_t	 bits; /* name type mask */
77	char		*desc; /* manual page description */
78	int		 form; /* bit field: formatted, zipped? */
79};
80
81static	void		 buildnames(const struct mansearch *,
82				struct manpage *, sqlite3 *,
83				sqlite3_stmt *, uint64_t,
84				const char *, int form);
85static	char		*buildoutput(sqlite3 *, sqlite3_stmt *,
86				 uint64_t, uint64_t);
87static	void		*hash_alloc(size_t, void *);
88static	void		 hash_free(void *, void *);
89static	void		*hash_calloc(size_t, size_t, void *);
90static	struct expr	*exprcomp(const struct mansearch *,
91				int, char *[]);
92static	void		 exprfree(struct expr *);
93static	struct expr	*exprterm(const struct mansearch *, char *, int);
94static	int		 manpage_compare(const void *, const void *);
95static	void		 sql_append(char **sql, size_t *sz,
96				const char *newstr, int count);
97static	void		 sql_match(sqlite3_context *context,
98				int argc, sqlite3_value **argv);
99static	void		 sql_regexp(sqlite3_context *context,
100				int argc, sqlite3_value **argv);
101static	char		*sql_statement(const struct expr *);
102
103
104int
105mansearch_setup(int start)
106{
107	static void	*pagecache;
108	int		 c;
109
110#define	PC_PAGESIZE	1280
111#define	PC_NUMPAGES	256
112
113	if (start) {
114		if (NULL != pagecache) {
115			fprintf(stderr, "pagecache already enabled\n");
116			return((int)MANDOCLEVEL_BADARG);
117		}
118
119		pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES,
120		    PROT_READ | PROT_WRITE,
121		    MAP_SHARED | MAP_ANON, -1, 0);
122
123		if (MAP_FAILED == pagecache) {
124			perror("mmap");
125			pagecache = NULL;
126			return((int)MANDOCLEVEL_SYSERR);
127		}
128
129		c = sqlite3_config(SQLITE_CONFIG_PAGECACHE,
130		    pagecache, PC_PAGESIZE, PC_NUMPAGES);
131
132		if (SQLITE_OK == c)
133			return((int)MANDOCLEVEL_OK);
134
135		fprintf(stderr, "pagecache: %s\n", sqlite3_errstr(c));
136
137	} else if (NULL == pagecache) {
138		fprintf(stderr, "pagecache missing\n");
139		return((int)MANDOCLEVEL_BADARG);
140	}
141
142	if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) {
143		perror("munmap");
144		pagecache = NULL;
145		return((int)MANDOCLEVEL_SYSERR);
146	}
147
148	pagecache = NULL;
149	return((int)MANDOCLEVEL_OK);
150}
151
152int
153mansearch(const struct mansearch *search,
154		const struct manpaths *paths,
155		int argc, char *argv[],
156		struct manpage **res, size_t *sz)
157{
158	int64_t		 pageid;
159	uint64_t	 outbit, iterbit;
160	char		 buf[PATH_MAX];
161	char		*sql;
162	struct manpage	*mpage;
163	struct expr	*e, *ep;
164	sqlite3		*db;
165	sqlite3_stmt	*s, *s2;
166	struct match	*mp;
167	struct ohash_info info;
168	struct ohash	 htab;
169	unsigned int	 idx;
170	size_t		 i, j, cur, maxres;
171	int		 c, chdir_status, getcwd_status, indexbit;
172
173	if (argc == 0 || (e = exprcomp(search, argc, argv)) == NULL) {
174		*sz = 0;
175		return(0);
176	}
177
178	info.calloc = hash_calloc;
179	info.alloc = hash_alloc;
180	info.free = hash_free;
181	info.key_offset = offsetof(struct match, pageid);
182
183	cur = maxres = 0;
184	*res = NULL;
185
186	if (NULL != search->outkey) {
187		outbit = TYPE_Nd;
188		for (indexbit = 0, iterbit = 1;
189		     indexbit < mansearch_keymax;
190		     indexbit++, iterbit <<= 1) {
191			if (0 == strcasecmp(search->outkey,
192			    mansearch_keynames[indexbit])) {
193				outbit = iterbit;
194				break;
195			}
196		}
197	} else
198		outbit = 0;
199
200	/*
201	 * Remember the original working directory, if possible.
202	 * This will be needed if the second or a later directory
203	 * is given as a relative path.
204	 * Do not error out if the current directory is not
205	 * searchable: Maybe it won't be needed after all.
206	 */
207
208	if (getcwd(buf, PATH_MAX) == NULL) {
209		getcwd_status = 0;
210		(void)strlcpy(buf, strerror(errno), sizeof(buf));
211	} else
212		getcwd_status = 1;
213
214	sql = sql_statement(e);
215
216	/*
217	 * Loop over the directories (containing databases) for us to
218	 * search.
219	 * Don't let missing/bad databases/directories phase us.
220	 * In each, try to open the resident database and, if it opens,
221	 * scan it for our match expression.
222	 */
223
224	chdir_status = 0;
225	for (i = 0; i < paths->sz; i++) {
226		if (chdir_status && paths->paths[i][0] != '/') {
227			if ( ! getcwd_status) {
228				fprintf(stderr, "%s: getcwd: %s\n",
229				    paths->paths[i], buf);
230				continue;
231			} else if (chdir(buf) == -1) {
232				perror(buf);
233				continue;
234			}
235		}
236		if (chdir(paths->paths[i]) == -1) {
237			perror(paths->paths[i]);
238			continue;
239		}
240		chdir_status = 1;
241
242		c = sqlite3_open_v2(MANDOC_DB, &db,
243		    SQLITE_OPEN_READONLY, NULL);
244
245		if (SQLITE_OK != c) {
246			fprintf(stderr, "%s/%s: %s\n",
247			    paths->paths[i], MANDOC_DB, strerror(errno));
248			sqlite3_close(db);
249			continue;
250		}
251
252		/*
253		 * Define the SQL functions for substring
254		 * and regular expression matching.
255		 */
256
257		c = sqlite3_create_function(db, "match", 2,
258		    SQLITE_UTF8 | SQLITE_DETERMINISTIC,
259		    NULL, sql_match, NULL, NULL);
260		assert(SQLITE_OK == c);
261		c = sqlite3_create_function(db, "regexp", 2,
262		    SQLITE_UTF8 | SQLITE_DETERMINISTIC,
263		    NULL, sql_regexp, NULL, NULL);
264		assert(SQLITE_OK == c);
265
266		j = 1;
267		c = sqlite3_prepare_v2(db, sql, -1, &s, NULL);
268		if (SQLITE_OK != c)
269			fprintf(stderr, "%s\n", sqlite3_errmsg(db));
270
271		for (ep = e; NULL != ep; ep = ep->next) {
272			if (NULL == ep->substr) {
273				SQL_BIND_BLOB(db, s, j, ep->regexp);
274			} else
275				SQL_BIND_TEXT(db, s, j, ep->substr);
276			if (0 == ((TYPE_Nd | TYPE_Nm) & ep->bits))
277				SQL_BIND_INT64(db, s, j, ep->bits);
278		}
279
280		memset(&htab, 0, sizeof(struct ohash));
281		ohash_init(&htab, 4, &info);
282
283		/*
284		 * Hash each entry on its [unique] document identifier.
285		 * This is a uint64_t.
286		 * Instead of using a hash function, simply convert the
287		 * uint64_t to a uint32_t, the hash value's type.
288		 * This gives good performance and preserves the
289		 * distribution of buckets in the table.
290		 */
291		while (SQLITE_ROW == (c = sqlite3_step(s))) {
292			pageid = sqlite3_column_int64(s, 2);
293			idx = ohash_lookup_memory(&htab,
294			    (char *)&pageid, sizeof(uint64_t),
295			    (uint32_t)pageid);
296
297			if (NULL != ohash_find(&htab, idx))
298				continue;
299
300			mp = mandoc_calloc(1, sizeof(struct match));
301			mp->pageid = pageid;
302			mp->form = sqlite3_column_int(s, 1);
303			mp->bits = sqlite3_column_int64(s, 3);
304			if (TYPE_Nd == outbit)
305				mp->desc = mandoc_strdup((const char *)
306				    sqlite3_column_text(s, 0));
307			ohash_insert(&htab, idx, mp);
308		}
309
310		if (SQLITE_DONE != c)
311			fprintf(stderr, "%s\n", sqlite3_errmsg(db));
312
313		sqlite3_finalize(s);
314
315		c = sqlite3_prepare_v2(db,
316		    "SELECT sec, arch, name, pageid FROM mlinks "
317		    "WHERE pageid=? ORDER BY sec, arch, name",
318		    -1, &s, NULL);
319		if (SQLITE_OK != c)
320			fprintf(stderr, "%s\n", sqlite3_errmsg(db));
321
322		c = sqlite3_prepare_v2(db,
323		    "SELECT bits, key, pageid FROM keys "
324		    "WHERE pageid=? AND bits & ?",
325		    -1, &s2, NULL);
326		if (SQLITE_OK != c)
327			fprintf(stderr, "%s\n", sqlite3_errmsg(db));
328
329		for (mp = ohash_first(&htab, &idx);
330				NULL != mp;
331				mp = ohash_next(&htab, &idx)) {
332			if (cur + 1 > maxres) {
333				maxres += 1024;
334				*res = mandoc_reallocarray(*res,
335				    maxres, sizeof(struct manpage));
336			}
337			mpage = *res + cur;
338			mpage->ipath = i;
339			mpage->bits = mp->bits;
340			mpage->sec = 10;
341			mpage->form = mp->form;
342			buildnames(search, mpage, db, s, mp->pageid,
343			    paths->paths[i], mp->form);
344			if (mpage->names != NULL) {
345				mpage->output = TYPE_Nd & outbit ?
346				    mp->desc : outbit ?
347				    buildoutput(db, s2, mp->pageid, outbit) :
348				    NULL;
349				cur++;
350			}
351			free(mp);
352		}
353
354		sqlite3_finalize(s);
355		sqlite3_finalize(s2);
356		sqlite3_close(db);
357		ohash_delete(&htab);
358
359		/*
360		 * In man(1) mode, prefer matches in earlier trees
361		 * over matches in later trees.
362		 */
363
364		if (cur && search->firstmatch)
365			break;
366	}
367	qsort(*res, cur, sizeof(struct manpage), manpage_compare);
368	if (chdir_status && getcwd_status && chdir(buf) == -1)
369		perror(buf);
370	exprfree(e);
371	free(sql);
372	*sz = cur;
373	return(1);
374}
375
376void
377mansearch_free(struct manpage *res, size_t sz)
378{
379	size_t	 i;
380
381	for (i = 0; i < sz; i++) {
382		free(res[i].file);
383		free(res[i].names);
384		free(res[i].output);
385	}
386	free(res);
387}
388
389static int
390manpage_compare(const void *vp1, const void *vp2)
391{
392	const struct manpage	*mp1, *mp2;
393	int			 diff;
394
395	mp1 = vp1;
396	mp2 = vp2;
397	return(	(diff = mp2->bits - mp1->bits) ? diff :
398		(diff = mp1->sec - mp2->sec) ? diff :
399		strcasecmp(mp1->names, mp2->names));
400}
401
402static void
403buildnames(const struct mansearch *search, struct manpage *mpage,
404		sqlite3 *db, sqlite3_stmt *s,
405		uint64_t pageid, const char *path, int form)
406{
407	glob_t		 globinfo;
408	char		*firstname, *newnames, *prevsec, *prevarch;
409	const char	*oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec;
410	size_t		 i;
411	int		 c, globres;
412
413	mpage->file = NULL;
414	mpage->names = NULL;
415	firstname = prevsec = prevarch = NULL;
416	i = 1;
417	SQL_BIND_INT64(db, s, i, pageid);
418	while (SQLITE_ROW == (c = sqlite3_step(s))) {
419
420		/* Decide whether we already have some names. */
421
422		if (NULL == mpage->names) {
423			oldnames = "";
424			sep1 = "";
425		} else {
426			oldnames = mpage->names;
427			sep1 = ", ";
428		}
429
430		/* Fetch the next name, rejecting sec/arch mismatches. */
431
432		sec = (const char *)sqlite3_column_text(s, 0);
433		if (search->sec != NULL && strcasecmp(sec, search->sec))
434			continue;
435		arch = (const char *)sqlite3_column_text(s, 1);
436		if (search->arch != NULL && *arch != '\0' &&
437		    strcasecmp(arch, search->arch))
438			continue;
439		name = (const char *)sqlite3_column_text(s, 2);
440
441		/* Remember the first section found. */
442
443		if (9 < mpage->sec && '1' <= *sec && '9' >= *sec)
444			mpage->sec = (*sec - '1') + 1;
445
446		/* If the section changed, append the old one. */
447
448		if (NULL != prevsec &&
449		    (strcmp(sec, prevsec) ||
450		     strcmp(arch, prevarch))) {
451			sep2 = '\0' == *prevarch ? "" : "/";
452			mandoc_asprintf(&newnames, "%s(%s%s%s)",
453			    oldnames, prevsec, sep2, prevarch);
454			free(mpage->names);
455			oldnames = mpage->names = newnames;
456			free(prevsec);
457			free(prevarch);
458			prevsec = prevarch = NULL;
459		}
460
461		/* Save the new section, to append it later. */
462
463		if (NULL == prevsec) {
464			prevsec = mandoc_strdup(sec);
465			prevarch = mandoc_strdup(arch);
466		}
467
468		/* Append the new name. */
469
470		mandoc_asprintf(&newnames, "%s%s%s",
471		    oldnames, sep1, name);
472		free(mpage->names);
473		mpage->names = newnames;
474
475		/* Also save the first file name encountered. */
476
477		if (mpage->file != NULL)
478			continue;
479
480		if (form & FORM_SRC) {
481			sep1 = "man";
482			fsec = sec;
483		} else {
484			sep1 = "cat";
485			fsec = "0";
486		}
487		sep2 = *arch == '\0' ? "" : "/";
488		mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s",
489		    path, sep1, sec, sep2, arch, name, fsec);
490		if (access(mpage->file, R_OK) != -1)
491			continue;
492
493		/* Handle unusual file name extensions. */
494
495		if (firstname == NULL)
496			firstname = mpage->file;
497		else
498			free(mpage->file);
499		mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.*",
500		    path, sep1, sec, sep2, arch, name);
501		globres = glob(mpage->file, 0, NULL, &globinfo);
502		free(mpage->file);
503		mpage->file = globres ? NULL :
504		    mandoc_strdup(*globinfo.gl_pathv);
505		globfree(&globinfo);
506	}
507	if (c != SQLITE_DONE)
508		fprintf(stderr, "%s\n", sqlite3_errmsg(db));
509	sqlite3_reset(s);
510
511	/* If none of the files is usable, use the first name. */
512
513	if (mpage->file == NULL)
514		mpage->file = firstname;
515	else if (mpage->file != firstname)
516		free(firstname);
517
518	/* Append one final section to the names. */
519
520	if (prevsec != NULL) {
521		sep2 = *prevarch == '\0' ? "" : "/";
522		mandoc_asprintf(&newnames, "%s(%s%s%s)",
523		    mpage->names, prevsec, sep2, prevarch);
524		free(mpage->names);
525		mpage->names = newnames;
526		free(prevsec);
527		free(prevarch);
528	}
529}
530
531static char *
532buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, uint64_t outbit)
533{
534	char		*output, *newoutput;
535	const char	*oldoutput, *sep1, *data;
536	size_t		 i;
537	int		 c;
538
539	output = NULL;
540	i = 1;
541	SQL_BIND_INT64(db, s, i, pageid);
542	SQL_BIND_INT64(db, s, i, outbit);
543	while (SQLITE_ROW == (c = sqlite3_step(s))) {
544		if (NULL == output) {
545			oldoutput = "";
546			sep1 = "";
547		} else {
548			oldoutput = output;
549			sep1 = " # ";
550		}
551		data = (const char *)sqlite3_column_text(s, 1);
552		mandoc_asprintf(&newoutput, "%s%s%s",
553		    oldoutput, sep1, data);
554		free(output);
555		output = newoutput;
556	}
557	if (SQLITE_DONE != c)
558		fprintf(stderr, "%s\n", sqlite3_errmsg(db));
559	sqlite3_reset(s);
560	return(output);
561}
562
563/*
564 * Implement substring match as an application-defined SQL function.
565 * Using the SQL LIKE or GLOB operators instead would be a bad idea
566 * because that would require escaping metacharacters in the string
567 * being searched for.
568 */
569static void
570sql_match(sqlite3_context *context, int argc, sqlite3_value **argv)
571{
572
573	assert(2 == argc);
574	sqlite3_result_int(context, NULL != strcasestr(
575	    (const char *)sqlite3_value_text(argv[1]),
576	    (const char *)sqlite3_value_text(argv[0])));
577}
578
579/*
580 * Implement regular expression match
581 * as an application-defined SQL function.
582 */
583static void
584sql_regexp(sqlite3_context *context, int argc, sqlite3_value **argv)
585{
586
587	assert(2 == argc);
588	sqlite3_result_int(context, !regexec(
589	    (regex_t *)sqlite3_value_blob(argv[0]),
590	    (const char *)sqlite3_value_text(argv[1]),
591	    0, NULL, 0));
592}
593
594static void
595sql_append(char **sql, size_t *sz, const char *newstr, int count)
596{
597	size_t		 newsz;
598
599	newsz = 1 < count ? (size_t)count : strlen(newstr);
600	*sql = mandoc_realloc(*sql, *sz + newsz + 1);
601	if (1 < count)
602		memset(*sql + *sz, *newstr, (size_t)count);
603	else
604		memcpy(*sql + *sz, newstr, newsz);
605	*sz += newsz;
606	(*sql)[*sz] = '\0';
607}
608
609/*
610 * Prepare the search SQL statement.
611 */
612static char *
613sql_statement(const struct expr *e)
614{
615	char		*sql;
616	size_t		 sz;
617	int		 needop;
618
619	sql = mandoc_strdup(e->equal ?
620	    "SELECT desc, form, pageid, bits "
621		"FROM mpages NATURAL JOIN names WHERE " :
622	    "SELECT desc, form, pageid, 0 FROM mpages WHERE ");
623	sz = strlen(sql);
624
625	for (needop = 0; NULL != e; e = e->next) {
626		if (e->and)
627			sql_append(&sql, &sz, " AND ", 1);
628		else if (needop)
629			sql_append(&sql, &sz, " OR ", 1);
630		if (e->open)
631			sql_append(&sql, &sz, "(", e->open);
632		sql_append(&sql, &sz,
633		    TYPE_Nd & e->bits
634		    ? (NULL == e->substr
635			? "desc REGEXP ?"
636			: "desc MATCH ?")
637		    : TYPE_Nm == e->bits
638		    ? (NULL == e->substr
639			? "pageid IN (SELECT pageid FROM names "
640			  "WHERE name REGEXP ?)"
641			: e->equal
642			? "name = ? "
643			: "pageid IN (SELECT pageid FROM names "
644			  "WHERE name MATCH ?)")
645		    : (NULL == e->substr
646			? "pageid IN (SELECT pageid FROM keys "
647			  "WHERE key REGEXP ? AND bits & ?)"
648			: "pageid IN (SELECT pageid FROM keys "
649			  "WHERE key MATCH ? AND bits & ?)"), 1);
650		if (e->close)
651			sql_append(&sql, &sz, ")", e->close);
652		needop = 1;
653	}
654
655	return(sql);
656}
657
658/*
659 * Compile a set of string tokens into an expression.
660 * Tokens in "argv" are assumed to be individual expression atoms (e.g.,
661 * "(", "foo=bar", etc.).
662 */
663static struct expr *
664exprcomp(const struct mansearch *search, int argc, char *argv[])
665{
666	uint64_t	 mask;
667	int		 i, toopen, logic, igncase, toclose;
668	struct expr	*first, *prev, *cur, *next;
669
670	first = cur = NULL;
671	logic = igncase = toopen = toclose = 0;
672
673	for (i = 0; i < argc; i++) {
674		if (0 == strcmp("(", argv[i])) {
675			if (igncase)
676				goto fail;
677			toopen++;
678			toclose++;
679			continue;
680		} else if (0 == strcmp(")", argv[i])) {
681			if (toopen || logic || igncase || NULL == cur)
682				goto fail;
683			cur->close++;
684			if (0 > --toclose)
685				goto fail;
686			continue;
687		} else if (0 == strcmp("-a", argv[i])) {
688			if (toopen || logic || igncase || NULL == cur)
689				goto fail;
690			logic = 1;
691			continue;
692		} else if (0 == strcmp("-o", argv[i])) {
693			if (toopen || logic || igncase || NULL == cur)
694				goto fail;
695			logic = 2;
696			continue;
697		} else if (0 == strcmp("-i", argv[i])) {
698			if (igncase)
699				goto fail;
700			igncase = 1;
701			continue;
702		}
703		next = exprterm(search, argv[i], !igncase);
704		if (NULL == next)
705			goto fail;
706		if (NULL == first)
707			first = next;
708		else
709			cur->next = next;
710		prev = cur = next;
711
712		/*
713		 * Searching for descriptions must be split out
714		 * because they are stored in the mpages table,
715		 * not in the keys table.
716		 */
717
718		for (mask = TYPE_Nm; mask <= TYPE_Nd; mask <<= 1) {
719			if (mask & cur->bits && ~mask & cur->bits) {
720				next = mandoc_calloc(1,
721				    sizeof(struct expr));
722				memcpy(next, cur, sizeof(struct expr));
723				prev->open = 1;
724				cur->bits = mask;
725				cur->next = next;
726				cur = next;
727				cur->bits &= ~mask;
728			}
729		}
730		prev->and = (1 == logic);
731		prev->open += toopen;
732		if (cur != prev)
733			cur->close = 1;
734
735		toopen = logic = igncase = 0;
736	}
737	if ( ! (toopen || logic || igncase || toclose))
738		return(first);
739
740fail:
741	if (NULL != first)
742		exprfree(first);
743	return(NULL);
744}
745
746static struct expr *
747exprterm(const struct mansearch *search, char *buf, int cs)
748{
749	char		 errbuf[BUFSIZ];
750	struct expr	*e;
751	char		*key, *val;
752	uint64_t	 iterbit;
753	int		 i, irc;
754
755	if ('\0' == *buf)
756		return(NULL);
757
758	e = mandoc_calloc(1, sizeof(struct expr));
759
760	if (search->argmode == ARG_NAME) {
761		e->bits = TYPE_Nm;
762		e->substr = buf;
763		e->equal = 1;
764		return(e);
765	}
766
767	/*
768	 * Separate macro keys from search string.
769	 * If needed, request regular expression handling
770	 * by setting e->substr to NULL.
771	 */
772
773	if (search->argmode == ARG_WORD) {
774		e->bits = TYPE_Nm;
775		e->substr = NULL;
776		mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf);
777		cs = 0;
778	} else if ((val = strpbrk(buf, "=~")) == NULL) {
779		e->bits = TYPE_Nm | TYPE_Nd;
780		e->substr = buf;
781	} else {
782		if (val == buf)
783			e->bits = TYPE_Nm | TYPE_Nd;
784		if ('=' == *val)
785			e->substr = val + 1;
786		*val++ = '\0';
787		if (NULL != strstr(buf, "arch"))
788			cs = 0;
789	}
790
791	/* Compile regular expressions. */
792
793	if (NULL == e->substr) {
794		irc = regcomp(&e->regexp, val,
795		    REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
796		if (search->argmode == ARG_WORD)
797			free(val);
798		if (irc) {
799			regerror(irc, &e->regexp, errbuf, sizeof(errbuf));
800			fprintf(stderr, "regcomp: %s\n", errbuf);
801			free(e);
802			return(NULL);
803		}
804	}
805
806	if (e->bits)
807		return(e);
808
809	/*
810	 * Parse out all possible fields.
811	 * If the field doesn't resolve, bail.
812	 */
813
814	while (NULL != (key = strsep(&buf, ","))) {
815		if ('\0' == *key)
816			continue;
817		for (i = 0, iterbit = 1;
818		     i < mansearch_keymax;
819		     i++, iterbit <<= 1) {
820			if (0 == strcasecmp(key,
821			    mansearch_keynames[i])) {
822				e->bits |= iterbit;
823				break;
824			}
825		}
826		if (i == mansearch_keymax) {
827			if (strcasecmp(key, "any")) {
828				free(e);
829				return(NULL);
830			}
831			e->bits |= ~0ULL;
832		}
833	}
834
835	return(e);
836}
837
838static void
839exprfree(struct expr *p)
840{
841	struct expr	*pp;
842
843	while (NULL != p) {
844		pp = p->next;
845		free(p);
846		p = pp;
847	}
848}
849
850static void *
851hash_calloc(size_t nmemb, size_t sz, void *arg)
852{
853
854	return(mandoc_calloc(nmemb, sz));
855}
856
857static void *
858hash_alloc(size_t sz, void *arg)
859{
860
861	return(mandoc_malloc(sz));
862}
863
864static void
865hash_free(void *p, void *arg)
866{
867
868	free(p);
869}
870