1*45a5aec3SBaptiste Daroussin /* $Id: mansearch.c,v 1.82 2019/07/01 22:56:24 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
47295610fSBaptiste Daroussin * Copyright (c) 2013-2018 Ingo Schwarze <schwarze@openbsd.org>
561d06d6bSBaptiste Daroussin *
661d06d6bSBaptiste Daroussin * Permission to use, copy, modify, and distribute this software for any
761d06d6bSBaptiste Daroussin * purpose with or without fee is hereby granted, provided that the above
861d06d6bSBaptiste Daroussin * copyright notice and this permission notice appear in all copies.
961d06d6bSBaptiste Daroussin *
1061d06d6bSBaptiste Daroussin * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1161d06d6bSBaptiste Daroussin * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1261d06d6bSBaptiste Daroussin * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1361d06d6bSBaptiste Daroussin * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1461d06d6bSBaptiste Daroussin * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1561d06d6bSBaptiste Daroussin * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1661d06d6bSBaptiste Daroussin * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1761d06d6bSBaptiste Daroussin */
1861d06d6bSBaptiste Daroussin #include "config.h"
1961d06d6bSBaptiste Daroussin
2061d06d6bSBaptiste Daroussin #include <sys/mman.h>
2161d06d6bSBaptiste Daroussin #include <sys/types.h>
2261d06d6bSBaptiste Daroussin
2361d06d6bSBaptiste Daroussin #include <assert.h>
2461d06d6bSBaptiste Daroussin #if HAVE_ERR
2561d06d6bSBaptiste Daroussin #include <err.h>
2661d06d6bSBaptiste Daroussin #endif
2761d06d6bSBaptiste Daroussin #include <errno.h>
2861d06d6bSBaptiste Daroussin #include <fcntl.h>
2961d06d6bSBaptiste Daroussin #include <glob.h>
3061d06d6bSBaptiste Daroussin #include <limits.h>
3161d06d6bSBaptiste Daroussin #include <regex.h>
3261d06d6bSBaptiste Daroussin #include <stdio.h>
3361d06d6bSBaptiste Daroussin #include <stdint.h>
3461d06d6bSBaptiste Daroussin #include <stddef.h>
3561d06d6bSBaptiste Daroussin #include <stdlib.h>
3661d06d6bSBaptiste Daroussin #include <string.h>
3761d06d6bSBaptiste Daroussin #include <unistd.h>
3861d06d6bSBaptiste Daroussin
3961d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
4061d06d6bSBaptiste Daroussin #include "mandoc_ohash.h"
4161d06d6bSBaptiste Daroussin #include "manconf.h"
4261d06d6bSBaptiste Daroussin #include "mansearch.h"
4361d06d6bSBaptiste Daroussin #include "dbm.h"
4461d06d6bSBaptiste Daroussin
4561d06d6bSBaptiste Daroussin struct expr {
4661d06d6bSBaptiste Daroussin /* Used for terms: */
4761d06d6bSBaptiste Daroussin struct dbm_match match; /* Match type and expression. */
4861d06d6bSBaptiste Daroussin uint64_t bits; /* Type mask. */
4961d06d6bSBaptiste Daroussin /* Used for OR and AND groups: */
5061d06d6bSBaptiste Daroussin struct expr *next; /* Next child in the parent group. */
5161d06d6bSBaptiste Daroussin struct expr *child; /* First child in this group. */
5261d06d6bSBaptiste Daroussin enum { EXPR_TERM, EXPR_OR, EXPR_AND } type;
5361d06d6bSBaptiste Daroussin };
5461d06d6bSBaptiste Daroussin
5561d06d6bSBaptiste Daroussin const char *const mansearch_keynames[KEY_MAX] = {
5661d06d6bSBaptiste Daroussin "arch", "sec", "Xr", "Ar", "Fa", "Fl", "Dv", "Fn",
5761d06d6bSBaptiste Daroussin "Ic", "Pa", "Cm", "Li", "Em", "Cd", "Va", "Ft",
5861d06d6bSBaptiste Daroussin "Tn", "Er", "Ev", "Sy", "Sh", "In", "Ss", "Ox",
5961d06d6bSBaptiste Daroussin "An", "Mt", "St", "Bx", "At", "Nx", "Fx", "Lk",
6061d06d6bSBaptiste Daroussin "Ms", "Bsx", "Dx", "Rs", "Vt", "Lb", "Nm", "Nd"
6161d06d6bSBaptiste Daroussin };
6261d06d6bSBaptiste Daroussin
6361d06d6bSBaptiste Daroussin
6461d06d6bSBaptiste Daroussin static struct ohash *manmerge(struct expr *, struct ohash *);
6561d06d6bSBaptiste Daroussin static struct ohash *manmerge_term(struct expr *, struct ohash *);
6661d06d6bSBaptiste Daroussin static struct ohash *manmerge_or(struct expr *, struct ohash *);
6761d06d6bSBaptiste Daroussin static struct ohash *manmerge_and(struct expr *, struct ohash *);
6861d06d6bSBaptiste Daroussin static char *buildnames(const struct dbm_page *);
6961d06d6bSBaptiste Daroussin static char *buildoutput(size_t, struct dbm_page *);
7061d06d6bSBaptiste Daroussin static size_t lstlen(const char *, size_t);
7161d06d6bSBaptiste Daroussin static void lstcat(char *, size_t *, const char *, const char *);
7261d06d6bSBaptiste Daroussin static int lstmatch(const char *, const char *);
7361d06d6bSBaptiste Daroussin static struct expr *exprcomp(const struct mansearch *,
7461d06d6bSBaptiste Daroussin int, char *[], int *);
7561d06d6bSBaptiste Daroussin static struct expr *expr_and(const struct mansearch *,
7661d06d6bSBaptiste Daroussin int, char *[], int *);
7761d06d6bSBaptiste Daroussin static struct expr *exprterm(const struct mansearch *,
7861d06d6bSBaptiste Daroussin int, char *[], int *);
7961d06d6bSBaptiste Daroussin static void exprfree(struct expr *);
8061d06d6bSBaptiste Daroussin static int manpage_compare(const void *, const void *);
8161d06d6bSBaptiste Daroussin
8261d06d6bSBaptiste Daroussin
8361d06d6bSBaptiste Daroussin int
mansearch(const struct mansearch * search,const struct manpaths * paths,int argc,char * argv[],struct manpage ** res,size_t * sz)8461d06d6bSBaptiste Daroussin mansearch(const struct mansearch *search,
8561d06d6bSBaptiste Daroussin const struct manpaths *paths,
8661d06d6bSBaptiste Daroussin int argc, char *argv[],
8761d06d6bSBaptiste Daroussin struct manpage **res, size_t *sz)
8861d06d6bSBaptiste Daroussin {
8961d06d6bSBaptiste Daroussin char buf[PATH_MAX];
9061d06d6bSBaptiste Daroussin struct dbm_res *rp;
9161d06d6bSBaptiste Daroussin struct expr *e;
9261d06d6bSBaptiste Daroussin struct dbm_page *page;
9361d06d6bSBaptiste Daroussin struct manpage *mpage;
9461d06d6bSBaptiste Daroussin struct ohash *htab;
9561d06d6bSBaptiste Daroussin size_t cur, i, maxres, outkey;
9661d06d6bSBaptiste Daroussin unsigned int slot;
9761d06d6bSBaptiste Daroussin int argi, chdir_status, getcwd_status, im;
9861d06d6bSBaptiste Daroussin
9961d06d6bSBaptiste Daroussin argi = 0;
10061d06d6bSBaptiste Daroussin if ((e = exprcomp(search, argc, argv, &argi)) == NULL) {
10161d06d6bSBaptiste Daroussin *sz = 0;
10261d06d6bSBaptiste Daroussin return 0;
10361d06d6bSBaptiste Daroussin }
10461d06d6bSBaptiste Daroussin
10561d06d6bSBaptiste Daroussin cur = maxres = 0;
10661d06d6bSBaptiste Daroussin if (res != NULL)
10761d06d6bSBaptiste Daroussin *res = NULL;
10861d06d6bSBaptiste Daroussin
10961d06d6bSBaptiste Daroussin outkey = KEY_Nd;
11061d06d6bSBaptiste Daroussin if (search->outkey != NULL)
11161d06d6bSBaptiste Daroussin for (im = 0; im < KEY_MAX; im++)
11261d06d6bSBaptiste Daroussin if (0 == strcasecmp(search->outkey,
11361d06d6bSBaptiste Daroussin mansearch_keynames[im])) {
11461d06d6bSBaptiste Daroussin outkey = im;
11561d06d6bSBaptiste Daroussin break;
11661d06d6bSBaptiste Daroussin }
11761d06d6bSBaptiste Daroussin
11861d06d6bSBaptiste Daroussin /*
11961d06d6bSBaptiste Daroussin * Remember the original working directory, if possible.
12061d06d6bSBaptiste Daroussin * This will be needed if the second or a later directory
12161d06d6bSBaptiste Daroussin * is given as a relative path.
12261d06d6bSBaptiste Daroussin * Do not error out if the current directory is not
12361d06d6bSBaptiste Daroussin * searchable: Maybe it won't be needed after all.
12461d06d6bSBaptiste Daroussin */
12561d06d6bSBaptiste Daroussin
12661d06d6bSBaptiste Daroussin if (getcwd(buf, PATH_MAX) == NULL) {
12761d06d6bSBaptiste Daroussin getcwd_status = 0;
12861d06d6bSBaptiste Daroussin (void)strlcpy(buf, strerror(errno), sizeof(buf));
12961d06d6bSBaptiste Daroussin } else
13061d06d6bSBaptiste Daroussin getcwd_status = 1;
13161d06d6bSBaptiste Daroussin
13261d06d6bSBaptiste Daroussin /*
13361d06d6bSBaptiste Daroussin * Loop over the directories (containing databases) for us to
13461d06d6bSBaptiste Daroussin * search.
13561d06d6bSBaptiste Daroussin * Don't let missing/bad databases/directories phase us.
13661d06d6bSBaptiste Daroussin * In each, try to open the resident database and, if it opens,
13761d06d6bSBaptiste Daroussin * scan it for our match expression.
13861d06d6bSBaptiste Daroussin */
13961d06d6bSBaptiste Daroussin
14061d06d6bSBaptiste Daroussin chdir_status = 0;
14161d06d6bSBaptiste Daroussin for (i = 0; i < paths->sz; i++) {
14261d06d6bSBaptiste Daroussin if (chdir_status && paths->paths[i][0] != '/') {
14361d06d6bSBaptiste Daroussin if ( ! getcwd_status) {
14461d06d6bSBaptiste Daroussin warnx("%s: getcwd: %s", paths->paths[i], buf);
14561d06d6bSBaptiste Daroussin continue;
14661d06d6bSBaptiste Daroussin } else if (chdir(buf) == -1) {
14761d06d6bSBaptiste Daroussin warn("%s", buf);
14861d06d6bSBaptiste Daroussin continue;
14961d06d6bSBaptiste Daroussin }
15061d06d6bSBaptiste Daroussin }
15161d06d6bSBaptiste Daroussin if (chdir(paths->paths[i]) == -1) {
15261d06d6bSBaptiste Daroussin warn("%s", paths->paths[i]);
15361d06d6bSBaptiste Daroussin continue;
15461d06d6bSBaptiste Daroussin }
15561d06d6bSBaptiste Daroussin chdir_status = 1;
15661d06d6bSBaptiste Daroussin
15761d06d6bSBaptiste Daroussin if (dbm_open(MANDOC_DB) == -1) {
15861d06d6bSBaptiste Daroussin if (errno != ENOENT)
15961d06d6bSBaptiste Daroussin warn("%s/%s", paths->paths[i], MANDOC_DB);
16061d06d6bSBaptiste Daroussin continue;
16161d06d6bSBaptiste Daroussin }
16261d06d6bSBaptiste Daroussin
16361d06d6bSBaptiste Daroussin if ((htab = manmerge(e, NULL)) == NULL) {
16461d06d6bSBaptiste Daroussin dbm_close();
16561d06d6bSBaptiste Daroussin continue;
16661d06d6bSBaptiste Daroussin }
16761d06d6bSBaptiste Daroussin
16861d06d6bSBaptiste Daroussin for (rp = ohash_first(htab, &slot); rp != NULL;
16961d06d6bSBaptiste Daroussin rp = ohash_next(htab, &slot)) {
17061d06d6bSBaptiste Daroussin page = dbm_page_get(rp->page);
17161d06d6bSBaptiste Daroussin
17261d06d6bSBaptiste Daroussin if (lstmatch(search->sec, page->sect) == 0 ||
17361d06d6bSBaptiste Daroussin lstmatch(search->arch, page->arch) == 0 ||
17461d06d6bSBaptiste Daroussin (search->argmode == ARG_NAME &&
17561d06d6bSBaptiste Daroussin rp->bits <= (int32_t)(NAME_SYN & NAME_MASK)))
17661d06d6bSBaptiste Daroussin continue;
17761d06d6bSBaptiste Daroussin
17861d06d6bSBaptiste Daroussin if (res == NULL) {
17961d06d6bSBaptiste Daroussin cur = 1;
18061d06d6bSBaptiste Daroussin break;
18161d06d6bSBaptiste Daroussin }
18261d06d6bSBaptiste Daroussin if (cur + 1 > maxres) {
18361d06d6bSBaptiste Daroussin maxres += 1024;
18461d06d6bSBaptiste Daroussin *res = mandoc_reallocarray(*res,
18561d06d6bSBaptiste Daroussin maxres, sizeof(**res));
18661d06d6bSBaptiste Daroussin }
18761d06d6bSBaptiste Daroussin mpage = *res + cur;
18861d06d6bSBaptiste Daroussin mandoc_asprintf(&mpage->file, "%s/%s",
18961d06d6bSBaptiste Daroussin paths->paths[i], page->file + 1);
19061d06d6bSBaptiste Daroussin if (access(chdir_status ? page->file + 1 :
19161d06d6bSBaptiste Daroussin mpage->file, R_OK) == -1) {
19261d06d6bSBaptiste Daroussin warn("%s", mpage->file);
19361d06d6bSBaptiste Daroussin warnx("outdated mandoc.db contains "
19461d06d6bSBaptiste Daroussin "bogus %s entry, run makewhatis %s",
19561d06d6bSBaptiste Daroussin page->file + 1, paths->paths[i]);
19661d06d6bSBaptiste Daroussin free(mpage->file);
19761d06d6bSBaptiste Daroussin free(rp);
19861d06d6bSBaptiste Daroussin continue;
19961d06d6bSBaptiste Daroussin }
20061d06d6bSBaptiste Daroussin mpage->names = buildnames(page);
20161d06d6bSBaptiste Daroussin mpage->output = buildoutput(outkey, page);
202*45a5aec3SBaptiste Daroussin mpage->bits = search->firstmatch ? rp->bits : 0;
20361d06d6bSBaptiste Daroussin mpage->ipath = i;
20461d06d6bSBaptiste Daroussin mpage->sec = *page->sect - '0';
20561d06d6bSBaptiste Daroussin if (mpage->sec < 0 || mpage->sec > 9)
20661d06d6bSBaptiste Daroussin mpage->sec = 10;
20761d06d6bSBaptiste Daroussin mpage->form = *page->file;
20861d06d6bSBaptiste Daroussin free(rp);
20961d06d6bSBaptiste Daroussin cur++;
21061d06d6bSBaptiste Daroussin }
21161d06d6bSBaptiste Daroussin ohash_delete(htab);
21261d06d6bSBaptiste Daroussin free(htab);
21361d06d6bSBaptiste Daroussin dbm_close();
21461d06d6bSBaptiste Daroussin
21561d06d6bSBaptiste Daroussin /*
21661d06d6bSBaptiste Daroussin * In man(1) mode, prefer matches in earlier trees
21761d06d6bSBaptiste Daroussin * over matches in later trees.
21861d06d6bSBaptiste Daroussin */
21961d06d6bSBaptiste Daroussin
22061d06d6bSBaptiste Daroussin if (cur && search->firstmatch)
22161d06d6bSBaptiste Daroussin break;
22261d06d6bSBaptiste Daroussin }
22361d06d6bSBaptiste Daroussin if (res != NULL)
22461d06d6bSBaptiste Daroussin qsort(*res, cur, sizeof(struct manpage), manpage_compare);
22561d06d6bSBaptiste Daroussin if (chdir_status && getcwd_status && chdir(buf) == -1)
22661d06d6bSBaptiste Daroussin warn("%s", buf);
22761d06d6bSBaptiste Daroussin exprfree(e);
22861d06d6bSBaptiste Daroussin *sz = cur;
22961d06d6bSBaptiste Daroussin return res != NULL || cur;
23061d06d6bSBaptiste Daroussin }
23161d06d6bSBaptiste Daroussin
23261d06d6bSBaptiste Daroussin /*
23361d06d6bSBaptiste Daroussin * Merge the results for the expression tree rooted at e
23461d06d6bSBaptiste Daroussin * into the the result list htab.
23561d06d6bSBaptiste Daroussin */
23661d06d6bSBaptiste Daroussin static struct ohash *
manmerge(struct expr * e,struct ohash * htab)23761d06d6bSBaptiste Daroussin manmerge(struct expr *e, struct ohash *htab)
23861d06d6bSBaptiste Daroussin {
23961d06d6bSBaptiste Daroussin switch (e->type) {
24061d06d6bSBaptiste Daroussin case EXPR_TERM:
24161d06d6bSBaptiste Daroussin return manmerge_term(e, htab);
24261d06d6bSBaptiste Daroussin case EXPR_OR:
24361d06d6bSBaptiste Daroussin return manmerge_or(e->child, htab);
24461d06d6bSBaptiste Daroussin case EXPR_AND:
24561d06d6bSBaptiste Daroussin return manmerge_and(e->child, htab);
24661d06d6bSBaptiste Daroussin default:
24761d06d6bSBaptiste Daroussin abort();
24861d06d6bSBaptiste Daroussin }
24961d06d6bSBaptiste Daroussin }
25061d06d6bSBaptiste Daroussin
25161d06d6bSBaptiste Daroussin static struct ohash *
manmerge_term(struct expr * e,struct ohash * htab)25261d06d6bSBaptiste Daroussin manmerge_term(struct expr *e, struct ohash *htab)
25361d06d6bSBaptiste Daroussin {
25461d06d6bSBaptiste Daroussin struct dbm_res res, *rp;
25561d06d6bSBaptiste Daroussin uint64_t ib;
25661d06d6bSBaptiste Daroussin unsigned int slot;
25761d06d6bSBaptiste Daroussin int im;
25861d06d6bSBaptiste Daroussin
25961d06d6bSBaptiste Daroussin if (htab == NULL) {
26061d06d6bSBaptiste Daroussin htab = mandoc_malloc(sizeof(*htab));
26161d06d6bSBaptiste Daroussin mandoc_ohash_init(htab, 4, offsetof(struct dbm_res, page));
26261d06d6bSBaptiste Daroussin }
26361d06d6bSBaptiste Daroussin
26461d06d6bSBaptiste Daroussin for (im = 0, ib = 1; im < KEY_MAX; im++, ib <<= 1) {
26561d06d6bSBaptiste Daroussin if ((e->bits & ib) == 0)
26661d06d6bSBaptiste Daroussin continue;
26761d06d6bSBaptiste Daroussin
26861d06d6bSBaptiste Daroussin switch (ib) {
26961d06d6bSBaptiste Daroussin case TYPE_arch:
27061d06d6bSBaptiste Daroussin dbm_page_byarch(&e->match);
27161d06d6bSBaptiste Daroussin break;
27261d06d6bSBaptiste Daroussin case TYPE_sec:
27361d06d6bSBaptiste Daroussin dbm_page_bysect(&e->match);
27461d06d6bSBaptiste Daroussin break;
27561d06d6bSBaptiste Daroussin case TYPE_Nm:
27661d06d6bSBaptiste Daroussin dbm_page_byname(&e->match);
27761d06d6bSBaptiste Daroussin break;
27861d06d6bSBaptiste Daroussin case TYPE_Nd:
27961d06d6bSBaptiste Daroussin dbm_page_bydesc(&e->match);
28061d06d6bSBaptiste Daroussin break;
28161d06d6bSBaptiste Daroussin default:
28261d06d6bSBaptiste Daroussin dbm_page_bymacro(im - 2, &e->match);
28361d06d6bSBaptiste Daroussin break;
28461d06d6bSBaptiste Daroussin }
28561d06d6bSBaptiste Daroussin
28661d06d6bSBaptiste Daroussin /*
28761d06d6bSBaptiste Daroussin * When hashing for deduplication, use the unique
28861d06d6bSBaptiste Daroussin * page ID itself instead of a hash function;
28961d06d6bSBaptiste Daroussin * that is quite efficient.
29061d06d6bSBaptiste Daroussin */
29161d06d6bSBaptiste Daroussin
29261d06d6bSBaptiste Daroussin for (;;) {
29361d06d6bSBaptiste Daroussin res = dbm_page_next();
29461d06d6bSBaptiste Daroussin if (res.page == -1)
29561d06d6bSBaptiste Daroussin break;
29661d06d6bSBaptiste Daroussin slot = ohash_lookup_memory(htab,
29761d06d6bSBaptiste Daroussin (char *)&res, sizeof(res.page), res.page);
298*45a5aec3SBaptiste Daroussin if ((rp = ohash_find(htab, slot)) != NULL) {
299*45a5aec3SBaptiste Daroussin rp->bits |= res.bits;
30061d06d6bSBaptiste Daroussin continue;
301*45a5aec3SBaptiste Daroussin }
30261d06d6bSBaptiste Daroussin rp = mandoc_malloc(sizeof(*rp));
30361d06d6bSBaptiste Daroussin *rp = res;
30461d06d6bSBaptiste Daroussin ohash_insert(htab, slot, rp);
30561d06d6bSBaptiste Daroussin }
30661d06d6bSBaptiste Daroussin }
30761d06d6bSBaptiste Daroussin return htab;
30861d06d6bSBaptiste Daroussin }
30961d06d6bSBaptiste Daroussin
31061d06d6bSBaptiste Daroussin static struct ohash *
manmerge_or(struct expr * e,struct ohash * htab)31161d06d6bSBaptiste Daroussin manmerge_or(struct expr *e, struct ohash *htab)
31261d06d6bSBaptiste Daroussin {
31361d06d6bSBaptiste Daroussin while (e != NULL) {
31461d06d6bSBaptiste Daroussin htab = manmerge(e, htab);
31561d06d6bSBaptiste Daroussin e = e->next;
31661d06d6bSBaptiste Daroussin }
31761d06d6bSBaptiste Daroussin return htab;
31861d06d6bSBaptiste Daroussin }
31961d06d6bSBaptiste Daroussin
32061d06d6bSBaptiste Daroussin static struct ohash *
manmerge_and(struct expr * e,struct ohash * htab)32161d06d6bSBaptiste Daroussin manmerge_and(struct expr *e, struct ohash *htab)
32261d06d6bSBaptiste Daroussin {
32361d06d6bSBaptiste Daroussin struct ohash *hand, *h1, *h2;
32461d06d6bSBaptiste Daroussin struct dbm_res *res;
32561d06d6bSBaptiste Daroussin unsigned int slot1, slot2;
32661d06d6bSBaptiste Daroussin
32761d06d6bSBaptiste Daroussin /* Evaluate the first term of the AND clause. */
32861d06d6bSBaptiste Daroussin
32961d06d6bSBaptiste Daroussin hand = manmerge(e, NULL);
33061d06d6bSBaptiste Daroussin
33161d06d6bSBaptiste Daroussin while ((e = e->next) != NULL) {
33261d06d6bSBaptiste Daroussin
33361d06d6bSBaptiste Daroussin /* Evaluate the next term and prepare for ANDing. */
33461d06d6bSBaptiste Daroussin
33561d06d6bSBaptiste Daroussin h2 = manmerge(e, NULL);
33661d06d6bSBaptiste Daroussin if (ohash_entries(h2) < ohash_entries(hand)) {
33761d06d6bSBaptiste Daroussin h1 = h2;
33861d06d6bSBaptiste Daroussin h2 = hand;
33961d06d6bSBaptiste Daroussin } else
34061d06d6bSBaptiste Daroussin h1 = hand;
34161d06d6bSBaptiste Daroussin hand = mandoc_malloc(sizeof(*hand));
34261d06d6bSBaptiste Daroussin mandoc_ohash_init(hand, 4, offsetof(struct dbm_res, page));
34361d06d6bSBaptiste Daroussin
34461d06d6bSBaptiste Daroussin /* Keep all pages that are in both result sets. */
34561d06d6bSBaptiste Daroussin
34661d06d6bSBaptiste Daroussin for (res = ohash_first(h1, &slot1); res != NULL;
34761d06d6bSBaptiste Daroussin res = ohash_next(h1, &slot1)) {
34861d06d6bSBaptiste Daroussin if (ohash_find(h2, ohash_lookup_memory(h2,
34961d06d6bSBaptiste Daroussin (char *)res, sizeof(res->page),
35061d06d6bSBaptiste Daroussin res->page)) == NULL)
35161d06d6bSBaptiste Daroussin free(res);
35261d06d6bSBaptiste Daroussin else
35361d06d6bSBaptiste Daroussin ohash_insert(hand, ohash_lookup_memory(hand,
35461d06d6bSBaptiste Daroussin (char *)res, sizeof(res->page),
35561d06d6bSBaptiste Daroussin res->page), res);
35661d06d6bSBaptiste Daroussin }
35761d06d6bSBaptiste Daroussin
35861d06d6bSBaptiste Daroussin /* Discard the merged results. */
35961d06d6bSBaptiste Daroussin
36061d06d6bSBaptiste Daroussin for (res = ohash_first(h2, &slot2); res != NULL;
36161d06d6bSBaptiste Daroussin res = ohash_next(h2, &slot2))
36261d06d6bSBaptiste Daroussin free(res);
36361d06d6bSBaptiste Daroussin ohash_delete(h2);
36461d06d6bSBaptiste Daroussin free(h2);
36561d06d6bSBaptiste Daroussin ohash_delete(h1);
36661d06d6bSBaptiste Daroussin free(h1);
36761d06d6bSBaptiste Daroussin }
36861d06d6bSBaptiste Daroussin
36961d06d6bSBaptiste Daroussin /* Merge the result of the AND into htab. */
37061d06d6bSBaptiste Daroussin
37161d06d6bSBaptiste Daroussin if (htab == NULL)
37261d06d6bSBaptiste Daroussin return hand;
37361d06d6bSBaptiste Daroussin
37461d06d6bSBaptiste Daroussin for (res = ohash_first(hand, &slot1); res != NULL;
37561d06d6bSBaptiste Daroussin res = ohash_next(hand, &slot1)) {
37661d06d6bSBaptiste Daroussin slot2 = ohash_lookup_memory(htab,
37761d06d6bSBaptiste Daroussin (char *)res, sizeof(res->page), res->page);
37861d06d6bSBaptiste Daroussin if (ohash_find(htab, slot2) == NULL)
37961d06d6bSBaptiste Daroussin ohash_insert(htab, slot2, res);
38061d06d6bSBaptiste Daroussin else
38161d06d6bSBaptiste Daroussin free(res);
38261d06d6bSBaptiste Daroussin }
38361d06d6bSBaptiste Daroussin
38461d06d6bSBaptiste Daroussin /* Discard the merged result. */
38561d06d6bSBaptiste Daroussin
38661d06d6bSBaptiste Daroussin ohash_delete(hand);
38761d06d6bSBaptiste Daroussin free(hand);
38861d06d6bSBaptiste Daroussin return htab;
38961d06d6bSBaptiste Daroussin }
39061d06d6bSBaptiste Daroussin
39161d06d6bSBaptiste Daroussin void
mansearch_free(struct manpage * res,size_t sz)39261d06d6bSBaptiste Daroussin mansearch_free(struct manpage *res, size_t sz)
39361d06d6bSBaptiste Daroussin {
39461d06d6bSBaptiste Daroussin size_t i;
39561d06d6bSBaptiste Daroussin
39661d06d6bSBaptiste Daroussin for (i = 0; i < sz; i++) {
39761d06d6bSBaptiste Daroussin free(res[i].file);
39861d06d6bSBaptiste Daroussin free(res[i].names);
39961d06d6bSBaptiste Daroussin free(res[i].output);
40061d06d6bSBaptiste Daroussin }
40161d06d6bSBaptiste Daroussin free(res);
40261d06d6bSBaptiste Daroussin }
40361d06d6bSBaptiste Daroussin
40461d06d6bSBaptiste Daroussin static int
manpage_compare(const void * vp1,const void * vp2)40561d06d6bSBaptiste Daroussin manpage_compare(const void *vp1, const void *vp2)
40661d06d6bSBaptiste Daroussin {
40761d06d6bSBaptiste Daroussin const struct manpage *mp1, *mp2;
40861d06d6bSBaptiste Daroussin const char *cp1, *cp2;
40961d06d6bSBaptiste Daroussin size_t sz1, sz2;
41061d06d6bSBaptiste Daroussin int diff;
41161d06d6bSBaptiste Daroussin
41261d06d6bSBaptiste Daroussin mp1 = vp1;
41361d06d6bSBaptiste Daroussin mp2 = vp2;
414*45a5aec3SBaptiste Daroussin if ((diff = mp2->bits - mp1->bits) ||
415*45a5aec3SBaptiste Daroussin (diff = mp1->sec - mp2->sec))
41661d06d6bSBaptiste Daroussin return diff;
41761d06d6bSBaptiste Daroussin
41861d06d6bSBaptiste Daroussin /* Fall back to alphabetic ordering of names. */
41961d06d6bSBaptiste Daroussin sz1 = strcspn(mp1->names, "(");
42061d06d6bSBaptiste Daroussin sz2 = strcspn(mp2->names, "(");
42161d06d6bSBaptiste Daroussin if (sz1 < sz2)
42261d06d6bSBaptiste Daroussin sz1 = sz2;
42361d06d6bSBaptiste Daroussin if ((diff = strncasecmp(mp1->names, mp2->names, sz1)))
42461d06d6bSBaptiste Daroussin return diff;
42561d06d6bSBaptiste Daroussin
42661d06d6bSBaptiste Daroussin /* For identical names and sections, prefer arch-dependent. */
42761d06d6bSBaptiste Daroussin cp1 = strchr(mp1->names + sz1, '/');
42861d06d6bSBaptiste Daroussin cp2 = strchr(mp2->names + sz2, '/');
42961d06d6bSBaptiste Daroussin return cp1 != NULL && cp2 != NULL ? strcasecmp(cp1, cp2) :
43061d06d6bSBaptiste Daroussin cp1 != NULL ? -1 : cp2 != NULL ? 1 : 0;
43161d06d6bSBaptiste Daroussin }
43261d06d6bSBaptiste Daroussin
43361d06d6bSBaptiste Daroussin static char *
buildnames(const struct dbm_page * page)43461d06d6bSBaptiste Daroussin buildnames(const struct dbm_page *page)
43561d06d6bSBaptiste Daroussin {
43661d06d6bSBaptiste Daroussin char *buf;
43761d06d6bSBaptiste Daroussin size_t i, sz;
43861d06d6bSBaptiste Daroussin
43961d06d6bSBaptiste Daroussin sz = lstlen(page->name, 2) + 1 + lstlen(page->sect, 2) +
44061d06d6bSBaptiste Daroussin (page->arch == NULL ? 0 : 1 + lstlen(page->arch, 2)) + 2;
44161d06d6bSBaptiste Daroussin buf = mandoc_malloc(sz);
44261d06d6bSBaptiste Daroussin i = 0;
44361d06d6bSBaptiste Daroussin lstcat(buf, &i, page->name, ", ");
44461d06d6bSBaptiste Daroussin buf[i++] = '(';
44561d06d6bSBaptiste Daroussin lstcat(buf, &i, page->sect, ", ");
44661d06d6bSBaptiste Daroussin if (page->arch != NULL) {
44761d06d6bSBaptiste Daroussin buf[i++] = '/';
44861d06d6bSBaptiste Daroussin lstcat(buf, &i, page->arch, ", ");
44961d06d6bSBaptiste Daroussin }
45061d06d6bSBaptiste Daroussin buf[i++] = ')';
45161d06d6bSBaptiste Daroussin buf[i++] = '\0';
45261d06d6bSBaptiste Daroussin assert(i == sz);
45361d06d6bSBaptiste Daroussin return buf;
45461d06d6bSBaptiste Daroussin }
45561d06d6bSBaptiste Daroussin
45661d06d6bSBaptiste Daroussin /*
45761d06d6bSBaptiste Daroussin * Count the buffer space needed to print the NUL-terminated
45861d06d6bSBaptiste Daroussin * list of NUL-terminated strings, when printing sep separator
45961d06d6bSBaptiste Daroussin * characters between strings.
46061d06d6bSBaptiste Daroussin */
46161d06d6bSBaptiste Daroussin static size_t
lstlen(const char * cp,size_t sep)46261d06d6bSBaptiste Daroussin lstlen(const char *cp, size_t sep)
46361d06d6bSBaptiste Daroussin {
46461d06d6bSBaptiste Daroussin size_t sz;
46561d06d6bSBaptiste Daroussin
46661d06d6bSBaptiste Daroussin for (sz = 0; *cp != '\0'; cp++) {
46761d06d6bSBaptiste Daroussin
46861d06d6bSBaptiste Daroussin /* Skip names appearing only in the SYNOPSIS. */
46961d06d6bSBaptiste Daroussin if (*cp <= (char)(NAME_SYN & NAME_MASK)) {
47061d06d6bSBaptiste Daroussin while (*cp != '\0')
47161d06d6bSBaptiste Daroussin cp++;
47261d06d6bSBaptiste Daroussin continue;
47361d06d6bSBaptiste Daroussin }
47461d06d6bSBaptiste Daroussin
47561d06d6bSBaptiste Daroussin /* Skip name class markers. */
47661d06d6bSBaptiste Daroussin if (*cp < ' ')
47761d06d6bSBaptiste Daroussin cp++;
47861d06d6bSBaptiste Daroussin
47961d06d6bSBaptiste Daroussin /* Print a separator before each but the first string. */
48061d06d6bSBaptiste Daroussin if (sz)
48161d06d6bSBaptiste Daroussin sz += sep;
48261d06d6bSBaptiste Daroussin
48361d06d6bSBaptiste Daroussin /* Copy one string. */
48461d06d6bSBaptiste Daroussin while (*cp != '\0') {
48561d06d6bSBaptiste Daroussin sz++;
48661d06d6bSBaptiste Daroussin cp++;
48761d06d6bSBaptiste Daroussin }
48861d06d6bSBaptiste Daroussin }
48961d06d6bSBaptiste Daroussin return sz;
49061d06d6bSBaptiste Daroussin }
49161d06d6bSBaptiste Daroussin
49261d06d6bSBaptiste Daroussin /*
49361d06d6bSBaptiste Daroussin * Print the NUL-terminated list of NUL-terminated strings
49461d06d6bSBaptiste Daroussin * into the buffer, seperating strings with sep.
49561d06d6bSBaptiste Daroussin */
49661d06d6bSBaptiste Daroussin static void
lstcat(char * buf,size_t * i,const char * cp,const char * sep)49761d06d6bSBaptiste Daroussin lstcat(char *buf, size_t *i, const char *cp, const char *sep)
49861d06d6bSBaptiste Daroussin {
49961d06d6bSBaptiste Daroussin const char *s;
50061d06d6bSBaptiste Daroussin size_t i_start;
50161d06d6bSBaptiste Daroussin
50261d06d6bSBaptiste Daroussin for (i_start = *i; *cp != '\0'; cp++) {
50361d06d6bSBaptiste Daroussin
50461d06d6bSBaptiste Daroussin /* Skip names appearing only in the SYNOPSIS. */
50561d06d6bSBaptiste Daroussin if (*cp <= (char)(NAME_SYN & NAME_MASK)) {
50661d06d6bSBaptiste Daroussin while (*cp != '\0')
50761d06d6bSBaptiste Daroussin cp++;
50861d06d6bSBaptiste Daroussin continue;
50961d06d6bSBaptiste Daroussin }
51061d06d6bSBaptiste Daroussin
51161d06d6bSBaptiste Daroussin /* Skip name class markers. */
51261d06d6bSBaptiste Daroussin if (*cp < ' ')
51361d06d6bSBaptiste Daroussin cp++;
51461d06d6bSBaptiste Daroussin
51561d06d6bSBaptiste Daroussin /* Print a separator before each but the first string. */
51661d06d6bSBaptiste Daroussin if (*i > i_start) {
51761d06d6bSBaptiste Daroussin s = sep;
51861d06d6bSBaptiste Daroussin while (*s != '\0')
51961d06d6bSBaptiste Daroussin buf[(*i)++] = *s++;
52061d06d6bSBaptiste Daroussin }
52161d06d6bSBaptiste Daroussin
52261d06d6bSBaptiste Daroussin /* Copy one string. */
52361d06d6bSBaptiste Daroussin while (*cp != '\0')
52461d06d6bSBaptiste Daroussin buf[(*i)++] = *cp++;
52561d06d6bSBaptiste Daroussin }
52661d06d6bSBaptiste Daroussin
52761d06d6bSBaptiste Daroussin }
52861d06d6bSBaptiste Daroussin
52961d06d6bSBaptiste Daroussin /*
53061d06d6bSBaptiste Daroussin * Return 1 if the string *want occurs in any of the strings
53161d06d6bSBaptiste Daroussin * in the NUL-terminated string list *have, or 0 otherwise.
53261d06d6bSBaptiste Daroussin * If either argument is NULL or empty, assume no filtering
53361d06d6bSBaptiste Daroussin * is desired and return 1.
53461d06d6bSBaptiste Daroussin */
53561d06d6bSBaptiste Daroussin static int
lstmatch(const char * want,const char * have)53661d06d6bSBaptiste Daroussin lstmatch(const char *want, const char *have)
53761d06d6bSBaptiste Daroussin {
53861d06d6bSBaptiste Daroussin if (want == NULL || have == NULL || *have == '\0')
53961d06d6bSBaptiste Daroussin return 1;
54061d06d6bSBaptiste Daroussin while (*have != '\0') {
54161d06d6bSBaptiste Daroussin if (strcasestr(have, want) != NULL)
54261d06d6bSBaptiste Daroussin return 1;
54361d06d6bSBaptiste Daroussin have = strchr(have, '\0') + 1;
54461d06d6bSBaptiste Daroussin }
54561d06d6bSBaptiste Daroussin return 0;
54661d06d6bSBaptiste Daroussin }
54761d06d6bSBaptiste Daroussin
54861d06d6bSBaptiste Daroussin /*
54961d06d6bSBaptiste Daroussin * Build a list of values taken by the macro im in the manual page.
55061d06d6bSBaptiste Daroussin */
55161d06d6bSBaptiste Daroussin static char *
buildoutput(size_t im,struct dbm_page * page)55261d06d6bSBaptiste Daroussin buildoutput(size_t im, struct dbm_page *page)
55361d06d6bSBaptiste Daroussin {
55461d06d6bSBaptiste Daroussin const char *oldoutput, *sep, *input;
55561d06d6bSBaptiste Daroussin char *output, *newoutput, *value;
55661d06d6bSBaptiste Daroussin size_t sz, i;
55761d06d6bSBaptiste Daroussin
55861d06d6bSBaptiste Daroussin switch (im) {
55961d06d6bSBaptiste Daroussin case KEY_Nd:
56061d06d6bSBaptiste Daroussin return mandoc_strdup(page->desc);
56161d06d6bSBaptiste Daroussin case KEY_Nm:
56261d06d6bSBaptiste Daroussin input = page->name;
56361d06d6bSBaptiste Daroussin break;
56461d06d6bSBaptiste Daroussin case KEY_sec:
56561d06d6bSBaptiste Daroussin input = page->sect;
56661d06d6bSBaptiste Daroussin break;
56761d06d6bSBaptiste Daroussin case KEY_arch:
56861d06d6bSBaptiste Daroussin input = page->arch;
56961d06d6bSBaptiste Daroussin if (input == NULL)
57061d06d6bSBaptiste Daroussin input = "all\0";
57161d06d6bSBaptiste Daroussin break;
57261d06d6bSBaptiste Daroussin default:
57361d06d6bSBaptiste Daroussin input = NULL;
57461d06d6bSBaptiste Daroussin break;
57561d06d6bSBaptiste Daroussin }
57661d06d6bSBaptiste Daroussin
57761d06d6bSBaptiste Daroussin if (input != NULL) {
57861d06d6bSBaptiste Daroussin sz = lstlen(input, 3) + 1;
57961d06d6bSBaptiste Daroussin output = mandoc_malloc(sz);
58061d06d6bSBaptiste Daroussin i = 0;
58161d06d6bSBaptiste Daroussin lstcat(output, &i, input, " # ");
58261d06d6bSBaptiste Daroussin output[i++] = '\0';
58361d06d6bSBaptiste Daroussin assert(i == sz);
58461d06d6bSBaptiste Daroussin return output;
58561d06d6bSBaptiste Daroussin }
58661d06d6bSBaptiste Daroussin
58761d06d6bSBaptiste Daroussin output = NULL;
58861d06d6bSBaptiste Daroussin dbm_macro_bypage(im - 2, page->addr);
58961d06d6bSBaptiste Daroussin while ((value = dbm_macro_next()) != NULL) {
59061d06d6bSBaptiste Daroussin if (output == NULL) {
59161d06d6bSBaptiste Daroussin oldoutput = "";
59261d06d6bSBaptiste Daroussin sep = "";
59361d06d6bSBaptiste Daroussin } else {
59461d06d6bSBaptiste Daroussin oldoutput = output;
59561d06d6bSBaptiste Daroussin sep = " # ";
59661d06d6bSBaptiste Daroussin }
59761d06d6bSBaptiste Daroussin mandoc_asprintf(&newoutput, "%s%s%s", oldoutput, sep, value);
59861d06d6bSBaptiste Daroussin free(output);
59961d06d6bSBaptiste Daroussin output = newoutput;
60061d06d6bSBaptiste Daroussin }
60161d06d6bSBaptiste Daroussin return output;
60261d06d6bSBaptiste Daroussin }
60361d06d6bSBaptiste Daroussin
60461d06d6bSBaptiste Daroussin /*
60561d06d6bSBaptiste Daroussin * Compile a set of string tokens into an expression.
60661d06d6bSBaptiste Daroussin * Tokens in "argv" are assumed to be individual expression atoms (e.g.,
60761d06d6bSBaptiste Daroussin * "(", "foo=bar", etc.).
60861d06d6bSBaptiste Daroussin */
60961d06d6bSBaptiste Daroussin static struct expr *
exprcomp(const struct mansearch * search,int argc,char * argv[],int * argi)61061d06d6bSBaptiste Daroussin exprcomp(const struct mansearch *search, int argc, char *argv[], int *argi)
61161d06d6bSBaptiste Daroussin {
61261d06d6bSBaptiste Daroussin struct expr *parent, *child;
61361d06d6bSBaptiste Daroussin int needterm, nested;
61461d06d6bSBaptiste Daroussin
61561d06d6bSBaptiste Daroussin if ((nested = *argi) == argc)
61661d06d6bSBaptiste Daroussin return NULL;
61761d06d6bSBaptiste Daroussin needterm = 1;
61861d06d6bSBaptiste Daroussin parent = child = NULL;
61961d06d6bSBaptiste Daroussin while (*argi < argc) {
62061d06d6bSBaptiste Daroussin if (strcmp(")", argv[*argi]) == 0) {
62161d06d6bSBaptiste Daroussin if (needterm)
62261d06d6bSBaptiste Daroussin warnx("missing term "
62361d06d6bSBaptiste Daroussin "before closing parenthesis");
62461d06d6bSBaptiste Daroussin needterm = 0;
62561d06d6bSBaptiste Daroussin if (nested)
62661d06d6bSBaptiste Daroussin break;
62761d06d6bSBaptiste Daroussin warnx("ignoring unmatched right parenthesis");
62861d06d6bSBaptiste Daroussin ++*argi;
62961d06d6bSBaptiste Daroussin continue;
63061d06d6bSBaptiste Daroussin }
63161d06d6bSBaptiste Daroussin if (strcmp("-o", argv[*argi]) == 0) {
63261d06d6bSBaptiste Daroussin if (needterm) {
63361d06d6bSBaptiste Daroussin if (*argi > 0)
63461d06d6bSBaptiste Daroussin warnx("ignoring -o after %s",
63561d06d6bSBaptiste Daroussin argv[*argi - 1]);
63661d06d6bSBaptiste Daroussin else
63761d06d6bSBaptiste Daroussin warnx("ignoring initial -o");
63861d06d6bSBaptiste Daroussin }
63961d06d6bSBaptiste Daroussin needterm = 1;
64061d06d6bSBaptiste Daroussin ++*argi;
64161d06d6bSBaptiste Daroussin continue;
64261d06d6bSBaptiste Daroussin }
64361d06d6bSBaptiste Daroussin needterm = 0;
64461d06d6bSBaptiste Daroussin if (child == NULL) {
64561d06d6bSBaptiste Daroussin child = expr_and(search, argc, argv, argi);
64661d06d6bSBaptiste Daroussin continue;
64761d06d6bSBaptiste Daroussin }
64861d06d6bSBaptiste Daroussin if (parent == NULL) {
64961d06d6bSBaptiste Daroussin parent = mandoc_calloc(1, sizeof(*parent));
65061d06d6bSBaptiste Daroussin parent->type = EXPR_OR;
65161d06d6bSBaptiste Daroussin parent->next = NULL;
65261d06d6bSBaptiste Daroussin parent->child = child;
65361d06d6bSBaptiste Daroussin }
65461d06d6bSBaptiste Daroussin child->next = expr_and(search, argc, argv, argi);
65561d06d6bSBaptiste Daroussin child = child->next;
65661d06d6bSBaptiste Daroussin }
65761d06d6bSBaptiste Daroussin if (needterm && *argi)
65861d06d6bSBaptiste Daroussin warnx("ignoring trailing %s", argv[*argi - 1]);
65961d06d6bSBaptiste Daroussin return parent == NULL ? child : parent;
66061d06d6bSBaptiste Daroussin }
66161d06d6bSBaptiste Daroussin
66261d06d6bSBaptiste Daroussin static struct expr *
expr_and(const struct mansearch * search,int argc,char * argv[],int * argi)66361d06d6bSBaptiste Daroussin expr_and(const struct mansearch *search, int argc, char *argv[], int *argi)
66461d06d6bSBaptiste Daroussin {
66561d06d6bSBaptiste Daroussin struct expr *parent, *child;
66661d06d6bSBaptiste Daroussin int needterm;
66761d06d6bSBaptiste Daroussin
66861d06d6bSBaptiste Daroussin needterm = 1;
66961d06d6bSBaptiste Daroussin parent = child = NULL;
67061d06d6bSBaptiste Daroussin while (*argi < argc) {
67161d06d6bSBaptiste Daroussin if (strcmp(")", argv[*argi]) == 0) {
67261d06d6bSBaptiste Daroussin if (needterm)
67361d06d6bSBaptiste Daroussin warnx("missing term "
67461d06d6bSBaptiste Daroussin "before closing parenthesis");
67561d06d6bSBaptiste Daroussin needterm = 0;
67661d06d6bSBaptiste Daroussin break;
67761d06d6bSBaptiste Daroussin }
67861d06d6bSBaptiste Daroussin if (strcmp("-o", argv[*argi]) == 0)
67961d06d6bSBaptiste Daroussin break;
68061d06d6bSBaptiste Daroussin if (strcmp("-a", argv[*argi]) == 0) {
68161d06d6bSBaptiste Daroussin if (needterm) {
68261d06d6bSBaptiste Daroussin if (*argi > 0)
68361d06d6bSBaptiste Daroussin warnx("ignoring -a after %s",
68461d06d6bSBaptiste Daroussin argv[*argi - 1]);
68561d06d6bSBaptiste Daroussin else
68661d06d6bSBaptiste Daroussin warnx("ignoring initial -a");
68761d06d6bSBaptiste Daroussin }
68861d06d6bSBaptiste Daroussin needterm = 1;
68961d06d6bSBaptiste Daroussin ++*argi;
69061d06d6bSBaptiste Daroussin continue;
69161d06d6bSBaptiste Daroussin }
69261d06d6bSBaptiste Daroussin if (needterm == 0)
69361d06d6bSBaptiste Daroussin break;
69461d06d6bSBaptiste Daroussin if (child == NULL) {
69561d06d6bSBaptiste Daroussin child = exprterm(search, argc, argv, argi);
69661d06d6bSBaptiste Daroussin if (child != NULL)
69761d06d6bSBaptiste Daroussin needterm = 0;
69861d06d6bSBaptiste Daroussin continue;
69961d06d6bSBaptiste Daroussin }
70061d06d6bSBaptiste Daroussin needterm = 0;
70161d06d6bSBaptiste Daroussin if (parent == NULL) {
70261d06d6bSBaptiste Daroussin parent = mandoc_calloc(1, sizeof(*parent));
70361d06d6bSBaptiste Daroussin parent->type = EXPR_AND;
70461d06d6bSBaptiste Daroussin parent->next = NULL;
70561d06d6bSBaptiste Daroussin parent->child = child;
70661d06d6bSBaptiste Daroussin }
70761d06d6bSBaptiste Daroussin child->next = exprterm(search, argc, argv, argi);
70861d06d6bSBaptiste Daroussin if (child->next != NULL) {
70961d06d6bSBaptiste Daroussin child = child->next;
71061d06d6bSBaptiste Daroussin needterm = 0;
71161d06d6bSBaptiste Daroussin }
71261d06d6bSBaptiste Daroussin }
71361d06d6bSBaptiste Daroussin if (needterm && *argi)
71461d06d6bSBaptiste Daroussin warnx("ignoring trailing %s", argv[*argi - 1]);
71561d06d6bSBaptiste Daroussin return parent == NULL ? child : parent;
71661d06d6bSBaptiste Daroussin }
71761d06d6bSBaptiste Daroussin
71861d06d6bSBaptiste Daroussin static struct expr *
exprterm(const struct mansearch * search,int argc,char * argv[],int * argi)71961d06d6bSBaptiste Daroussin exprterm(const struct mansearch *search, int argc, char *argv[], int *argi)
72061d06d6bSBaptiste Daroussin {
72161d06d6bSBaptiste Daroussin char errbuf[BUFSIZ];
72261d06d6bSBaptiste Daroussin struct expr *e;
72361d06d6bSBaptiste Daroussin char *key, *val;
72461d06d6bSBaptiste Daroussin uint64_t iterbit;
72561d06d6bSBaptiste Daroussin int cs, i, irc;
72661d06d6bSBaptiste Daroussin
72761d06d6bSBaptiste Daroussin if (strcmp("(", argv[*argi]) == 0) {
72861d06d6bSBaptiste Daroussin ++*argi;
72961d06d6bSBaptiste Daroussin e = exprcomp(search, argc, argv, argi);
73061d06d6bSBaptiste Daroussin if (*argi < argc) {
73161d06d6bSBaptiste Daroussin assert(strcmp(")", argv[*argi]) == 0);
73261d06d6bSBaptiste Daroussin ++*argi;
73361d06d6bSBaptiste Daroussin } else
73461d06d6bSBaptiste Daroussin warnx("unclosed parenthesis");
73561d06d6bSBaptiste Daroussin return e;
73661d06d6bSBaptiste Daroussin }
73761d06d6bSBaptiste Daroussin
73861d06d6bSBaptiste Daroussin if (strcmp("-i", argv[*argi]) == 0 && *argi + 1 < argc) {
73961d06d6bSBaptiste Daroussin cs = 0;
74061d06d6bSBaptiste Daroussin ++*argi;
74161d06d6bSBaptiste Daroussin } else
74261d06d6bSBaptiste Daroussin cs = 1;
74361d06d6bSBaptiste Daroussin
74461d06d6bSBaptiste Daroussin e = mandoc_calloc(1, sizeof(*e));
74561d06d6bSBaptiste Daroussin e->type = EXPR_TERM;
74661d06d6bSBaptiste Daroussin e->bits = 0;
74761d06d6bSBaptiste Daroussin e->next = NULL;
74861d06d6bSBaptiste Daroussin e->child = NULL;
74961d06d6bSBaptiste Daroussin
75061d06d6bSBaptiste Daroussin if (search->argmode == ARG_NAME) {
75161d06d6bSBaptiste Daroussin e->bits = TYPE_Nm;
75261d06d6bSBaptiste Daroussin e->match.type = DBM_EXACT;
75361d06d6bSBaptiste Daroussin e->match.str = argv[(*argi)++];
75461d06d6bSBaptiste Daroussin return e;
75561d06d6bSBaptiste Daroussin }
75661d06d6bSBaptiste Daroussin
75761d06d6bSBaptiste Daroussin /*
75861d06d6bSBaptiste Daroussin * Separate macro keys from search string.
75961d06d6bSBaptiste Daroussin * If needed, request regular expression handling.
76061d06d6bSBaptiste Daroussin */
76161d06d6bSBaptiste Daroussin
76261d06d6bSBaptiste Daroussin if (search->argmode == ARG_WORD) {
76361d06d6bSBaptiste Daroussin e->bits = TYPE_Nm;
76461d06d6bSBaptiste Daroussin e->match.type = DBM_REGEX;
76561d06d6bSBaptiste Daroussin #if HAVE_REWB_BSD
76661d06d6bSBaptiste Daroussin mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", argv[*argi]);
76761d06d6bSBaptiste Daroussin #elif HAVE_REWB_SYSV
76861d06d6bSBaptiste Daroussin mandoc_asprintf(&val, "\\<%s\\>", argv[*argi]);
76961d06d6bSBaptiste Daroussin #else
77061d06d6bSBaptiste Daroussin mandoc_asprintf(&val,
77161d06d6bSBaptiste Daroussin "(^|[^a-zA-Z01-9_])%s([^a-zA-Z01-9_]|$)", argv[*argi]);
77261d06d6bSBaptiste Daroussin #endif
77361d06d6bSBaptiste Daroussin cs = 0;
77461d06d6bSBaptiste Daroussin } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) {
77561d06d6bSBaptiste Daroussin e->bits = TYPE_Nm | TYPE_Nd;
7767295610fSBaptiste Daroussin e->match.type = DBM_REGEX;
7777295610fSBaptiste Daroussin val = argv[*argi];
7787295610fSBaptiste Daroussin cs = 0;
77961d06d6bSBaptiste Daroussin } else {
78061d06d6bSBaptiste Daroussin if (val == argv[*argi])
78161d06d6bSBaptiste Daroussin e->bits = TYPE_Nm | TYPE_Nd;
78261d06d6bSBaptiste Daroussin if (*val == '=') {
78361d06d6bSBaptiste Daroussin e->match.type = DBM_SUB;
78461d06d6bSBaptiste Daroussin e->match.str = val + 1;
78561d06d6bSBaptiste Daroussin } else
78661d06d6bSBaptiste Daroussin e->match.type = DBM_REGEX;
78761d06d6bSBaptiste Daroussin *val++ = '\0';
78861d06d6bSBaptiste Daroussin if (strstr(argv[*argi], "arch") != NULL)
78961d06d6bSBaptiste Daroussin cs = 0;
79061d06d6bSBaptiste Daroussin }
79161d06d6bSBaptiste Daroussin
79261d06d6bSBaptiste Daroussin /* Compile regular expressions. */
79361d06d6bSBaptiste Daroussin
79461d06d6bSBaptiste Daroussin if (e->match.type == DBM_REGEX) {
79561d06d6bSBaptiste Daroussin e->match.re = mandoc_malloc(sizeof(*e->match.re));
79661d06d6bSBaptiste Daroussin irc = regcomp(e->match.re, val,
79761d06d6bSBaptiste Daroussin REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
79861d06d6bSBaptiste Daroussin if (irc) {
79961d06d6bSBaptiste Daroussin regerror(irc, e->match.re, errbuf, sizeof(errbuf));
80061d06d6bSBaptiste Daroussin warnx("regcomp /%s/: %s", val, errbuf);
80161d06d6bSBaptiste Daroussin }
80261d06d6bSBaptiste Daroussin if (search->argmode == ARG_WORD)
80361d06d6bSBaptiste Daroussin free(val);
80461d06d6bSBaptiste Daroussin if (irc) {
80561d06d6bSBaptiste Daroussin free(e->match.re);
80661d06d6bSBaptiste Daroussin free(e);
80761d06d6bSBaptiste Daroussin ++*argi;
80861d06d6bSBaptiste Daroussin return NULL;
80961d06d6bSBaptiste Daroussin }
81061d06d6bSBaptiste Daroussin }
81161d06d6bSBaptiste Daroussin
81261d06d6bSBaptiste Daroussin if (e->bits) {
81361d06d6bSBaptiste Daroussin ++*argi;
81461d06d6bSBaptiste Daroussin return e;
81561d06d6bSBaptiste Daroussin }
81661d06d6bSBaptiste Daroussin
81761d06d6bSBaptiste Daroussin /*
81861d06d6bSBaptiste Daroussin * Parse out all possible fields.
81961d06d6bSBaptiste Daroussin * If the field doesn't resolve, bail.
82061d06d6bSBaptiste Daroussin */
82161d06d6bSBaptiste Daroussin
82261d06d6bSBaptiste Daroussin while (NULL != (key = strsep(&argv[*argi], ","))) {
82361d06d6bSBaptiste Daroussin if ('\0' == *key)
82461d06d6bSBaptiste Daroussin continue;
82561d06d6bSBaptiste Daroussin for (i = 0, iterbit = 1; i < KEY_MAX; i++, iterbit <<= 1) {
82661d06d6bSBaptiste Daroussin if (0 == strcasecmp(key, mansearch_keynames[i])) {
82761d06d6bSBaptiste Daroussin e->bits |= iterbit;
82861d06d6bSBaptiste Daroussin break;
82961d06d6bSBaptiste Daroussin }
83061d06d6bSBaptiste Daroussin }
83161d06d6bSBaptiste Daroussin if (i == KEY_MAX) {
83261d06d6bSBaptiste Daroussin if (strcasecmp(key, "any"))
83361d06d6bSBaptiste Daroussin warnx("treating unknown key "
83461d06d6bSBaptiste Daroussin "\"%s\" as \"any\"", key);
83561d06d6bSBaptiste Daroussin e->bits |= ~0ULL;
83661d06d6bSBaptiste Daroussin }
83761d06d6bSBaptiste Daroussin }
83861d06d6bSBaptiste Daroussin
83961d06d6bSBaptiste Daroussin ++*argi;
84061d06d6bSBaptiste Daroussin return e;
84161d06d6bSBaptiste Daroussin }
84261d06d6bSBaptiste Daroussin
84361d06d6bSBaptiste Daroussin static void
exprfree(struct expr * e)84461d06d6bSBaptiste Daroussin exprfree(struct expr *e)
84561d06d6bSBaptiste Daroussin {
84661d06d6bSBaptiste Daroussin if (e->next != NULL)
84761d06d6bSBaptiste Daroussin exprfree(e->next);
84861d06d6bSBaptiste Daroussin if (e->child != NULL)
84961d06d6bSBaptiste Daroussin exprfree(e->child);
85061d06d6bSBaptiste Daroussin free(e);
85161d06d6bSBaptiste Daroussin }
852