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