1*4d131170SRobert Mustacchi /* $Id: mansearch.c,v 1.82 2019/07/01 22:56:24 schwarze Exp $ */
2a40ea1a7SYuri Pankov /*
3a40ea1a7SYuri Pankov * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
4cec8643bSMichal Nowak * Copyright (c) 2013-2018 Ingo Schwarze <schwarze@openbsd.org>
5a40ea1a7SYuri Pankov *
6a40ea1a7SYuri Pankov * Permission to use, copy, modify, and distribute this software for any
7a40ea1a7SYuri Pankov * purpose with or without fee is hereby granted, provided that the above
8a40ea1a7SYuri Pankov * copyright notice and this permission notice appear in all copies.
9a40ea1a7SYuri Pankov *
10a40ea1a7SYuri Pankov * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
11a40ea1a7SYuri Pankov * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a40ea1a7SYuri Pankov * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
13a40ea1a7SYuri Pankov * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a40ea1a7SYuri Pankov * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a40ea1a7SYuri Pankov * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a40ea1a7SYuri Pankov * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a40ea1a7SYuri Pankov */
18a40ea1a7SYuri Pankov #include "config.h"
19a40ea1a7SYuri Pankov
20a40ea1a7SYuri Pankov #include <sys/mman.h>
21a40ea1a7SYuri Pankov #include <sys/types.h>
22a40ea1a7SYuri Pankov
23a40ea1a7SYuri Pankov #include <assert.h>
24a40ea1a7SYuri Pankov #if HAVE_ERR
25a40ea1a7SYuri Pankov #include <err.h>
26a40ea1a7SYuri Pankov #endif
27a40ea1a7SYuri Pankov #include <errno.h>
28a40ea1a7SYuri Pankov #include <fcntl.h>
29a40ea1a7SYuri Pankov #include <glob.h>
30a40ea1a7SYuri Pankov #include <limits.h>
31a40ea1a7SYuri Pankov #include <regex.h>
32a40ea1a7SYuri Pankov #include <stdio.h>
33a40ea1a7SYuri Pankov #include <stdint.h>
34a40ea1a7SYuri Pankov #include <stddef.h>
35a40ea1a7SYuri Pankov #include <stdlib.h>
36a40ea1a7SYuri Pankov #include <string.h>
37a40ea1a7SYuri Pankov #include <unistd.h>
38a40ea1a7SYuri Pankov
39a40ea1a7SYuri Pankov #include "mandoc_aux.h"
40a40ea1a7SYuri Pankov #include "mandoc_ohash.h"
41a40ea1a7SYuri Pankov #include "manconf.h"
42a40ea1a7SYuri Pankov #include "mansearch.h"
43a40ea1a7SYuri Pankov #include "dbm.h"
44a40ea1a7SYuri Pankov
45a40ea1a7SYuri Pankov struct expr {
46a40ea1a7SYuri Pankov /* Used for terms: */
47a40ea1a7SYuri Pankov struct dbm_match match; /* Match type and expression. */
48a40ea1a7SYuri Pankov uint64_t bits; /* Type mask. */
49a40ea1a7SYuri Pankov /* Used for OR and AND groups: */
50a40ea1a7SYuri Pankov struct expr *next; /* Next child in the parent group. */
51a40ea1a7SYuri Pankov struct expr *child; /* First child in this group. */
52a40ea1a7SYuri Pankov enum { EXPR_TERM, EXPR_OR, EXPR_AND } type;
53a40ea1a7SYuri Pankov };
54a40ea1a7SYuri Pankov
55a40ea1a7SYuri Pankov const char *const mansearch_keynames[KEY_MAX] = {
56a40ea1a7SYuri Pankov "arch", "sec", "Xr", "Ar", "Fa", "Fl", "Dv", "Fn",
57a40ea1a7SYuri Pankov "Ic", "Pa", "Cm", "Li", "Em", "Cd", "Va", "Ft",
58a40ea1a7SYuri Pankov "Tn", "Er", "Ev", "Sy", "Sh", "In", "Ss", "Ox",
59a40ea1a7SYuri Pankov "An", "Mt", "St", "Bx", "At", "Nx", "Fx", "Lk",
60a40ea1a7SYuri Pankov "Ms", "Bsx", "Dx", "Rs", "Vt", "Lb", "Nm", "Nd"
61a40ea1a7SYuri Pankov };
62a40ea1a7SYuri Pankov
63a40ea1a7SYuri Pankov
64a40ea1a7SYuri Pankov static struct ohash *manmerge(struct expr *, struct ohash *);
65a40ea1a7SYuri Pankov static struct ohash *manmerge_term(struct expr *, struct ohash *);
66a40ea1a7SYuri Pankov static struct ohash *manmerge_or(struct expr *, struct ohash *);
67a40ea1a7SYuri Pankov static struct ohash *manmerge_and(struct expr *, struct ohash *);
68a40ea1a7SYuri Pankov static char *buildnames(const struct dbm_page *);
69c66b8046SYuri Pankov static char *buildoutput(size_t, struct dbm_page *);
70c66b8046SYuri Pankov static size_t lstlen(const char *, size_t);
71c66b8046SYuri Pankov static void lstcat(char *, size_t *, const char *, const char *);
72a40ea1a7SYuri Pankov static int lstmatch(const char *, const char *);
73a40ea1a7SYuri Pankov static struct expr *exprcomp(const struct mansearch *,
74a40ea1a7SYuri Pankov int, char *[], int *);
75a40ea1a7SYuri Pankov static struct expr *expr_and(const struct mansearch *,
76a40ea1a7SYuri Pankov int, char *[], int *);
77a40ea1a7SYuri Pankov static struct expr *exprterm(const struct mansearch *,
78a40ea1a7SYuri Pankov int, char *[], int *);
79a40ea1a7SYuri Pankov static void exprfree(struct expr *);
80a40ea1a7SYuri Pankov static int manpage_compare(const void *, const void *);
81a40ea1a7SYuri Pankov
82a40ea1a7SYuri Pankov
83a40ea1a7SYuri Pankov int
mansearch(const struct mansearch * search,const struct manpaths * paths,int argc,char * argv[],struct manpage ** res,size_t * sz)84a40ea1a7SYuri Pankov mansearch(const struct mansearch *search,
85a40ea1a7SYuri Pankov const struct manpaths *paths,
86a40ea1a7SYuri Pankov int argc, char *argv[],
87a40ea1a7SYuri Pankov struct manpage **res, size_t *sz)
88a40ea1a7SYuri Pankov {
89a40ea1a7SYuri Pankov char buf[PATH_MAX];
90a40ea1a7SYuri Pankov struct dbm_res *rp;
91a40ea1a7SYuri Pankov struct expr *e;
92a40ea1a7SYuri Pankov struct dbm_page *page;
93a40ea1a7SYuri Pankov struct manpage *mpage;
94a40ea1a7SYuri Pankov struct ohash *htab;
95a40ea1a7SYuri Pankov size_t cur, i, maxres, outkey;
96a40ea1a7SYuri Pankov unsigned int slot;
97a40ea1a7SYuri Pankov int argi, chdir_status, getcwd_status, im;
98a40ea1a7SYuri Pankov
99a40ea1a7SYuri Pankov argi = 0;
100a40ea1a7SYuri Pankov if ((e = exprcomp(search, argc, argv, &argi)) == NULL) {
101a40ea1a7SYuri Pankov *sz = 0;
102a40ea1a7SYuri Pankov return 0;
103a40ea1a7SYuri Pankov }
104a40ea1a7SYuri Pankov
105a40ea1a7SYuri Pankov cur = maxres = 0;
106c66b8046SYuri Pankov if (res != NULL)
107a40ea1a7SYuri Pankov *res = NULL;
108a40ea1a7SYuri Pankov
109a40ea1a7SYuri Pankov outkey = KEY_Nd;
110a40ea1a7SYuri Pankov if (search->outkey != NULL)
111a40ea1a7SYuri Pankov for (im = 0; im < KEY_MAX; im++)
112a40ea1a7SYuri Pankov if (0 == strcasecmp(search->outkey,
113a40ea1a7SYuri Pankov mansearch_keynames[im])) {
114a40ea1a7SYuri Pankov outkey = im;
115a40ea1a7SYuri Pankov break;
116a40ea1a7SYuri Pankov }
117a40ea1a7SYuri Pankov
118a40ea1a7SYuri Pankov /*
119a40ea1a7SYuri Pankov * Remember the original working directory, if possible.
120a40ea1a7SYuri Pankov * This will be needed if the second or a later directory
121a40ea1a7SYuri Pankov * is given as a relative path.
122a40ea1a7SYuri Pankov * Do not error out if the current directory is not
123a40ea1a7SYuri Pankov * searchable: Maybe it won't be needed after all.
124a40ea1a7SYuri Pankov */
125a40ea1a7SYuri Pankov
126a40ea1a7SYuri Pankov if (getcwd(buf, PATH_MAX) == NULL) {
127a40ea1a7SYuri Pankov getcwd_status = 0;
128a40ea1a7SYuri Pankov (void)strlcpy(buf, strerror(errno), sizeof(buf));
129a40ea1a7SYuri Pankov } else
130a40ea1a7SYuri Pankov getcwd_status = 1;
131a40ea1a7SYuri Pankov
132a40ea1a7SYuri Pankov /*
133a40ea1a7SYuri Pankov * Loop over the directories (containing databases) for us to
134a40ea1a7SYuri Pankov * search.
135a40ea1a7SYuri Pankov * Don't let missing/bad databases/directories phase us.
136a40ea1a7SYuri Pankov * In each, try to open the resident database and, if it opens,
137a40ea1a7SYuri Pankov * scan it for our match expression.
138a40ea1a7SYuri Pankov */
139a40ea1a7SYuri Pankov
140a40ea1a7SYuri Pankov chdir_status = 0;
141a40ea1a7SYuri Pankov for (i = 0; i < paths->sz; i++) {
142a40ea1a7SYuri Pankov if (chdir_status && paths->paths[i][0] != '/') {
143a40ea1a7SYuri Pankov if ( ! getcwd_status) {
144a40ea1a7SYuri Pankov warnx("%s: getcwd: %s", paths->paths[i], buf);
145a40ea1a7SYuri Pankov continue;
146a40ea1a7SYuri Pankov } else if (chdir(buf) == -1) {
147a40ea1a7SYuri Pankov warn("%s", buf);
148a40ea1a7SYuri Pankov continue;
149a40ea1a7SYuri Pankov }
150a40ea1a7SYuri Pankov }
151a40ea1a7SYuri Pankov if (chdir(paths->paths[i]) == -1) {
152a40ea1a7SYuri Pankov warn("%s", paths->paths[i]);
153a40ea1a7SYuri Pankov continue;
154a40ea1a7SYuri Pankov }
155a40ea1a7SYuri Pankov chdir_status = 1;
156a40ea1a7SYuri Pankov
157a40ea1a7SYuri Pankov if (dbm_open(MANDOC_DB) == -1) {
158c66b8046SYuri Pankov if (errno != ENOENT)
159a40ea1a7SYuri Pankov warn("%s/%s", paths->paths[i], MANDOC_DB);
160a40ea1a7SYuri Pankov continue;
161a40ea1a7SYuri Pankov }
162a40ea1a7SYuri Pankov
163a40ea1a7SYuri Pankov if ((htab = manmerge(e, NULL)) == NULL) {
164a40ea1a7SYuri Pankov dbm_close();
165a40ea1a7SYuri Pankov continue;
166a40ea1a7SYuri Pankov }
167a40ea1a7SYuri Pankov
168a40ea1a7SYuri Pankov for (rp = ohash_first(htab, &slot); rp != NULL;
169a40ea1a7SYuri Pankov rp = ohash_next(htab, &slot)) {
170a40ea1a7SYuri Pankov page = dbm_page_get(rp->page);
171a40ea1a7SYuri Pankov
172a40ea1a7SYuri Pankov if (lstmatch(search->sec, page->sect) == 0 ||
173c66b8046SYuri Pankov lstmatch(search->arch, page->arch) == 0 ||
174c66b8046SYuri Pankov (search->argmode == ARG_NAME &&
175c66b8046SYuri Pankov rp->bits <= (int32_t)(NAME_SYN & NAME_MASK)))
176a40ea1a7SYuri Pankov continue;
177a40ea1a7SYuri Pankov
178c66b8046SYuri Pankov if (res == NULL) {
179c66b8046SYuri Pankov cur = 1;
180c66b8046SYuri Pankov break;
181c66b8046SYuri Pankov }
182a40ea1a7SYuri Pankov if (cur + 1 > maxres) {
183a40ea1a7SYuri Pankov maxres += 1024;
184a40ea1a7SYuri Pankov *res = mandoc_reallocarray(*res,
185a40ea1a7SYuri Pankov maxres, sizeof(**res));
186a40ea1a7SYuri Pankov }
187a40ea1a7SYuri Pankov mpage = *res + cur;
188a40ea1a7SYuri Pankov mandoc_asprintf(&mpage->file, "%s/%s",
189a40ea1a7SYuri Pankov paths->paths[i], page->file + 1);
1906640c13bSYuri Pankov if (access(chdir_status ? page->file + 1 :
1916640c13bSYuri Pankov mpage->file, R_OK) == -1) {
1926640c13bSYuri Pankov warn("%s", mpage->file);
1936640c13bSYuri Pankov warnx("outdated mandoc.db contains "
1946640c13bSYuri Pankov "bogus %s entry, run makewhatis %s",
1956640c13bSYuri Pankov page->file + 1, paths->paths[i]);
1966640c13bSYuri Pankov free(mpage->file);
1976640c13bSYuri Pankov free(rp);
1986640c13bSYuri Pankov continue;
1996640c13bSYuri Pankov }
200a40ea1a7SYuri Pankov mpage->names = buildnames(page);
201c66b8046SYuri Pankov mpage->output = buildoutput(outkey, page);
202*4d131170SRobert Mustacchi mpage->bits = search->firstmatch ? rp->bits : 0;
203a40ea1a7SYuri Pankov mpage->ipath = i;
204a40ea1a7SYuri Pankov mpage->sec = *page->sect - '0';
205a40ea1a7SYuri Pankov if (mpage->sec < 0 || mpage->sec > 9)
206a40ea1a7SYuri Pankov mpage->sec = 10;
207a40ea1a7SYuri Pankov mpage->form = *page->file;
208a40ea1a7SYuri Pankov free(rp);
209a40ea1a7SYuri Pankov cur++;
210a40ea1a7SYuri Pankov }
211a40ea1a7SYuri Pankov ohash_delete(htab);
212a40ea1a7SYuri Pankov free(htab);
213a40ea1a7SYuri Pankov dbm_close();
214a40ea1a7SYuri Pankov
215a40ea1a7SYuri Pankov /*
216a40ea1a7SYuri Pankov * In man(1) mode, prefer matches in earlier trees
217a40ea1a7SYuri Pankov * over matches in later trees.
218a40ea1a7SYuri Pankov */
219a40ea1a7SYuri Pankov
220a40ea1a7SYuri Pankov if (cur && search->firstmatch)
221a40ea1a7SYuri Pankov break;
222a40ea1a7SYuri Pankov }
223c66b8046SYuri Pankov if (res != NULL)
224a40ea1a7SYuri Pankov qsort(*res, cur, sizeof(struct manpage), manpage_compare);
225a40ea1a7SYuri Pankov if (chdir_status && getcwd_status && chdir(buf) == -1)
226a40ea1a7SYuri Pankov warn("%s", buf);
227a40ea1a7SYuri Pankov exprfree(e);
228a40ea1a7SYuri Pankov *sz = cur;
229c66b8046SYuri Pankov return res != NULL || cur;
230a40ea1a7SYuri Pankov }
231a40ea1a7SYuri Pankov
232a40ea1a7SYuri Pankov /*
233a40ea1a7SYuri Pankov * Merge the results for the expression tree rooted at e
234a40ea1a7SYuri Pankov * into the the result list htab.
235a40ea1a7SYuri Pankov */
236a40ea1a7SYuri Pankov static struct ohash *
manmerge(struct expr * e,struct ohash * htab)237a40ea1a7SYuri Pankov manmerge(struct expr *e, struct ohash *htab)
238a40ea1a7SYuri Pankov {
239a40ea1a7SYuri Pankov switch (e->type) {
240a40ea1a7SYuri Pankov case EXPR_TERM:
241a40ea1a7SYuri Pankov return manmerge_term(e, htab);
242a40ea1a7SYuri Pankov case EXPR_OR:
243a40ea1a7SYuri Pankov return manmerge_or(e->child, htab);
244a40ea1a7SYuri Pankov case EXPR_AND:
245a40ea1a7SYuri Pankov return manmerge_and(e->child, htab);
246a40ea1a7SYuri Pankov default:
247a40ea1a7SYuri Pankov abort();
248a40ea1a7SYuri Pankov }
249a40ea1a7SYuri Pankov }
250a40ea1a7SYuri Pankov
251a40ea1a7SYuri Pankov static struct ohash *
manmerge_term(struct expr * e,struct ohash * htab)252a40ea1a7SYuri Pankov manmerge_term(struct expr *e, struct ohash *htab)
253a40ea1a7SYuri Pankov {
254a40ea1a7SYuri Pankov struct dbm_res res, *rp;
255a40ea1a7SYuri Pankov uint64_t ib;
256a40ea1a7SYuri Pankov unsigned int slot;
257a40ea1a7SYuri Pankov int im;
258a40ea1a7SYuri Pankov
259a40ea1a7SYuri Pankov if (htab == NULL) {
260a40ea1a7SYuri Pankov htab = mandoc_malloc(sizeof(*htab));
261a40ea1a7SYuri Pankov mandoc_ohash_init(htab, 4, offsetof(struct dbm_res, page));
262a40ea1a7SYuri Pankov }
263a40ea1a7SYuri Pankov
264a40ea1a7SYuri Pankov for (im = 0, ib = 1; im < KEY_MAX; im++, ib <<= 1) {
265a40ea1a7SYuri Pankov if ((e->bits & ib) == 0)
266a40ea1a7SYuri Pankov continue;
267a40ea1a7SYuri Pankov
268a40ea1a7SYuri Pankov switch (ib) {
269a40ea1a7SYuri Pankov case TYPE_arch:
270a40ea1a7SYuri Pankov dbm_page_byarch(&e->match);
271a40ea1a7SYuri Pankov break;
272a40ea1a7SYuri Pankov case TYPE_sec:
273a40ea1a7SYuri Pankov dbm_page_bysect(&e->match);
274a40ea1a7SYuri Pankov break;
275a40ea1a7SYuri Pankov case TYPE_Nm:
276a40ea1a7SYuri Pankov dbm_page_byname(&e->match);
277a40ea1a7SYuri Pankov break;
278a40ea1a7SYuri Pankov case TYPE_Nd:
279a40ea1a7SYuri Pankov dbm_page_bydesc(&e->match);
280a40ea1a7SYuri Pankov break;
281a40ea1a7SYuri Pankov default:
282a40ea1a7SYuri Pankov dbm_page_bymacro(im - 2, &e->match);
283a40ea1a7SYuri Pankov break;
284a40ea1a7SYuri Pankov }
285a40ea1a7SYuri Pankov
286a40ea1a7SYuri Pankov /*
287a40ea1a7SYuri Pankov * When hashing for deduplication, use the unique
288a40ea1a7SYuri Pankov * page ID itself instead of a hash function;
289a40ea1a7SYuri Pankov * that is quite efficient.
290a40ea1a7SYuri Pankov */
291a40ea1a7SYuri Pankov
292a40ea1a7SYuri Pankov for (;;) {
293a40ea1a7SYuri Pankov res = dbm_page_next();
294a40ea1a7SYuri Pankov if (res.page == -1)
295a40ea1a7SYuri Pankov break;
296a40ea1a7SYuri Pankov slot = ohash_lookup_memory(htab,
297a40ea1a7SYuri Pankov (char *)&res, sizeof(res.page), res.page);
298*4d131170SRobert Mustacchi if ((rp = ohash_find(htab, slot)) != NULL) {
299*4d131170SRobert Mustacchi rp->bits |= res.bits;
300a40ea1a7SYuri Pankov continue;
301*4d131170SRobert Mustacchi }
302a40ea1a7SYuri Pankov rp = mandoc_malloc(sizeof(*rp));
303a40ea1a7SYuri Pankov *rp = res;
304a40ea1a7SYuri Pankov ohash_insert(htab, slot, rp);
305a40ea1a7SYuri Pankov }
306a40ea1a7SYuri Pankov }
307a40ea1a7SYuri Pankov return htab;
308a40ea1a7SYuri Pankov }
309a40ea1a7SYuri Pankov
310a40ea1a7SYuri Pankov static struct ohash *
manmerge_or(struct expr * e,struct ohash * htab)311a40ea1a7SYuri Pankov manmerge_or(struct expr *e, struct ohash *htab)
312a40ea1a7SYuri Pankov {
313a40ea1a7SYuri Pankov while (e != NULL) {
314a40ea1a7SYuri Pankov htab = manmerge(e, htab);
315a40ea1a7SYuri Pankov e = e->next;
316a40ea1a7SYuri Pankov }
317a40ea1a7SYuri Pankov return htab;
318a40ea1a7SYuri Pankov }
319a40ea1a7SYuri Pankov
320a40ea1a7SYuri Pankov static struct ohash *
manmerge_and(struct expr * e,struct ohash * htab)321a40ea1a7SYuri Pankov manmerge_and(struct expr *e, struct ohash *htab)
322a40ea1a7SYuri Pankov {
323a40ea1a7SYuri Pankov struct ohash *hand, *h1, *h2;
324a40ea1a7SYuri Pankov struct dbm_res *res;
325a40ea1a7SYuri Pankov unsigned int slot1, slot2;
326a40ea1a7SYuri Pankov
327a40ea1a7SYuri Pankov /* Evaluate the first term of the AND clause. */
328a40ea1a7SYuri Pankov
329a40ea1a7SYuri Pankov hand = manmerge(e, NULL);
330a40ea1a7SYuri Pankov
331a40ea1a7SYuri Pankov while ((e = e->next) != NULL) {
332a40ea1a7SYuri Pankov
333a40ea1a7SYuri Pankov /* Evaluate the next term and prepare for ANDing. */
334a40ea1a7SYuri Pankov
335a40ea1a7SYuri Pankov h2 = manmerge(e, NULL);
336a40ea1a7SYuri Pankov if (ohash_entries(h2) < ohash_entries(hand)) {
337a40ea1a7SYuri Pankov h1 = h2;
338a40ea1a7SYuri Pankov h2 = hand;
339a40ea1a7SYuri Pankov } else
340a40ea1a7SYuri Pankov h1 = hand;
341a40ea1a7SYuri Pankov hand = mandoc_malloc(sizeof(*hand));
342a40ea1a7SYuri Pankov mandoc_ohash_init(hand, 4, offsetof(struct dbm_res, page));
343a40ea1a7SYuri Pankov
344a40ea1a7SYuri Pankov /* Keep all pages that are in both result sets. */
345a40ea1a7SYuri Pankov
346a40ea1a7SYuri Pankov for (res = ohash_first(h1, &slot1); res != NULL;
347a40ea1a7SYuri Pankov res = ohash_next(h1, &slot1)) {
348a40ea1a7SYuri Pankov if (ohash_find(h2, ohash_lookup_memory(h2,
349a40ea1a7SYuri Pankov (char *)res, sizeof(res->page),
350a40ea1a7SYuri Pankov res->page)) == NULL)
351a40ea1a7SYuri Pankov free(res);
352a40ea1a7SYuri Pankov else
353a40ea1a7SYuri Pankov ohash_insert(hand, ohash_lookup_memory(hand,
354a40ea1a7SYuri Pankov (char *)res, sizeof(res->page),
355a40ea1a7SYuri Pankov res->page), res);
356a40ea1a7SYuri Pankov }
357a40ea1a7SYuri Pankov
358a40ea1a7SYuri Pankov /* Discard the merged results. */
359a40ea1a7SYuri Pankov
360a40ea1a7SYuri Pankov for (res = ohash_first(h2, &slot2); res != NULL;
361a40ea1a7SYuri Pankov res = ohash_next(h2, &slot2))
362a40ea1a7SYuri Pankov free(res);
363a40ea1a7SYuri Pankov ohash_delete(h2);
364a40ea1a7SYuri Pankov free(h2);
365a40ea1a7SYuri Pankov ohash_delete(h1);
366a40ea1a7SYuri Pankov free(h1);
367a40ea1a7SYuri Pankov }
368a40ea1a7SYuri Pankov
369a40ea1a7SYuri Pankov /* Merge the result of the AND into htab. */
370a40ea1a7SYuri Pankov
371a40ea1a7SYuri Pankov if (htab == NULL)
372a40ea1a7SYuri Pankov return hand;
373a40ea1a7SYuri Pankov
374a40ea1a7SYuri Pankov for (res = ohash_first(hand, &slot1); res != NULL;
375a40ea1a7SYuri Pankov res = ohash_next(hand, &slot1)) {
376a40ea1a7SYuri Pankov slot2 = ohash_lookup_memory(htab,
377a40ea1a7SYuri Pankov (char *)res, sizeof(res->page), res->page);
378a40ea1a7SYuri Pankov if (ohash_find(htab, slot2) == NULL)
379a40ea1a7SYuri Pankov ohash_insert(htab, slot2, res);
380a40ea1a7SYuri Pankov else
381a40ea1a7SYuri Pankov free(res);
382a40ea1a7SYuri Pankov }
383a40ea1a7SYuri Pankov
384a40ea1a7SYuri Pankov /* Discard the merged result. */
385a40ea1a7SYuri Pankov
386a40ea1a7SYuri Pankov ohash_delete(hand);
387a40ea1a7SYuri Pankov free(hand);
388a40ea1a7SYuri Pankov return htab;
389a40ea1a7SYuri Pankov }
390a40ea1a7SYuri Pankov
391a40ea1a7SYuri Pankov void
mansearch_free(struct manpage * res,size_t sz)392a40ea1a7SYuri Pankov mansearch_free(struct manpage *res, size_t sz)
393a40ea1a7SYuri Pankov {
394a40ea1a7SYuri Pankov size_t i;
395a40ea1a7SYuri Pankov
396a40ea1a7SYuri Pankov for (i = 0; i < sz; i++) {
397a40ea1a7SYuri Pankov free(res[i].file);
398a40ea1a7SYuri Pankov free(res[i].names);
399a40ea1a7SYuri Pankov free(res[i].output);
400a40ea1a7SYuri Pankov }
401a40ea1a7SYuri Pankov free(res);
402a40ea1a7SYuri Pankov }
403a40ea1a7SYuri Pankov
404a40ea1a7SYuri Pankov static int
manpage_compare(const void * vp1,const void * vp2)405a40ea1a7SYuri Pankov manpage_compare(const void *vp1, const void *vp2)
406a40ea1a7SYuri Pankov {
407a40ea1a7SYuri Pankov const struct manpage *mp1, *mp2;
408c66b8046SYuri Pankov const char *cp1, *cp2;
409c66b8046SYuri Pankov size_t sz1, sz2;
410a40ea1a7SYuri Pankov int diff;
411a40ea1a7SYuri Pankov
412a40ea1a7SYuri Pankov mp1 = vp1;
413a40ea1a7SYuri Pankov mp2 = vp2;
414*4d131170SRobert Mustacchi if ((diff = mp2->bits - mp1->bits) ||
415*4d131170SRobert Mustacchi (diff = mp1->sec - mp2->sec))
416c66b8046SYuri Pankov return diff;
417c66b8046SYuri Pankov
418c66b8046SYuri Pankov /* Fall back to alphabetic ordering of names. */
419c66b8046SYuri Pankov sz1 = strcspn(mp1->names, "(");
420c66b8046SYuri Pankov sz2 = strcspn(mp2->names, "(");
421c66b8046SYuri Pankov if (sz1 < sz2)
422c66b8046SYuri Pankov sz1 = sz2;
423c66b8046SYuri Pankov if ((diff = strncasecmp(mp1->names, mp2->names, sz1)))
424c66b8046SYuri Pankov return diff;
425c66b8046SYuri Pankov
426c66b8046SYuri Pankov /* For identical names and sections, prefer arch-dependent. */
427c66b8046SYuri Pankov cp1 = strchr(mp1->names + sz1, '/');
428c66b8046SYuri Pankov cp2 = strchr(mp2->names + sz2, '/');
429c66b8046SYuri Pankov return cp1 != NULL && cp2 != NULL ? strcasecmp(cp1, cp2) :
430c66b8046SYuri Pankov cp1 != NULL ? -1 : cp2 != NULL ? 1 : 0;
431a40ea1a7SYuri Pankov }
432a40ea1a7SYuri Pankov
433a40ea1a7SYuri Pankov static char *
buildnames(const struct dbm_page * page)434a40ea1a7SYuri Pankov buildnames(const struct dbm_page *page)
435a40ea1a7SYuri Pankov {
436a40ea1a7SYuri Pankov char *buf;
437a40ea1a7SYuri Pankov size_t i, sz;
438a40ea1a7SYuri Pankov
439c66b8046SYuri Pankov sz = lstlen(page->name, 2) + 1 + lstlen(page->sect, 2) +
440c66b8046SYuri Pankov (page->arch == NULL ? 0 : 1 + lstlen(page->arch, 2)) + 2;
441a40ea1a7SYuri Pankov buf = mandoc_malloc(sz);
442a40ea1a7SYuri Pankov i = 0;
443c66b8046SYuri Pankov lstcat(buf, &i, page->name, ", ");
444a40ea1a7SYuri Pankov buf[i++] = '(';
445c66b8046SYuri Pankov lstcat(buf, &i, page->sect, ", ");
446a40ea1a7SYuri Pankov if (page->arch != NULL) {
447a40ea1a7SYuri Pankov buf[i++] = '/';
448c66b8046SYuri Pankov lstcat(buf, &i, page->arch, ", ");
449a40ea1a7SYuri Pankov }
450a40ea1a7SYuri Pankov buf[i++] = ')';
451a40ea1a7SYuri Pankov buf[i++] = '\0';
452a40ea1a7SYuri Pankov assert(i == sz);
453a40ea1a7SYuri Pankov return buf;
454a40ea1a7SYuri Pankov }
455a40ea1a7SYuri Pankov
456a40ea1a7SYuri Pankov /*
457a40ea1a7SYuri Pankov * Count the buffer space needed to print the NUL-terminated
458c66b8046SYuri Pankov * list of NUL-terminated strings, when printing sep separator
459a40ea1a7SYuri Pankov * characters between strings.
460a40ea1a7SYuri Pankov */
461a40ea1a7SYuri Pankov static size_t
lstlen(const char * cp,size_t sep)462c66b8046SYuri Pankov lstlen(const char *cp, size_t sep)
463a40ea1a7SYuri Pankov {
464a40ea1a7SYuri Pankov size_t sz;
465a40ea1a7SYuri Pankov
466c66b8046SYuri Pankov for (sz = 0; *cp != '\0'; cp++) {
467c66b8046SYuri Pankov
468c66b8046SYuri Pankov /* Skip names appearing only in the SYNOPSIS. */
469c66b8046SYuri Pankov if (*cp <= (char)(NAME_SYN & NAME_MASK)) {
470c66b8046SYuri Pankov while (*cp != '\0')
471a40ea1a7SYuri Pankov cp++;
472c66b8046SYuri Pankov continue;
473c66b8046SYuri Pankov }
474c66b8046SYuri Pankov
475c66b8046SYuri Pankov /* Skip name class markers. */
476c66b8046SYuri Pankov if (*cp < ' ')
477c66b8046SYuri Pankov cp++;
478c66b8046SYuri Pankov
479c66b8046SYuri Pankov /* Print a separator before each but the first string. */
480c66b8046SYuri Pankov if (sz)
481c66b8046SYuri Pankov sz += sep;
482c66b8046SYuri Pankov
483c66b8046SYuri Pankov /* Copy one string. */
484c66b8046SYuri Pankov while (*cp != '\0') {
485c66b8046SYuri Pankov sz++;
486c66b8046SYuri Pankov cp++;
487c66b8046SYuri Pankov }
488a40ea1a7SYuri Pankov }
489a40ea1a7SYuri Pankov return sz;
490a40ea1a7SYuri Pankov }
491a40ea1a7SYuri Pankov
492a40ea1a7SYuri Pankov /*
493a40ea1a7SYuri Pankov * Print the NUL-terminated list of NUL-terminated strings
494c66b8046SYuri Pankov * into the buffer, seperating strings with sep.
495a40ea1a7SYuri Pankov */
496a40ea1a7SYuri Pankov static void
lstcat(char * buf,size_t * i,const char * cp,const char * sep)497c66b8046SYuri Pankov lstcat(char *buf, size_t *i, const char *cp, const char *sep)
498a40ea1a7SYuri Pankov {
499c66b8046SYuri Pankov const char *s;
500c66b8046SYuri Pankov size_t i_start;
501c66b8046SYuri Pankov
502c66b8046SYuri Pankov for (i_start = *i; *cp != '\0'; cp++) {
503c66b8046SYuri Pankov
504c66b8046SYuri Pankov /* Skip names appearing only in the SYNOPSIS. */
505c66b8046SYuri Pankov if (*cp <= (char)(NAME_SYN & NAME_MASK)) {
506c66b8046SYuri Pankov while (*cp != '\0')
507a40ea1a7SYuri Pankov cp++;
508c66b8046SYuri Pankov continue;
509a40ea1a7SYuri Pankov }
510c66b8046SYuri Pankov
511c66b8046SYuri Pankov /* Skip name class markers. */
512c66b8046SYuri Pankov if (*cp < ' ')
513c66b8046SYuri Pankov cp++;
514c66b8046SYuri Pankov
515c66b8046SYuri Pankov /* Print a separator before each but the first string. */
516c66b8046SYuri Pankov if (*i > i_start) {
517c66b8046SYuri Pankov s = sep;
518c66b8046SYuri Pankov while (*s != '\0')
519c66b8046SYuri Pankov buf[(*i)++] = *s++;
520c66b8046SYuri Pankov }
521c66b8046SYuri Pankov
522c66b8046SYuri Pankov /* Copy one string. */
523c66b8046SYuri Pankov while (*cp != '\0')
524c66b8046SYuri Pankov buf[(*i)++] = *cp++;
525c66b8046SYuri Pankov }
526c66b8046SYuri Pankov
527a40ea1a7SYuri Pankov }
528a40ea1a7SYuri Pankov
529a40ea1a7SYuri Pankov /*
530a40ea1a7SYuri Pankov * Return 1 if the string *want occurs in any of the strings
531a40ea1a7SYuri Pankov * in the NUL-terminated string list *have, or 0 otherwise.
532a40ea1a7SYuri Pankov * If either argument is NULL or empty, assume no filtering
533a40ea1a7SYuri Pankov * is desired and return 1.
534a40ea1a7SYuri Pankov */
535a40ea1a7SYuri Pankov static int
lstmatch(const char * want,const char * have)536a40ea1a7SYuri Pankov lstmatch(const char *want, const char *have)
537a40ea1a7SYuri Pankov {
538a40ea1a7SYuri Pankov if (want == NULL || have == NULL || *have == '\0')
539a40ea1a7SYuri Pankov return 1;
540a40ea1a7SYuri Pankov while (*have != '\0') {
541a40ea1a7SYuri Pankov if (strcasestr(have, want) != NULL)
542a40ea1a7SYuri Pankov return 1;
543a40ea1a7SYuri Pankov have = strchr(have, '\0') + 1;
544a40ea1a7SYuri Pankov }
545a40ea1a7SYuri Pankov return 0;
546a40ea1a7SYuri Pankov }
547a40ea1a7SYuri Pankov
548a40ea1a7SYuri Pankov /*
549c66b8046SYuri Pankov * Build a list of values taken by the macro im in the manual page.
550a40ea1a7SYuri Pankov */
551a40ea1a7SYuri Pankov static char *
buildoutput(size_t im,struct dbm_page * page)552c66b8046SYuri Pankov buildoutput(size_t im, struct dbm_page *page)
553a40ea1a7SYuri Pankov {
554c66b8046SYuri Pankov const char *oldoutput, *sep, *input;
555a40ea1a7SYuri Pankov char *output, *newoutput, *value;
556c66b8046SYuri Pankov size_t sz, i;
557c66b8046SYuri Pankov
558c66b8046SYuri Pankov switch (im) {
559c66b8046SYuri Pankov case KEY_Nd:
560c66b8046SYuri Pankov return mandoc_strdup(page->desc);
561c66b8046SYuri Pankov case KEY_Nm:
562c66b8046SYuri Pankov input = page->name;
563c66b8046SYuri Pankov break;
564c66b8046SYuri Pankov case KEY_sec:
565c66b8046SYuri Pankov input = page->sect;
566c66b8046SYuri Pankov break;
567c66b8046SYuri Pankov case KEY_arch:
568c66b8046SYuri Pankov input = page->arch;
569c66b8046SYuri Pankov if (input == NULL)
570c66b8046SYuri Pankov input = "all\0";
571c66b8046SYuri Pankov break;
572c66b8046SYuri Pankov default:
573c66b8046SYuri Pankov input = NULL;
574c66b8046SYuri Pankov break;
575c66b8046SYuri Pankov }
576c66b8046SYuri Pankov
577c66b8046SYuri Pankov if (input != NULL) {
578c66b8046SYuri Pankov sz = lstlen(input, 3) + 1;
579c66b8046SYuri Pankov output = mandoc_malloc(sz);
580c66b8046SYuri Pankov i = 0;
581c66b8046SYuri Pankov lstcat(output, &i, input, " # ");
582c66b8046SYuri Pankov output[i++] = '\0';
583c66b8046SYuri Pankov assert(i == sz);
584c66b8046SYuri Pankov return output;
585c66b8046SYuri Pankov }
586a40ea1a7SYuri Pankov
587a40ea1a7SYuri Pankov output = NULL;
588c66b8046SYuri Pankov dbm_macro_bypage(im - 2, page->addr);
589a40ea1a7SYuri Pankov while ((value = dbm_macro_next()) != NULL) {
590a40ea1a7SYuri Pankov if (output == NULL) {
591a40ea1a7SYuri Pankov oldoutput = "";
592a40ea1a7SYuri Pankov sep = "";
593a40ea1a7SYuri Pankov } else {
594a40ea1a7SYuri Pankov oldoutput = output;
595a40ea1a7SYuri Pankov sep = " # ";
596a40ea1a7SYuri Pankov }
597a40ea1a7SYuri Pankov mandoc_asprintf(&newoutput, "%s%s%s", oldoutput, sep, value);
598a40ea1a7SYuri Pankov free(output);
599a40ea1a7SYuri Pankov output = newoutput;
600a40ea1a7SYuri Pankov }
601a40ea1a7SYuri Pankov return output;
602a40ea1a7SYuri Pankov }
603a40ea1a7SYuri Pankov
604a40ea1a7SYuri Pankov /*
605a40ea1a7SYuri Pankov * Compile a set of string tokens into an expression.
606a40ea1a7SYuri Pankov * Tokens in "argv" are assumed to be individual expression atoms (e.g.,
607a40ea1a7SYuri Pankov * "(", "foo=bar", etc.).
608a40ea1a7SYuri Pankov */
609a40ea1a7SYuri Pankov static struct expr *
exprcomp(const struct mansearch * search,int argc,char * argv[],int * argi)610a40ea1a7SYuri Pankov exprcomp(const struct mansearch *search, int argc, char *argv[], int *argi)
611a40ea1a7SYuri Pankov {
612a40ea1a7SYuri Pankov struct expr *parent, *child;
613a40ea1a7SYuri Pankov int needterm, nested;
614a40ea1a7SYuri Pankov
615a40ea1a7SYuri Pankov if ((nested = *argi) == argc)
616a40ea1a7SYuri Pankov return NULL;
617a40ea1a7SYuri Pankov needterm = 1;
618a40ea1a7SYuri Pankov parent = child = NULL;
619a40ea1a7SYuri Pankov while (*argi < argc) {
620a40ea1a7SYuri Pankov if (strcmp(")", argv[*argi]) == 0) {
621a40ea1a7SYuri Pankov if (needterm)
622a40ea1a7SYuri Pankov warnx("missing term "
623a40ea1a7SYuri Pankov "before closing parenthesis");
624a40ea1a7SYuri Pankov needterm = 0;
625a40ea1a7SYuri Pankov if (nested)
626a40ea1a7SYuri Pankov break;
627a40ea1a7SYuri Pankov warnx("ignoring unmatched right parenthesis");
628a40ea1a7SYuri Pankov ++*argi;
629a40ea1a7SYuri Pankov continue;
630a40ea1a7SYuri Pankov }
631a40ea1a7SYuri Pankov if (strcmp("-o", argv[*argi]) == 0) {
632a40ea1a7SYuri Pankov if (needterm) {
633a40ea1a7SYuri Pankov if (*argi > 0)
634a40ea1a7SYuri Pankov warnx("ignoring -o after %s",
635a40ea1a7SYuri Pankov argv[*argi - 1]);
636a40ea1a7SYuri Pankov else
637a40ea1a7SYuri Pankov warnx("ignoring initial -o");
638a40ea1a7SYuri Pankov }
639a40ea1a7SYuri Pankov needterm = 1;
640a40ea1a7SYuri Pankov ++*argi;
641a40ea1a7SYuri Pankov continue;
642a40ea1a7SYuri Pankov }
643a40ea1a7SYuri Pankov needterm = 0;
644a40ea1a7SYuri Pankov if (child == NULL) {
645a40ea1a7SYuri Pankov child = expr_and(search, argc, argv, argi);
646a40ea1a7SYuri Pankov continue;
647a40ea1a7SYuri Pankov }
648a40ea1a7SYuri Pankov if (parent == NULL) {
649a40ea1a7SYuri Pankov parent = mandoc_calloc(1, sizeof(*parent));
650a40ea1a7SYuri Pankov parent->type = EXPR_OR;
651a40ea1a7SYuri Pankov parent->next = NULL;
652a40ea1a7SYuri Pankov parent->child = child;
653a40ea1a7SYuri Pankov }
654a40ea1a7SYuri Pankov child->next = expr_and(search, argc, argv, argi);
655a40ea1a7SYuri Pankov child = child->next;
656a40ea1a7SYuri Pankov }
657a40ea1a7SYuri Pankov if (needterm && *argi)
658a40ea1a7SYuri Pankov warnx("ignoring trailing %s", argv[*argi - 1]);
659a40ea1a7SYuri Pankov return parent == NULL ? child : parent;
660a40ea1a7SYuri Pankov }
661a40ea1a7SYuri Pankov
662a40ea1a7SYuri Pankov static struct expr *
expr_and(const struct mansearch * search,int argc,char * argv[],int * argi)663a40ea1a7SYuri Pankov expr_and(const struct mansearch *search, int argc, char *argv[], int *argi)
664a40ea1a7SYuri Pankov {
665a40ea1a7SYuri Pankov struct expr *parent, *child;
666a40ea1a7SYuri Pankov int needterm;
667a40ea1a7SYuri Pankov
668a40ea1a7SYuri Pankov needterm = 1;
669a40ea1a7SYuri Pankov parent = child = NULL;
670a40ea1a7SYuri Pankov while (*argi < argc) {
671a40ea1a7SYuri Pankov if (strcmp(")", argv[*argi]) == 0) {
672a40ea1a7SYuri Pankov if (needterm)
673a40ea1a7SYuri Pankov warnx("missing term "
674a40ea1a7SYuri Pankov "before closing parenthesis");
675a40ea1a7SYuri Pankov needterm = 0;
676a40ea1a7SYuri Pankov break;
677a40ea1a7SYuri Pankov }
678a40ea1a7SYuri Pankov if (strcmp("-o", argv[*argi]) == 0)
679a40ea1a7SYuri Pankov break;
680a40ea1a7SYuri Pankov if (strcmp("-a", argv[*argi]) == 0) {
681a40ea1a7SYuri Pankov if (needterm) {
682a40ea1a7SYuri Pankov if (*argi > 0)
683a40ea1a7SYuri Pankov warnx("ignoring -a after %s",
684a40ea1a7SYuri Pankov argv[*argi - 1]);
685a40ea1a7SYuri Pankov else
686a40ea1a7SYuri Pankov warnx("ignoring initial -a");
687a40ea1a7SYuri Pankov }
688a40ea1a7SYuri Pankov needterm = 1;
689a40ea1a7SYuri Pankov ++*argi;
690a40ea1a7SYuri Pankov continue;
691a40ea1a7SYuri Pankov }
692a40ea1a7SYuri Pankov if (needterm == 0)
693a40ea1a7SYuri Pankov break;
694a40ea1a7SYuri Pankov if (child == NULL) {
695a40ea1a7SYuri Pankov child = exprterm(search, argc, argv, argi);
696a40ea1a7SYuri Pankov if (child != NULL)
697a40ea1a7SYuri Pankov needterm = 0;
698a40ea1a7SYuri Pankov continue;
699a40ea1a7SYuri Pankov }
700a40ea1a7SYuri Pankov needterm = 0;
701a40ea1a7SYuri Pankov if (parent == NULL) {
702a40ea1a7SYuri Pankov parent = mandoc_calloc(1, sizeof(*parent));
703a40ea1a7SYuri Pankov parent->type = EXPR_AND;
704a40ea1a7SYuri Pankov parent->next = NULL;
705a40ea1a7SYuri Pankov parent->child = child;
706a40ea1a7SYuri Pankov }
707a40ea1a7SYuri Pankov child->next = exprterm(search, argc, argv, argi);
708a40ea1a7SYuri Pankov if (child->next != NULL) {
709a40ea1a7SYuri Pankov child = child->next;
710a40ea1a7SYuri Pankov needterm = 0;
711a40ea1a7SYuri Pankov }
712a40ea1a7SYuri Pankov }
713a40ea1a7SYuri Pankov if (needterm && *argi)
714a40ea1a7SYuri Pankov warnx("ignoring trailing %s", argv[*argi - 1]);
715a40ea1a7SYuri Pankov return parent == NULL ? child : parent;
716a40ea1a7SYuri Pankov }
717a40ea1a7SYuri Pankov
718a40ea1a7SYuri Pankov static struct expr *
exprterm(const struct mansearch * search,int argc,char * argv[],int * argi)719a40ea1a7SYuri Pankov exprterm(const struct mansearch *search, int argc, char *argv[], int *argi)
720a40ea1a7SYuri Pankov {
721a40ea1a7SYuri Pankov char errbuf[BUFSIZ];
722a40ea1a7SYuri Pankov struct expr *e;
723a40ea1a7SYuri Pankov char *key, *val;
724a40ea1a7SYuri Pankov uint64_t iterbit;
725a40ea1a7SYuri Pankov int cs, i, irc;
726a40ea1a7SYuri Pankov
727a40ea1a7SYuri Pankov if (strcmp("(", argv[*argi]) == 0) {
728a40ea1a7SYuri Pankov ++*argi;
729a40ea1a7SYuri Pankov e = exprcomp(search, argc, argv, argi);
730a40ea1a7SYuri Pankov if (*argi < argc) {
731a40ea1a7SYuri Pankov assert(strcmp(")", argv[*argi]) == 0);
732a40ea1a7SYuri Pankov ++*argi;
733a40ea1a7SYuri Pankov } else
734a40ea1a7SYuri Pankov warnx("unclosed parenthesis");
735a40ea1a7SYuri Pankov return e;
736a40ea1a7SYuri Pankov }
737a40ea1a7SYuri Pankov
738c66b8046SYuri Pankov if (strcmp("-i", argv[*argi]) == 0 && *argi + 1 < argc) {
739c66b8046SYuri Pankov cs = 0;
740c66b8046SYuri Pankov ++*argi;
741c66b8046SYuri Pankov } else
742c66b8046SYuri Pankov cs = 1;
743c66b8046SYuri Pankov
744a40ea1a7SYuri Pankov e = mandoc_calloc(1, sizeof(*e));
745a40ea1a7SYuri Pankov e->type = EXPR_TERM;
746a40ea1a7SYuri Pankov e->bits = 0;
747a40ea1a7SYuri Pankov e->next = NULL;
748a40ea1a7SYuri Pankov e->child = NULL;
749a40ea1a7SYuri Pankov
750a40ea1a7SYuri Pankov if (search->argmode == ARG_NAME) {
751a40ea1a7SYuri Pankov e->bits = TYPE_Nm;
752a40ea1a7SYuri Pankov e->match.type = DBM_EXACT;
753a40ea1a7SYuri Pankov e->match.str = argv[(*argi)++];
754a40ea1a7SYuri Pankov return e;
755a40ea1a7SYuri Pankov }
756a40ea1a7SYuri Pankov
757a40ea1a7SYuri Pankov /*
758a40ea1a7SYuri Pankov * Separate macro keys from search string.
759a40ea1a7SYuri Pankov * If needed, request regular expression handling.
760a40ea1a7SYuri Pankov */
761a40ea1a7SYuri Pankov
762a40ea1a7SYuri Pankov if (search->argmode == ARG_WORD) {
763a40ea1a7SYuri Pankov e->bits = TYPE_Nm;
764a40ea1a7SYuri Pankov e->match.type = DBM_REGEX;
765a40ea1a7SYuri Pankov #if HAVE_REWB_BSD
766a40ea1a7SYuri Pankov mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", argv[*argi]);
767a40ea1a7SYuri Pankov #elif HAVE_REWB_SYSV
768a40ea1a7SYuri Pankov mandoc_asprintf(&val, "\\<%s\\>", argv[*argi]);
769a40ea1a7SYuri Pankov #else
770a40ea1a7SYuri Pankov mandoc_asprintf(&val,
771a40ea1a7SYuri Pankov "(^|[^a-zA-Z01-9_])%s([^a-zA-Z01-9_]|$)", argv[*argi]);
772a40ea1a7SYuri Pankov #endif
773a40ea1a7SYuri Pankov cs = 0;
774a40ea1a7SYuri Pankov } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) {
775a40ea1a7SYuri Pankov e->bits = TYPE_Nm | TYPE_Nd;
776cec8643bSMichal Nowak e->match.type = DBM_REGEX;
777cec8643bSMichal Nowak val = argv[*argi];
778cec8643bSMichal Nowak cs = 0;
779a40ea1a7SYuri Pankov } else {
780a40ea1a7SYuri Pankov if (val == argv[*argi])
781a40ea1a7SYuri Pankov e->bits = TYPE_Nm | TYPE_Nd;
782a40ea1a7SYuri Pankov if (*val == '=') {
783a40ea1a7SYuri Pankov e->match.type = DBM_SUB;
784a40ea1a7SYuri Pankov e->match.str = val + 1;
785a40ea1a7SYuri Pankov } else
786a40ea1a7SYuri Pankov e->match.type = DBM_REGEX;
787a40ea1a7SYuri Pankov *val++ = '\0';
788a40ea1a7SYuri Pankov if (strstr(argv[*argi], "arch") != NULL)
789a40ea1a7SYuri Pankov cs = 0;
790a40ea1a7SYuri Pankov }
791a40ea1a7SYuri Pankov
792a40ea1a7SYuri Pankov /* Compile regular expressions. */
793a40ea1a7SYuri Pankov
794a40ea1a7SYuri Pankov if (e->match.type == DBM_REGEX) {
795a40ea1a7SYuri Pankov e->match.re = mandoc_malloc(sizeof(*e->match.re));
796a40ea1a7SYuri Pankov irc = regcomp(e->match.re, val,
797a40ea1a7SYuri Pankov REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
798a40ea1a7SYuri Pankov if (irc) {
799a40ea1a7SYuri Pankov regerror(irc, e->match.re, errbuf, sizeof(errbuf));
800a40ea1a7SYuri Pankov warnx("regcomp /%s/: %s", val, errbuf);
801a40ea1a7SYuri Pankov }
802a40ea1a7SYuri Pankov if (search->argmode == ARG_WORD)
803a40ea1a7SYuri Pankov free(val);
804a40ea1a7SYuri Pankov if (irc) {
805a40ea1a7SYuri Pankov free(e->match.re);
806a40ea1a7SYuri Pankov free(e);
807a40ea1a7SYuri Pankov ++*argi;
808a40ea1a7SYuri Pankov return NULL;
809a40ea1a7SYuri Pankov }
810a40ea1a7SYuri Pankov }
811a40ea1a7SYuri Pankov
812a40ea1a7SYuri Pankov if (e->bits) {
813a40ea1a7SYuri Pankov ++*argi;
814a40ea1a7SYuri Pankov return e;
815a40ea1a7SYuri Pankov }
816a40ea1a7SYuri Pankov
817a40ea1a7SYuri Pankov /*
818a40ea1a7SYuri Pankov * Parse out all possible fields.
819a40ea1a7SYuri Pankov * If the field doesn't resolve, bail.
820a40ea1a7SYuri Pankov */
821a40ea1a7SYuri Pankov
822a40ea1a7SYuri Pankov while (NULL != (key = strsep(&argv[*argi], ","))) {
823a40ea1a7SYuri Pankov if ('\0' == *key)
824a40ea1a7SYuri Pankov continue;
825a40ea1a7SYuri Pankov for (i = 0, iterbit = 1; i < KEY_MAX; i++, iterbit <<= 1) {
826a40ea1a7SYuri Pankov if (0 == strcasecmp(key, mansearch_keynames[i])) {
827a40ea1a7SYuri Pankov e->bits |= iterbit;
828a40ea1a7SYuri Pankov break;
829a40ea1a7SYuri Pankov }
830a40ea1a7SYuri Pankov }
831a40ea1a7SYuri Pankov if (i == KEY_MAX) {
832a40ea1a7SYuri Pankov if (strcasecmp(key, "any"))
833a40ea1a7SYuri Pankov warnx("treating unknown key "
834a40ea1a7SYuri Pankov "\"%s\" as \"any\"", key);
835a40ea1a7SYuri Pankov e->bits |= ~0ULL;
836a40ea1a7SYuri Pankov }
837a40ea1a7SYuri Pankov }
838a40ea1a7SYuri Pankov
839a40ea1a7SYuri Pankov ++*argi;
840a40ea1a7SYuri Pankov return e;
841a40ea1a7SYuri Pankov }
842a40ea1a7SYuri Pankov
843a40ea1a7SYuri Pankov static void
exprfree(struct expr * e)844a40ea1a7SYuri Pankov exprfree(struct expr *e)
845a40ea1a7SYuri Pankov {
846a40ea1a7SYuri Pankov if (e->next != NULL)
847a40ea1a7SYuri Pankov exprfree(e->next);
848a40ea1a7SYuri Pankov if (e->child != NULL)
849a40ea1a7SYuri Pankov exprfree(e->child);
850a40ea1a7SYuri Pankov free(e);
851a40ea1a7SYuri Pankov }
852