1*99db7d0eSSascha Wildner /* $Id: mansearch.c,v 1.82 2019/07/01 22:56:24 schwarze Exp $ */
2070c62a6SFranco Fichtner /*
3070c62a6SFranco Fichtner * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
454ba9607SSascha Wildner * Copyright (c) 2013-2018 Ingo Schwarze <schwarze@openbsd.org>
5070c62a6SFranco Fichtner *
6070c62a6SFranco Fichtner * Permission to use, copy, modify, and distribute this software for any
7070c62a6SFranco Fichtner * purpose with or without fee is hereby granted, provided that the above
8070c62a6SFranco Fichtner * copyright notice and this permission notice appear in all copies.
9070c62a6SFranco Fichtner *
1054ba9607SSascha Wildner * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11070c62a6SFranco Fichtner * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1254ba9607SSascha Wildner * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13070c62a6SFranco Fichtner * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14070c62a6SFranco Fichtner * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15070c62a6SFranco Fichtner * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16070c62a6SFranco Fichtner * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17070c62a6SFranco Fichtner */
18070c62a6SFranco Fichtner #include "config.h"
19070c62a6SFranco Fichtner
20070c62a6SFranco Fichtner #include <sys/mman.h>
2154ba9607SSascha Wildner #include <sys/types.h>
2254ba9607SSascha Wildner
23070c62a6SFranco Fichtner #include <assert.h>
2454ba9607SSascha Wildner #if HAVE_ERR
2554ba9607SSascha Wildner #include <err.h>
2654ba9607SSascha Wildner #endif
2754ba9607SSascha Wildner #include <errno.h>
28070c62a6SFranco Fichtner #include <fcntl.h>
2954ba9607SSascha Wildner #include <glob.h>
30070c62a6SFranco Fichtner #include <limits.h>
31070c62a6SFranco Fichtner #include <regex.h>
32070c62a6SFranco Fichtner #include <stdio.h>
33070c62a6SFranco Fichtner #include <stdint.h>
34070c62a6SFranco Fichtner #include <stddef.h>
35070c62a6SFranco Fichtner #include <stdlib.h>
36070c62a6SFranco Fichtner #include <string.h>
37070c62a6SFranco Fichtner #include <unistd.h>
38070c62a6SFranco Fichtner
39070c62a6SFranco Fichtner #include "mandoc_aux.h"
4054ba9607SSascha Wildner #include "mandoc_ohash.h"
4154ba9607SSascha Wildner #include "manconf.h"
42070c62a6SFranco Fichtner #include "mansearch.h"
4354ba9607SSascha Wildner #include "dbm.h"
44070c62a6SFranco Fichtner
45070c62a6SFranco Fichtner struct expr {
4654ba9607SSascha Wildner /* Used for terms: */
4754ba9607SSascha Wildner struct dbm_match match; /* Match type and expression. */
4854ba9607SSascha Wildner uint64_t bits; /* Type mask. */
4954ba9607SSascha Wildner /* Used for OR and AND groups: */
5054ba9607SSascha Wildner struct expr *next; /* Next child in the parent group. */
5154ba9607SSascha Wildner struct expr *child; /* First child in this group. */
5254ba9607SSascha Wildner enum { EXPR_TERM, EXPR_OR, EXPR_AND } type;
53070c62a6SFranco Fichtner };
54070c62a6SFranco Fichtner
5554ba9607SSascha Wildner const char *const mansearch_keynames[KEY_MAX] = {
5654ba9607SSascha Wildner "arch", "sec", "Xr", "Ar", "Fa", "Fl", "Dv", "Fn",
5754ba9607SSascha Wildner "Ic", "Pa", "Cm", "Li", "Em", "Cd", "Va", "Ft",
5854ba9607SSascha Wildner "Tn", "Er", "Ev", "Sy", "Sh", "In", "Ss", "Ox",
5954ba9607SSascha Wildner "An", "Mt", "St", "Bx", "At", "Nx", "Fx", "Lk",
6054ba9607SSascha Wildner "Ms", "Bsx", "Dx", "Rs", "Vt", "Lb", "Nm", "Nd"
61070c62a6SFranco Fichtner };
62070c62a6SFranco Fichtner
6354ba9607SSascha Wildner
6454ba9607SSascha Wildner static struct ohash *manmerge(struct expr *, struct ohash *);
6554ba9607SSascha Wildner static struct ohash *manmerge_term(struct expr *, struct ohash *);
6654ba9607SSascha Wildner static struct ohash *manmerge_or(struct expr *, struct ohash *);
6754ba9607SSascha Wildner static struct ohash *manmerge_and(struct expr *, struct ohash *);
6854ba9607SSascha Wildner static char *buildnames(const struct dbm_page *);
6954ba9607SSascha Wildner static char *buildoutput(size_t, struct dbm_page *);
7054ba9607SSascha Wildner static size_t lstlen(const char *, size_t);
7154ba9607SSascha Wildner static void lstcat(char *, size_t *, const char *, const char *);
7254ba9607SSascha Wildner static int lstmatch(const char *, const char *);
73070c62a6SFranco Fichtner static struct expr *exprcomp(const struct mansearch *,
7454ba9607SSascha Wildner int, char *[], int *);
7554ba9607SSascha Wildner static struct expr *expr_and(const struct mansearch *,
7654ba9607SSascha Wildner int, char *[], int *);
7754ba9607SSascha Wildner static struct expr *exprterm(const struct mansearch *,
7854ba9607SSascha Wildner int, char *[], int *);
79070c62a6SFranco Fichtner static void exprfree(struct expr *);
80070c62a6SFranco Fichtner static int manpage_compare(const void *, const void *);
81070c62a6SFranco Fichtner
82070c62a6SFranco Fichtner
83070c62a6SFranco Fichtner int
mansearch(const struct mansearch * search,const struct manpaths * paths,int argc,char * argv[],struct manpage ** res,size_t * sz)84070c62a6SFranco Fichtner mansearch(const struct mansearch *search,
85070c62a6SFranco Fichtner const struct manpaths *paths,
86070c62a6SFranco Fichtner int argc, char *argv[],
87070c62a6SFranco Fichtner struct manpage **res, size_t *sz)
88070c62a6SFranco Fichtner {
89070c62a6SFranco Fichtner char buf[PATH_MAX];
9054ba9607SSascha Wildner struct dbm_res *rp;
9154ba9607SSascha Wildner struct expr *e;
9254ba9607SSascha Wildner struct dbm_page *page;
93070c62a6SFranco Fichtner struct manpage *mpage;
9454ba9607SSascha Wildner struct ohash *htab;
9554ba9607SSascha Wildner size_t cur, i, maxres, outkey;
9654ba9607SSascha Wildner unsigned int slot;
9754ba9607SSascha Wildner int argi, chdir_status, getcwd_status, im;
98070c62a6SFranco Fichtner
9954ba9607SSascha Wildner argi = 0;
10054ba9607SSascha Wildner if ((e = exprcomp(search, argc, argv, &argi)) == NULL) {
10154ba9607SSascha Wildner *sz = 0;
10254ba9607SSascha Wildner return 0;
10354ba9607SSascha Wildner }
104070c62a6SFranco Fichtner
10554ba9607SSascha Wildner cur = maxres = 0;
10654ba9607SSascha Wildner if (res != NULL)
107070c62a6SFranco Fichtner *res = NULL;
108070c62a6SFranco Fichtner
10954ba9607SSascha Wildner outkey = KEY_Nd;
11054ba9607SSascha Wildner if (search->outkey != NULL)
11154ba9607SSascha Wildner for (im = 0; im < KEY_MAX; im++)
11254ba9607SSascha Wildner if (0 == strcasecmp(search->outkey,
11354ba9607SSascha Wildner mansearch_keynames[im])) {
11454ba9607SSascha Wildner outkey = im;
115070c62a6SFranco Fichtner break;
116070c62a6SFranco Fichtner }
117070c62a6SFranco Fichtner
118070c62a6SFranco Fichtner /*
11954ba9607SSascha Wildner * Remember the original working directory, if possible.
12054ba9607SSascha Wildner * This will be needed if the second or a later directory
12154ba9607SSascha Wildner * is given as a relative path.
12254ba9607SSascha Wildner * Do not error out if the current directory is not
12354ba9607SSascha Wildner * searchable: Maybe it won't be needed after all.
124070c62a6SFranco Fichtner */
125070c62a6SFranco Fichtner
12654ba9607SSascha Wildner if (getcwd(buf, PATH_MAX) == NULL) {
12754ba9607SSascha Wildner getcwd_status = 0;
12854ba9607SSascha Wildner (void)strlcpy(buf, strerror(errno), sizeof(buf));
12954ba9607SSascha Wildner } else
13054ba9607SSascha Wildner getcwd_status = 1;
131070c62a6SFranco Fichtner
132070c62a6SFranco Fichtner /*
133070c62a6SFranco Fichtner * Loop over the directories (containing databases) for us to
134070c62a6SFranco Fichtner * search.
135070c62a6SFranco Fichtner * Don't let missing/bad databases/directories phase us.
136070c62a6SFranco Fichtner * In each, try to open the resident database and, if it opens,
137070c62a6SFranco Fichtner * scan it for our match expression.
138070c62a6SFranco Fichtner */
139070c62a6SFranco Fichtner
14054ba9607SSascha Wildner chdir_status = 0;
141070c62a6SFranco Fichtner for (i = 0; i < paths->sz; i++) {
14254ba9607SSascha Wildner if (chdir_status && paths->paths[i][0] != '/') {
14354ba9607SSascha Wildner if ( ! getcwd_status) {
14454ba9607SSascha Wildner warnx("%s: getcwd: %s", paths->paths[i], buf);
14554ba9607SSascha Wildner continue;
14654ba9607SSascha Wildner } else if (chdir(buf) == -1) {
14754ba9607SSascha Wildner warn("%s", buf);
14854ba9607SSascha Wildner continue;
14954ba9607SSascha Wildner }
15054ba9607SSascha Wildner }
15154ba9607SSascha Wildner if (chdir(paths->paths[i]) == -1) {
15254ba9607SSascha Wildner warn("%s", paths->paths[i]);
15354ba9607SSascha Wildner continue;
15454ba9607SSascha Wildner }
15554ba9607SSascha Wildner chdir_status = 1;
15654ba9607SSascha Wildner
15754ba9607SSascha Wildner if (dbm_open(MANDOC_DB) == -1) {
15854ba9607SSascha Wildner if (errno != ENOENT)
15954ba9607SSascha Wildner warn("%s/%s", paths->paths[i], MANDOC_DB);
16054ba9607SSascha Wildner continue;
16154ba9607SSascha Wildner }
16254ba9607SSascha Wildner
16354ba9607SSascha Wildner if ((htab = manmerge(e, NULL)) == NULL) {
16454ba9607SSascha Wildner dbm_close();
16554ba9607SSascha Wildner continue;
16654ba9607SSascha Wildner }
16754ba9607SSascha Wildner
16854ba9607SSascha Wildner for (rp = ohash_first(htab, &slot); rp != NULL;
16954ba9607SSascha Wildner rp = ohash_next(htab, &slot)) {
17054ba9607SSascha Wildner page = dbm_page_get(rp->page);
17154ba9607SSascha Wildner
17254ba9607SSascha Wildner if (lstmatch(search->sec, page->sect) == 0 ||
17354ba9607SSascha Wildner lstmatch(search->arch, page->arch) == 0 ||
17454ba9607SSascha Wildner (search->argmode == ARG_NAME &&
17554ba9607SSascha Wildner rp->bits <= (int32_t)(NAME_SYN & NAME_MASK)))
17654ba9607SSascha Wildner continue;
17754ba9607SSascha Wildner
17854ba9607SSascha Wildner if (res == NULL) {
17954ba9607SSascha Wildner cur = 1;
180070c62a6SFranco Fichtner break;
181070c62a6SFranco Fichtner }
182070c62a6SFranco Fichtner if (cur + 1 > maxres) {
183070c62a6SFranco Fichtner maxres += 1024;
184070c62a6SFranco Fichtner *res = mandoc_reallocarray(*res,
18554ba9607SSascha Wildner maxres, sizeof(**res));
186070c62a6SFranco Fichtner }
187070c62a6SFranco Fichtner mpage = *res + cur;
18854ba9607SSascha Wildner mandoc_asprintf(&mpage->file, "%s/%s",
18954ba9607SSascha Wildner paths->paths[i], page->file + 1);
19054ba9607SSascha Wildner if (access(chdir_status ? page->file + 1 :
19154ba9607SSascha Wildner mpage->file, R_OK) == -1) {
19254ba9607SSascha Wildner warn("%s", mpage->file);
19354ba9607SSascha Wildner warnx("outdated mandoc.db contains "
19454ba9607SSascha Wildner "bogus %s entry, run makewhatis %s",
19554ba9607SSascha Wildner page->file + 1, paths->paths[i]);
19654ba9607SSascha Wildner free(mpage->file);
19754ba9607SSascha Wildner free(rp);
19854ba9607SSascha Wildner continue;
19954ba9607SSascha Wildner }
20054ba9607SSascha Wildner mpage->names = buildnames(page);
20154ba9607SSascha Wildner mpage->output = buildoutput(outkey, page);
202*99db7d0eSSascha Wildner mpage->bits = search->firstmatch ? rp->bits : 0;
20354ba9607SSascha Wildner mpage->ipath = i;
20454ba9607SSascha Wildner mpage->sec = *page->sect - '0';
20554ba9607SSascha Wildner if (mpage->sec < 0 || mpage->sec > 9)
206070c62a6SFranco Fichtner mpage->sec = 10;
20754ba9607SSascha Wildner mpage->form = *page->file;
20854ba9607SSascha Wildner free(rp);
209070c62a6SFranco Fichtner cur++;
210070c62a6SFranco Fichtner }
21154ba9607SSascha Wildner ohash_delete(htab);
21254ba9607SSascha Wildner free(htab);
21354ba9607SSascha Wildner dbm_close();
214070c62a6SFranco Fichtner
21554ba9607SSascha Wildner /*
21654ba9607SSascha Wildner * In man(1) mode, prefer matches in earlier trees
21754ba9607SSascha Wildner * over matches in later trees.
21854ba9607SSascha Wildner */
21954ba9607SSascha Wildner
22054ba9607SSascha Wildner if (cur && search->firstmatch)
22154ba9607SSascha Wildner break;
222070c62a6SFranco Fichtner }
22354ba9607SSascha Wildner if (res != NULL)
224070c62a6SFranco Fichtner qsort(*res, cur, sizeof(struct manpage), manpage_compare);
22554ba9607SSascha Wildner if (chdir_status && getcwd_status && chdir(buf) == -1)
22654ba9607SSascha Wildner warn("%s", buf);
227070c62a6SFranco Fichtner exprfree(e);
228070c62a6SFranco Fichtner *sz = cur;
22954ba9607SSascha Wildner return res != NULL || cur;
23054ba9607SSascha Wildner }
23154ba9607SSascha Wildner
23254ba9607SSascha Wildner /*
23354ba9607SSascha Wildner * Merge the results for the expression tree rooted at e
23454ba9607SSascha Wildner * into the the result list htab.
23554ba9607SSascha Wildner */
23654ba9607SSascha Wildner static struct ohash *
manmerge(struct expr * e,struct ohash * htab)23754ba9607SSascha Wildner manmerge(struct expr *e, struct ohash *htab)
23854ba9607SSascha Wildner {
23954ba9607SSascha Wildner switch (e->type) {
24054ba9607SSascha Wildner case EXPR_TERM:
24154ba9607SSascha Wildner return manmerge_term(e, htab);
24254ba9607SSascha Wildner case EXPR_OR:
24354ba9607SSascha Wildner return manmerge_or(e->child, htab);
24454ba9607SSascha Wildner case EXPR_AND:
24554ba9607SSascha Wildner return manmerge_and(e->child, htab);
24654ba9607SSascha Wildner default:
24754ba9607SSascha Wildner abort();
24854ba9607SSascha Wildner }
24954ba9607SSascha Wildner }
25054ba9607SSascha Wildner
25154ba9607SSascha Wildner static struct ohash *
manmerge_term(struct expr * e,struct ohash * htab)25254ba9607SSascha Wildner manmerge_term(struct expr *e, struct ohash *htab)
25354ba9607SSascha Wildner {
25454ba9607SSascha Wildner struct dbm_res res, *rp;
25554ba9607SSascha Wildner uint64_t ib;
25654ba9607SSascha Wildner unsigned int slot;
25754ba9607SSascha Wildner int im;
25854ba9607SSascha Wildner
25954ba9607SSascha Wildner if (htab == NULL) {
26054ba9607SSascha Wildner htab = mandoc_malloc(sizeof(*htab));
26154ba9607SSascha Wildner mandoc_ohash_init(htab, 4, offsetof(struct dbm_res, page));
26254ba9607SSascha Wildner }
26354ba9607SSascha Wildner
26454ba9607SSascha Wildner for (im = 0, ib = 1; im < KEY_MAX; im++, ib <<= 1) {
26554ba9607SSascha Wildner if ((e->bits & ib) == 0)
26654ba9607SSascha Wildner continue;
26754ba9607SSascha Wildner
26854ba9607SSascha Wildner switch (ib) {
26954ba9607SSascha Wildner case TYPE_arch:
27054ba9607SSascha Wildner dbm_page_byarch(&e->match);
27154ba9607SSascha Wildner break;
27254ba9607SSascha Wildner case TYPE_sec:
27354ba9607SSascha Wildner dbm_page_bysect(&e->match);
27454ba9607SSascha Wildner break;
27554ba9607SSascha Wildner case TYPE_Nm:
27654ba9607SSascha Wildner dbm_page_byname(&e->match);
27754ba9607SSascha Wildner break;
27854ba9607SSascha Wildner case TYPE_Nd:
27954ba9607SSascha Wildner dbm_page_bydesc(&e->match);
28054ba9607SSascha Wildner break;
28154ba9607SSascha Wildner default:
28254ba9607SSascha Wildner dbm_page_bymacro(im - 2, &e->match);
28354ba9607SSascha Wildner break;
28454ba9607SSascha Wildner }
28554ba9607SSascha Wildner
28654ba9607SSascha Wildner /*
28754ba9607SSascha Wildner * When hashing for deduplication, use the unique
28854ba9607SSascha Wildner * page ID itself instead of a hash function;
28954ba9607SSascha Wildner * that is quite efficient.
29054ba9607SSascha Wildner */
29154ba9607SSascha Wildner
29254ba9607SSascha Wildner for (;;) {
29354ba9607SSascha Wildner res = dbm_page_next();
29454ba9607SSascha Wildner if (res.page == -1)
29554ba9607SSascha Wildner break;
29654ba9607SSascha Wildner slot = ohash_lookup_memory(htab,
29754ba9607SSascha Wildner (char *)&res, sizeof(res.page), res.page);
298*99db7d0eSSascha Wildner if ((rp = ohash_find(htab, slot)) != NULL) {
299*99db7d0eSSascha Wildner rp->bits |= res.bits;
30054ba9607SSascha Wildner continue;
301*99db7d0eSSascha Wildner }
30254ba9607SSascha Wildner rp = mandoc_malloc(sizeof(*rp));
30354ba9607SSascha Wildner *rp = res;
30454ba9607SSascha Wildner ohash_insert(htab, slot, rp);
30554ba9607SSascha Wildner }
30654ba9607SSascha Wildner }
30754ba9607SSascha Wildner return htab;
30854ba9607SSascha Wildner }
30954ba9607SSascha Wildner
31054ba9607SSascha Wildner static struct ohash *
manmerge_or(struct expr * e,struct ohash * htab)31154ba9607SSascha Wildner manmerge_or(struct expr *e, struct ohash *htab)
31254ba9607SSascha Wildner {
31354ba9607SSascha Wildner while (e != NULL) {
31454ba9607SSascha Wildner htab = manmerge(e, htab);
31554ba9607SSascha Wildner e = e->next;
31654ba9607SSascha Wildner }
31754ba9607SSascha Wildner return htab;
31854ba9607SSascha Wildner }
31954ba9607SSascha Wildner
32054ba9607SSascha Wildner static struct ohash *
manmerge_and(struct expr * e,struct ohash * htab)32154ba9607SSascha Wildner manmerge_and(struct expr *e, struct ohash *htab)
32254ba9607SSascha Wildner {
32354ba9607SSascha Wildner struct ohash *hand, *h1, *h2;
32454ba9607SSascha Wildner struct dbm_res *res;
32554ba9607SSascha Wildner unsigned int slot1, slot2;
32654ba9607SSascha Wildner
32754ba9607SSascha Wildner /* Evaluate the first term of the AND clause. */
32854ba9607SSascha Wildner
32954ba9607SSascha Wildner hand = manmerge(e, NULL);
33054ba9607SSascha Wildner
33154ba9607SSascha Wildner while ((e = e->next) != NULL) {
33254ba9607SSascha Wildner
33354ba9607SSascha Wildner /* Evaluate the next term and prepare for ANDing. */
33454ba9607SSascha Wildner
33554ba9607SSascha Wildner h2 = manmerge(e, NULL);
33654ba9607SSascha Wildner if (ohash_entries(h2) < ohash_entries(hand)) {
33754ba9607SSascha Wildner h1 = h2;
33854ba9607SSascha Wildner h2 = hand;
33954ba9607SSascha Wildner } else
34054ba9607SSascha Wildner h1 = hand;
34154ba9607SSascha Wildner hand = mandoc_malloc(sizeof(*hand));
34254ba9607SSascha Wildner mandoc_ohash_init(hand, 4, offsetof(struct dbm_res, page));
34354ba9607SSascha Wildner
34454ba9607SSascha Wildner /* Keep all pages that are in both result sets. */
34554ba9607SSascha Wildner
34654ba9607SSascha Wildner for (res = ohash_first(h1, &slot1); res != NULL;
34754ba9607SSascha Wildner res = ohash_next(h1, &slot1)) {
34854ba9607SSascha Wildner if (ohash_find(h2, ohash_lookup_memory(h2,
34954ba9607SSascha Wildner (char *)res, sizeof(res->page),
35054ba9607SSascha Wildner res->page)) == NULL)
35154ba9607SSascha Wildner free(res);
35254ba9607SSascha Wildner else
35354ba9607SSascha Wildner ohash_insert(hand, ohash_lookup_memory(hand,
35454ba9607SSascha Wildner (char *)res, sizeof(res->page),
35554ba9607SSascha Wildner res->page), res);
35654ba9607SSascha Wildner }
35754ba9607SSascha Wildner
35854ba9607SSascha Wildner /* Discard the merged results. */
35954ba9607SSascha Wildner
36054ba9607SSascha Wildner for (res = ohash_first(h2, &slot2); res != NULL;
36154ba9607SSascha Wildner res = ohash_next(h2, &slot2))
36254ba9607SSascha Wildner free(res);
36354ba9607SSascha Wildner ohash_delete(h2);
36454ba9607SSascha Wildner free(h2);
36554ba9607SSascha Wildner ohash_delete(h1);
36654ba9607SSascha Wildner free(h1);
36754ba9607SSascha Wildner }
36854ba9607SSascha Wildner
36954ba9607SSascha Wildner /* Merge the result of the AND into htab. */
37054ba9607SSascha Wildner
37154ba9607SSascha Wildner if (htab == NULL)
37254ba9607SSascha Wildner return hand;
37354ba9607SSascha Wildner
37454ba9607SSascha Wildner for (res = ohash_first(hand, &slot1); res != NULL;
37554ba9607SSascha Wildner res = ohash_next(hand, &slot1)) {
37654ba9607SSascha Wildner slot2 = ohash_lookup_memory(htab,
37754ba9607SSascha Wildner (char *)res, sizeof(res->page), res->page);
37854ba9607SSascha Wildner if (ohash_find(htab, slot2) == NULL)
37954ba9607SSascha Wildner ohash_insert(htab, slot2, res);
38054ba9607SSascha Wildner else
38154ba9607SSascha Wildner free(res);
38254ba9607SSascha Wildner }
38354ba9607SSascha Wildner
38454ba9607SSascha Wildner /* Discard the merged result. */
38554ba9607SSascha Wildner
38654ba9607SSascha Wildner ohash_delete(hand);
38754ba9607SSascha Wildner free(hand);
38854ba9607SSascha Wildner return htab;
38954ba9607SSascha Wildner }
39054ba9607SSascha Wildner
39154ba9607SSascha Wildner void
mansearch_free(struct manpage * res,size_t sz)39254ba9607SSascha Wildner mansearch_free(struct manpage *res, size_t sz)
39354ba9607SSascha Wildner {
39454ba9607SSascha Wildner size_t i;
39554ba9607SSascha Wildner
39654ba9607SSascha Wildner for (i = 0; i < sz; i++) {
39754ba9607SSascha Wildner free(res[i].file);
39854ba9607SSascha Wildner free(res[i].names);
39954ba9607SSascha Wildner free(res[i].output);
40054ba9607SSascha Wildner }
40154ba9607SSascha Wildner free(res);
402070c62a6SFranco Fichtner }
403070c62a6SFranco Fichtner
404070c62a6SFranco Fichtner static int
manpage_compare(const void * vp1,const void * vp2)405070c62a6SFranco Fichtner manpage_compare(const void *vp1, const void *vp2)
406070c62a6SFranco Fichtner {
407070c62a6SFranco Fichtner const struct manpage *mp1, *mp2;
40854ba9607SSascha Wildner const char *cp1, *cp2;
40954ba9607SSascha Wildner size_t sz1, sz2;
410070c62a6SFranco Fichtner int diff;
411070c62a6SFranco Fichtner
412070c62a6SFranco Fichtner mp1 = vp1;
413070c62a6SFranco Fichtner mp2 = vp2;
414*99db7d0eSSascha Wildner if ((diff = mp2->bits - mp1->bits) ||
415*99db7d0eSSascha Wildner (diff = mp1->sec - mp2->sec))
41654ba9607SSascha Wildner return diff;
417070c62a6SFranco Fichtner
41854ba9607SSascha Wildner /* Fall back to alphabetic ordering of names. */
41954ba9607SSascha Wildner sz1 = strcspn(mp1->names, "(");
42054ba9607SSascha Wildner sz2 = strcspn(mp2->names, "(");
42154ba9607SSascha Wildner if (sz1 < sz2)
42254ba9607SSascha Wildner sz1 = sz2;
42354ba9607SSascha Wildner if ((diff = strncasecmp(mp1->names, mp2->names, sz1)))
42454ba9607SSascha Wildner return diff;
425070c62a6SFranco Fichtner
42654ba9607SSascha Wildner /* For identical names and sections, prefer arch-dependent. */
42754ba9607SSascha Wildner cp1 = strchr(mp1->names + sz1, '/');
42854ba9607SSascha Wildner cp2 = strchr(mp2->names + sz2, '/');
42954ba9607SSascha Wildner return cp1 != NULL && cp2 != NULL ? strcasecmp(cp1, cp2) :
43054ba9607SSascha Wildner cp1 != NULL ? -1 : cp2 != NULL ? 1 : 0;
431070c62a6SFranco Fichtner }
432070c62a6SFranco Fichtner
433070c62a6SFranco Fichtner static char *
buildnames(const struct dbm_page * page)43454ba9607SSascha Wildner buildnames(const struct dbm_page *page)
435070c62a6SFranco Fichtner {
43654ba9607SSascha Wildner char *buf;
43754ba9607SSascha Wildner size_t i, sz;
43854ba9607SSascha Wildner
43954ba9607SSascha Wildner sz = lstlen(page->name, 2) + 1 + lstlen(page->sect, 2) +
44054ba9607SSascha Wildner (page->arch == NULL ? 0 : 1 + lstlen(page->arch, 2)) + 2;
44154ba9607SSascha Wildner buf = mandoc_malloc(sz);
44254ba9607SSascha Wildner i = 0;
44354ba9607SSascha Wildner lstcat(buf, &i, page->name, ", ");
44454ba9607SSascha Wildner buf[i++] = '(';
44554ba9607SSascha Wildner lstcat(buf, &i, page->sect, ", ");
44654ba9607SSascha Wildner if (page->arch != NULL) {
44754ba9607SSascha Wildner buf[i++] = '/';
44854ba9607SSascha Wildner lstcat(buf, &i, page->arch, ", ");
44954ba9607SSascha Wildner }
45054ba9607SSascha Wildner buf[i++] = ')';
45154ba9607SSascha Wildner buf[i++] = '\0';
45254ba9607SSascha Wildner assert(i == sz);
45354ba9607SSascha Wildner return buf;
45454ba9607SSascha Wildner }
45554ba9607SSascha Wildner
45654ba9607SSascha Wildner /*
45754ba9607SSascha Wildner * Count the buffer space needed to print the NUL-terminated
45854ba9607SSascha Wildner * list of NUL-terminated strings, when printing sep separator
45954ba9607SSascha Wildner * characters between strings.
46054ba9607SSascha Wildner */
46154ba9607SSascha Wildner static size_t
lstlen(const char * cp,size_t sep)46254ba9607SSascha Wildner lstlen(const char *cp, size_t sep)
46354ba9607SSascha Wildner {
46454ba9607SSascha Wildner size_t sz;
46554ba9607SSascha Wildner
46654ba9607SSascha Wildner for (sz = 0; *cp != '\0'; cp++) {
46754ba9607SSascha Wildner
46854ba9607SSascha Wildner /* Skip names appearing only in the SYNOPSIS. */
46954ba9607SSascha Wildner if (*cp <= (char)(NAME_SYN & NAME_MASK)) {
47054ba9607SSascha Wildner while (*cp != '\0')
47154ba9607SSascha Wildner cp++;
47254ba9607SSascha Wildner continue;
47354ba9607SSascha Wildner }
47454ba9607SSascha Wildner
47554ba9607SSascha Wildner /* Skip name class markers. */
47654ba9607SSascha Wildner if (*cp < ' ')
47754ba9607SSascha Wildner cp++;
47854ba9607SSascha Wildner
47954ba9607SSascha Wildner /* Print a separator before each but the first string. */
48054ba9607SSascha Wildner if (sz)
48154ba9607SSascha Wildner sz += sep;
48254ba9607SSascha Wildner
48354ba9607SSascha Wildner /* Copy one string. */
48454ba9607SSascha Wildner while (*cp != '\0') {
48554ba9607SSascha Wildner sz++;
48654ba9607SSascha Wildner cp++;
48754ba9607SSascha Wildner }
48854ba9607SSascha Wildner }
48954ba9607SSascha Wildner return sz;
49054ba9607SSascha Wildner }
49154ba9607SSascha Wildner
49254ba9607SSascha Wildner /*
49354ba9607SSascha Wildner * Print the NUL-terminated list of NUL-terminated strings
49454ba9607SSascha Wildner * into the buffer, seperating strings with sep.
49554ba9607SSascha Wildner */
49654ba9607SSascha Wildner static void
lstcat(char * buf,size_t * i,const char * cp,const char * sep)49754ba9607SSascha Wildner lstcat(char *buf, size_t *i, const char *cp, const char *sep)
49854ba9607SSascha Wildner {
49954ba9607SSascha Wildner const char *s;
50054ba9607SSascha Wildner size_t i_start;
50154ba9607SSascha Wildner
50254ba9607SSascha Wildner for (i_start = *i; *cp != '\0'; cp++) {
50354ba9607SSascha Wildner
50454ba9607SSascha Wildner /* Skip names appearing only in the SYNOPSIS. */
50554ba9607SSascha Wildner if (*cp <= (char)(NAME_SYN & NAME_MASK)) {
50654ba9607SSascha Wildner while (*cp != '\0')
50754ba9607SSascha Wildner cp++;
50854ba9607SSascha Wildner continue;
50954ba9607SSascha Wildner }
51054ba9607SSascha Wildner
51154ba9607SSascha Wildner /* Skip name class markers. */
51254ba9607SSascha Wildner if (*cp < ' ')
51354ba9607SSascha Wildner cp++;
51454ba9607SSascha Wildner
51554ba9607SSascha Wildner /* Print a separator before each but the first string. */
51654ba9607SSascha Wildner if (*i > i_start) {
51754ba9607SSascha Wildner s = sep;
51854ba9607SSascha Wildner while (*s != '\0')
51954ba9607SSascha Wildner buf[(*i)++] = *s++;
52054ba9607SSascha Wildner }
52154ba9607SSascha Wildner
52254ba9607SSascha Wildner /* Copy one string. */
52354ba9607SSascha Wildner while (*cp != '\0')
52454ba9607SSascha Wildner buf[(*i)++] = *cp++;
52554ba9607SSascha Wildner }
52654ba9607SSascha Wildner
52754ba9607SSascha Wildner }
52854ba9607SSascha Wildner
52954ba9607SSascha Wildner /*
53054ba9607SSascha Wildner * Return 1 if the string *want occurs in any of the strings
53154ba9607SSascha Wildner * in the NUL-terminated string list *have, or 0 otherwise.
53254ba9607SSascha Wildner * If either argument is NULL or empty, assume no filtering
53354ba9607SSascha Wildner * is desired and return 1.
53454ba9607SSascha Wildner */
53554ba9607SSascha Wildner static int
lstmatch(const char * want,const char * have)53654ba9607SSascha Wildner lstmatch(const char *want, const char *have)
53754ba9607SSascha Wildner {
53854ba9607SSascha Wildner if (want == NULL || have == NULL || *have == '\0')
53954ba9607SSascha Wildner return 1;
54054ba9607SSascha Wildner while (*have != '\0') {
54154ba9607SSascha Wildner if (strcasestr(have, want) != NULL)
54254ba9607SSascha Wildner return 1;
54354ba9607SSascha Wildner have = strchr(have, '\0') + 1;
54454ba9607SSascha Wildner }
54554ba9607SSascha Wildner return 0;
54654ba9607SSascha Wildner }
54754ba9607SSascha Wildner
54854ba9607SSascha Wildner /*
54954ba9607SSascha Wildner * Build a list of values taken by the macro im in the manual page.
55054ba9607SSascha Wildner */
55154ba9607SSascha Wildner static char *
buildoutput(size_t im,struct dbm_page * page)55254ba9607SSascha Wildner buildoutput(size_t im, struct dbm_page *page)
55354ba9607SSascha Wildner {
55454ba9607SSascha Wildner const char *oldoutput, *sep, *input;
55554ba9607SSascha Wildner char *output, *newoutput, *value;
55654ba9607SSascha Wildner size_t sz, i;
55754ba9607SSascha Wildner
55854ba9607SSascha Wildner switch (im) {
55954ba9607SSascha Wildner case KEY_Nd:
56054ba9607SSascha Wildner return mandoc_strdup(page->desc);
56154ba9607SSascha Wildner case KEY_Nm:
56254ba9607SSascha Wildner input = page->name;
56354ba9607SSascha Wildner break;
56454ba9607SSascha Wildner case KEY_sec:
56554ba9607SSascha Wildner input = page->sect;
56654ba9607SSascha Wildner break;
56754ba9607SSascha Wildner case KEY_arch:
56854ba9607SSascha Wildner input = page->arch;
56954ba9607SSascha Wildner if (input == NULL)
57054ba9607SSascha Wildner input = "all\0";
57154ba9607SSascha Wildner break;
57254ba9607SSascha Wildner default:
57354ba9607SSascha Wildner input = NULL;
57454ba9607SSascha Wildner break;
57554ba9607SSascha Wildner }
57654ba9607SSascha Wildner
57754ba9607SSascha Wildner if (input != NULL) {
57854ba9607SSascha Wildner sz = lstlen(input, 3) + 1;
57954ba9607SSascha Wildner output = mandoc_malloc(sz);
58054ba9607SSascha Wildner i = 0;
58154ba9607SSascha Wildner lstcat(output, &i, input, " # ");
58254ba9607SSascha Wildner output[i++] = '\0';
58354ba9607SSascha Wildner assert(i == sz);
58454ba9607SSascha Wildner return output;
58554ba9607SSascha Wildner }
586070c62a6SFranco Fichtner
587070c62a6SFranco Fichtner output = NULL;
58854ba9607SSascha Wildner dbm_macro_bypage(im - 2, page->addr);
58954ba9607SSascha Wildner while ((value = dbm_macro_next()) != NULL) {
59054ba9607SSascha Wildner if (output == NULL) {
591070c62a6SFranco Fichtner oldoutput = "";
59254ba9607SSascha Wildner sep = "";
593070c62a6SFranco Fichtner } else {
594070c62a6SFranco Fichtner oldoutput = output;
59554ba9607SSascha Wildner sep = " # ";
596070c62a6SFranco Fichtner }
59754ba9607SSascha Wildner mandoc_asprintf(&newoutput, "%s%s%s", oldoutput, sep, value);
598070c62a6SFranco Fichtner free(output);
599070c62a6SFranco Fichtner output = newoutput;
600070c62a6SFranco Fichtner }
60154ba9607SSascha Wildner return output;
602070c62a6SFranco Fichtner }
603070c62a6SFranco Fichtner
604070c62a6SFranco Fichtner /*
605070c62a6SFranco Fichtner * Compile a set of string tokens into an expression.
606070c62a6SFranco Fichtner * Tokens in "argv" are assumed to be individual expression atoms (e.g.,
607070c62a6SFranco Fichtner * "(", "foo=bar", etc.).
608070c62a6SFranco Fichtner */
609070c62a6SFranco Fichtner static struct expr *
exprcomp(const struct mansearch * search,int argc,char * argv[],int * argi)61054ba9607SSascha Wildner exprcomp(const struct mansearch *search, int argc, char *argv[], int *argi)
611070c62a6SFranco Fichtner {
61254ba9607SSascha Wildner struct expr *parent, *child;
61354ba9607SSascha Wildner int needterm, nested;
614070c62a6SFranco Fichtner
61554ba9607SSascha Wildner if ((nested = *argi) == argc)
61654ba9607SSascha Wildner return NULL;
61754ba9607SSascha Wildner needterm = 1;
61854ba9607SSascha Wildner parent = child = NULL;
61954ba9607SSascha Wildner while (*argi < argc) {
62054ba9607SSascha Wildner if (strcmp(")", argv[*argi]) == 0) {
62154ba9607SSascha Wildner if (needterm)
62254ba9607SSascha Wildner warnx("missing term "
62354ba9607SSascha Wildner "before closing parenthesis");
62454ba9607SSascha Wildner needterm = 0;
62554ba9607SSascha Wildner if (nested)
62654ba9607SSascha Wildner break;
62754ba9607SSascha Wildner warnx("ignoring unmatched right parenthesis");
62854ba9607SSascha Wildner ++*argi;
629070c62a6SFranco Fichtner continue;
630070c62a6SFranco Fichtner }
63154ba9607SSascha Wildner if (strcmp("-o", argv[*argi]) == 0) {
63254ba9607SSascha Wildner if (needterm) {
63354ba9607SSascha Wildner if (*argi > 0)
63454ba9607SSascha Wildner warnx("ignoring -o after %s",
63554ba9607SSascha Wildner argv[*argi - 1]);
636070c62a6SFranco Fichtner else
63754ba9607SSascha Wildner warnx("ignoring initial -o");
638070c62a6SFranco Fichtner }
63954ba9607SSascha Wildner needterm = 1;
64054ba9607SSascha Wildner ++*argi;
64154ba9607SSascha Wildner continue;
642070c62a6SFranco Fichtner }
64354ba9607SSascha Wildner needterm = 0;
64454ba9607SSascha Wildner if (child == NULL) {
64554ba9607SSascha Wildner child = expr_and(search, argc, argv, argi);
64654ba9607SSascha Wildner continue;
647070c62a6SFranco Fichtner }
64854ba9607SSascha Wildner if (parent == NULL) {
64954ba9607SSascha Wildner parent = mandoc_calloc(1, sizeof(*parent));
65054ba9607SSascha Wildner parent->type = EXPR_OR;
65154ba9607SSascha Wildner parent->next = NULL;
65254ba9607SSascha Wildner parent->child = child;
65354ba9607SSascha Wildner }
65454ba9607SSascha Wildner child->next = expr_and(search, argc, argv, argi);
65554ba9607SSascha Wildner child = child->next;
65654ba9607SSascha Wildner }
65754ba9607SSascha Wildner if (needterm && *argi)
65854ba9607SSascha Wildner warnx("ignoring trailing %s", argv[*argi - 1]);
65954ba9607SSascha Wildner return parent == NULL ? child : parent;
660070c62a6SFranco Fichtner }
661070c62a6SFranco Fichtner
662070c62a6SFranco Fichtner static struct expr *
expr_and(const struct mansearch * search,int argc,char * argv[],int * argi)66354ba9607SSascha Wildner expr_and(const struct mansearch *search, int argc, char *argv[], int *argi)
664070c62a6SFranco Fichtner {
66554ba9607SSascha Wildner struct expr *parent, *child;
66654ba9607SSascha Wildner int needterm;
667070c62a6SFranco Fichtner
66854ba9607SSascha Wildner needterm = 1;
66954ba9607SSascha Wildner parent = child = NULL;
67054ba9607SSascha Wildner while (*argi < argc) {
67154ba9607SSascha Wildner if (strcmp(")", argv[*argi]) == 0) {
67254ba9607SSascha Wildner if (needterm)
67354ba9607SSascha Wildner warnx("missing term "
67454ba9607SSascha Wildner "before closing parenthesis");
67554ba9607SSascha Wildner needterm = 0;
67654ba9607SSascha Wildner break;
677070c62a6SFranco Fichtner }
67854ba9607SSascha Wildner if (strcmp("-o", argv[*argi]) == 0)
67954ba9607SSascha Wildner break;
68054ba9607SSascha Wildner if (strcmp("-a", argv[*argi]) == 0) {
68154ba9607SSascha Wildner if (needterm) {
68254ba9607SSascha Wildner if (*argi > 0)
68354ba9607SSascha Wildner warnx("ignoring -a after %s",
68454ba9607SSascha Wildner argv[*argi - 1]);
68554ba9607SSascha Wildner else
68654ba9607SSascha Wildner warnx("ignoring initial -a");
68754ba9607SSascha Wildner }
68854ba9607SSascha Wildner needterm = 1;
68954ba9607SSascha Wildner ++*argi;
69054ba9607SSascha Wildner continue;
69154ba9607SSascha Wildner }
69254ba9607SSascha Wildner if (needterm == 0)
69354ba9607SSascha Wildner break;
69454ba9607SSascha Wildner if (child == NULL) {
69554ba9607SSascha Wildner child = exprterm(search, argc, argv, argi);
69654ba9607SSascha Wildner if (child != NULL)
69754ba9607SSascha Wildner needterm = 0;
69854ba9607SSascha Wildner continue;
69954ba9607SSascha Wildner }
70054ba9607SSascha Wildner needterm = 0;
70154ba9607SSascha Wildner if (parent == NULL) {
70254ba9607SSascha Wildner parent = mandoc_calloc(1, sizeof(*parent));
70354ba9607SSascha Wildner parent->type = EXPR_AND;
70454ba9607SSascha Wildner parent->next = NULL;
70554ba9607SSascha Wildner parent->child = child;
70654ba9607SSascha Wildner }
70754ba9607SSascha Wildner child->next = exprterm(search, argc, argv, argi);
70854ba9607SSascha Wildner if (child->next != NULL) {
70954ba9607SSascha Wildner child = child->next;
71054ba9607SSascha Wildner needterm = 0;
71154ba9607SSascha Wildner }
71254ba9607SSascha Wildner }
71354ba9607SSascha Wildner if (needterm && *argi)
71454ba9607SSascha Wildner warnx("ignoring trailing %s", argv[*argi - 1]);
71554ba9607SSascha Wildner return parent == NULL ? child : parent;
716070c62a6SFranco Fichtner }
717070c62a6SFranco Fichtner
718070c62a6SFranco Fichtner static struct expr *
exprterm(const struct mansearch * search,int argc,char * argv[],int * argi)71954ba9607SSascha Wildner exprterm(const struct mansearch *search, int argc, char *argv[], int *argi)
720070c62a6SFranco Fichtner {
721070c62a6SFranco Fichtner char errbuf[BUFSIZ];
722070c62a6SFranco Fichtner struct expr *e;
723070c62a6SFranco Fichtner char *key, *val;
724070c62a6SFranco Fichtner uint64_t iterbit;
72554ba9607SSascha Wildner int cs, i, irc;
726070c62a6SFranco Fichtner
72754ba9607SSascha Wildner if (strcmp("(", argv[*argi]) == 0) {
72854ba9607SSascha Wildner ++*argi;
72954ba9607SSascha Wildner e = exprcomp(search, argc, argv, argi);
73054ba9607SSascha Wildner if (*argi < argc) {
73154ba9607SSascha Wildner assert(strcmp(")", argv[*argi]) == 0);
73254ba9607SSascha Wildner ++*argi;
73354ba9607SSascha Wildner } else
73454ba9607SSascha Wildner warnx("unclosed parenthesis");
73554ba9607SSascha Wildner return e;
73654ba9607SSascha Wildner }
737070c62a6SFranco Fichtner
73854ba9607SSascha Wildner if (strcmp("-i", argv[*argi]) == 0 && *argi + 1 < argc) {
73954ba9607SSascha Wildner cs = 0;
74054ba9607SSascha Wildner ++*argi;
74154ba9607SSascha Wildner } else
74254ba9607SSascha Wildner cs = 1;
743070c62a6SFranco Fichtner
74454ba9607SSascha Wildner e = mandoc_calloc(1, sizeof(*e));
74554ba9607SSascha Wildner e->type = EXPR_TERM;
74654ba9607SSascha Wildner e->bits = 0;
74754ba9607SSascha Wildner e->next = NULL;
74854ba9607SSascha Wildner e->child = NULL;
74954ba9607SSascha Wildner
75054ba9607SSascha Wildner if (search->argmode == ARG_NAME) {
75154ba9607SSascha Wildner e->bits = TYPE_Nm;
75254ba9607SSascha Wildner e->match.type = DBM_EXACT;
75354ba9607SSascha Wildner e->match.str = argv[(*argi)++];
75454ba9607SSascha Wildner return e;
755070c62a6SFranco Fichtner }
756070c62a6SFranco Fichtner
757070c62a6SFranco Fichtner /*
75854ba9607SSascha Wildner * Separate macro keys from search string.
75954ba9607SSascha Wildner * If needed, request regular expression handling.
760070c62a6SFranco Fichtner */
761070c62a6SFranco Fichtner
76254ba9607SSascha Wildner if (search->argmode == ARG_WORD) {
76354ba9607SSascha Wildner e->bits = TYPE_Nm;
76454ba9607SSascha Wildner e->match.type = DBM_REGEX;
76554ba9607SSascha Wildner #if HAVE_REWB_BSD
76654ba9607SSascha Wildner mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", argv[*argi]);
76754ba9607SSascha Wildner #elif HAVE_REWB_SYSV
76854ba9607SSascha Wildner mandoc_asprintf(&val, "\\<%s\\>", argv[*argi]);
76954ba9607SSascha Wildner #else
77054ba9607SSascha Wildner mandoc_asprintf(&val,
77154ba9607SSascha Wildner "(^|[^a-zA-Z01-9_])%s([^a-zA-Z01-9_]|$)", argv[*argi]);
77254ba9607SSascha Wildner #endif
77354ba9607SSascha Wildner cs = 0;
77454ba9607SSascha Wildner } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) {
77554ba9607SSascha Wildner e->bits = TYPE_Nm | TYPE_Nd;
77654ba9607SSascha Wildner e->match.type = DBM_REGEX;
77754ba9607SSascha Wildner val = argv[*argi];
77854ba9607SSascha Wildner cs = 0;
779070c62a6SFranco Fichtner } else {
78054ba9607SSascha Wildner if (val == argv[*argi])
78154ba9607SSascha Wildner e->bits = TYPE_Nm | TYPE_Nd;
78254ba9607SSascha Wildner if (*val == '=') {
78354ba9607SSascha Wildner e->match.type = DBM_SUB;
78454ba9607SSascha Wildner e->match.str = val + 1;
78554ba9607SSascha Wildner } else
78654ba9607SSascha Wildner e->match.type = DBM_REGEX;
787070c62a6SFranco Fichtner *val++ = '\0';
78854ba9607SSascha Wildner if (strstr(argv[*argi], "arch") != NULL)
789070c62a6SFranco Fichtner cs = 0;
790070c62a6SFranco Fichtner }
791070c62a6SFranco Fichtner
792070c62a6SFranco Fichtner /* Compile regular expressions. */
793070c62a6SFranco Fichtner
79454ba9607SSascha Wildner if (e->match.type == DBM_REGEX) {
79554ba9607SSascha Wildner e->match.re = mandoc_malloc(sizeof(*e->match.re));
79654ba9607SSascha Wildner irc = regcomp(e->match.re, val,
797070c62a6SFranco Fichtner REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
79854ba9607SSascha Wildner if (irc) {
79954ba9607SSascha Wildner regerror(irc, e->match.re, errbuf, sizeof(errbuf));
80054ba9607SSascha Wildner warnx("regcomp /%s/: %s", val, errbuf);
80154ba9607SSascha Wildner }
80254ba9607SSascha Wildner if (search->argmode == ARG_WORD)
803070c62a6SFranco Fichtner free(val);
804070c62a6SFranco Fichtner if (irc) {
80554ba9607SSascha Wildner free(e->match.re);
806070c62a6SFranco Fichtner free(e);
80754ba9607SSascha Wildner ++*argi;
80854ba9607SSascha Wildner return NULL;
809070c62a6SFranco Fichtner }
810070c62a6SFranco Fichtner }
811070c62a6SFranco Fichtner
81254ba9607SSascha Wildner if (e->bits) {
81354ba9607SSascha Wildner ++*argi;
81454ba9607SSascha Wildner return e;
81554ba9607SSascha Wildner }
816070c62a6SFranco Fichtner
817070c62a6SFranco Fichtner /*
818070c62a6SFranco Fichtner * Parse out all possible fields.
819070c62a6SFranco Fichtner * If the field doesn't resolve, bail.
820070c62a6SFranco Fichtner */
821070c62a6SFranco Fichtner
82254ba9607SSascha Wildner while (NULL != (key = strsep(&argv[*argi], ","))) {
823070c62a6SFranco Fichtner if ('\0' == *key)
824070c62a6SFranco Fichtner continue;
82554ba9607SSascha Wildner for (i = 0, iterbit = 1; i < KEY_MAX; i++, iterbit <<= 1) {
82654ba9607SSascha Wildner if (0 == strcasecmp(key, mansearch_keynames[i])) {
827070c62a6SFranco Fichtner e->bits |= iterbit;
828070c62a6SFranco Fichtner break;
829070c62a6SFranco Fichtner }
830070c62a6SFranco Fichtner }
83154ba9607SSascha Wildner if (i == KEY_MAX) {
83254ba9607SSascha Wildner if (strcasecmp(key, "any"))
83354ba9607SSascha Wildner warnx("treating unknown key "
83454ba9607SSascha Wildner "\"%s\" as \"any\"", key);
835070c62a6SFranco Fichtner e->bits |= ~0ULL;
836070c62a6SFranco Fichtner }
837070c62a6SFranco Fichtner }
838070c62a6SFranco Fichtner
83954ba9607SSascha Wildner ++*argi;
84054ba9607SSascha Wildner return e;
841070c62a6SFranco Fichtner }
842070c62a6SFranco Fichtner
843070c62a6SFranco Fichtner static void
exprfree(struct expr * e)84454ba9607SSascha Wildner exprfree(struct expr *e)
845070c62a6SFranco Fichtner {
84654ba9607SSascha Wildner if (e->next != NULL)
84754ba9607SSascha Wildner exprfree(e->next);
84854ba9607SSascha Wildner if (e->child != NULL)
84954ba9607SSascha Wildner exprfree(e->child);
85054ba9607SSascha Wildner free(e);
851070c62a6SFranco Fichtner }
852