1*6640c13bSYuri Pankov /* $Id: mansearch.c,v 1.77 2017/08/22 17:50:11 schwarze Exp $ */ 2a40ea1a7SYuri Pankov /* 3a40ea1a7SYuri Pankov * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> 4c66b8046SYuri 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 *); 70c66b8046SYuri Pankov static char *buildoutput(size_t, struct dbm_page *); 71c66b8046SYuri Pankov static size_t lstlen(const char *, size_t); 72c66b8046SYuri 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; 107c66b8046SYuri 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) { 159c66b8046SYuri 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 || 174c66b8046SYuri Pankov lstmatch(search->arch, page->arch) == 0 || 175c66b8046SYuri Pankov (search->argmode == ARG_NAME && 176c66b8046SYuri Pankov rp->bits <= (int32_t)(NAME_SYN & NAME_MASK))) 177a40ea1a7SYuri Pankov continue; 178a40ea1a7SYuri Pankov 179c66b8046SYuri Pankov if (res == NULL) { 180c66b8046SYuri Pankov cur = 1; 181c66b8046SYuri Pankov break; 182c66b8046SYuri 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); 191*6640c13bSYuri Pankov if (access(chdir_status ? page->file + 1 : 192*6640c13bSYuri Pankov mpage->file, R_OK) == -1) { 193*6640c13bSYuri Pankov warn("%s", mpage->file); 194*6640c13bSYuri Pankov warnx("outdated mandoc.db contains " 195*6640c13bSYuri Pankov "bogus %s entry, run makewhatis %s", 196*6640c13bSYuri Pankov page->file + 1, paths->paths[i]); 197*6640c13bSYuri Pankov free(mpage->file); 198*6640c13bSYuri Pankov free(rp); 199*6640c13bSYuri Pankov continue; 200*6640c13bSYuri Pankov } 201a40ea1a7SYuri Pankov mpage->names = buildnames(page); 202c66b8046SYuri Pankov mpage->output = buildoutput(outkey, page); 203a40ea1a7SYuri Pankov mpage->ipath = i; 204a40ea1a7SYuri Pankov mpage->bits = rp->bits; 205a40ea1a7SYuri Pankov mpage->sec = *page->sect - '0'; 206a40ea1a7SYuri Pankov if (mpage->sec < 0 || mpage->sec > 9) 207a40ea1a7SYuri Pankov mpage->sec = 10; 208a40ea1a7SYuri Pankov mpage->form = *page->file; 209a40ea1a7SYuri Pankov free(rp); 210a40ea1a7SYuri Pankov cur++; 211a40ea1a7SYuri Pankov } 212a40ea1a7SYuri Pankov ohash_delete(htab); 213a40ea1a7SYuri Pankov free(htab); 214a40ea1a7SYuri Pankov dbm_close(); 215a40ea1a7SYuri Pankov 216a40ea1a7SYuri Pankov /* 217a40ea1a7SYuri Pankov * In man(1) mode, prefer matches in earlier trees 218a40ea1a7SYuri Pankov * over matches in later trees. 219a40ea1a7SYuri Pankov */ 220a40ea1a7SYuri Pankov 221a40ea1a7SYuri Pankov if (cur && search->firstmatch) 222a40ea1a7SYuri Pankov break; 223a40ea1a7SYuri Pankov } 224c66b8046SYuri Pankov if (res != NULL) 225a40ea1a7SYuri Pankov qsort(*res, cur, sizeof(struct manpage), manpage_compare); 226a40ea1a7SYuri Pankov if (chdir_status && getcwd_status && chdir(buf) == -1) 227a40ea1a7SYuri Pankov warn("%s", buf); 228a40ea1a7SYuri Pankov exprfree(e); 229a40ea1a7SYuri Pankov *sz = cur; 230c66b8046SYuri Pankov return res != NULL || cur; 231a40ea1a7SYuri Pankov } 232a40ea1a7SYuri Pankov 233a40ea1a7SYuri Pankov /* 234a40ea1a7SYuri Pankov * Merge the results for the expression tree rooted at e 235a40ea1a7SYuri Pankov * into the the result list htab. 236a40ea1a7SYuri Pankov */ 237a40ea1a7SYuri Pankov static struct ohash * 238a40ea1a7SYuri Pankov manmerge(struct expr *e, struct ohash *htab) 239a40ea1a7SYuri Pankov { 240a40ea1a7SYuri Pankov switch (e->type) { 241a40ea1a7SYuri Pankov case EXPR_TERM: 242a40ea1a7SYuri Pankov return manmerge_term(e, htab); 243a40ea1a7SYuri Pankov case EXPR_OR: 244a40ea1a7SYuri Pankov return manmerge_or(e->child, htab); 245a40ea1a7SYuri Pankov case EXPR_AND: 246a40ea1a7SYuri Pankov return manmerge_and(e->child, htab); 247a40ea1a7SYuri Pankov default: 248a40ea1a7SYuri Pankov abort(); 249a40ea1a7SYuri Pankov } 250a40ea1a7SYuri Pankov } 251a40ea1a7SYuri Pankov 252a40ea1a7SYuri Pankov static struct ohash * 253a40ea1a7SYuri Pankov manmerge_term(struct expr *e, struct ohash *htab) 254a40ea1a7SYuri Pankov { 255a40ea1a7SYuri Pankov struct dbm_res res, *rp; 256a40ea1a7SYuri Pankov uint64_t ib; 257a40ea1a7SYuri Pankov unsigned int slot; 258a40ea1a7SYuri Pankov int im; 259a40ea1a7SYuri Pankov 260a40ea1a7SYuri Pankov if (htab == NULL) { 261a40ea1a7SYuri Pankov htab = mandoc_malloc(sizeof(*htab)); 262a40ea1a7SYuri Pankov mandoc_ohash_init(htab, 4, offsetof(struct dbm_res, page)); 263a40ea1a7SYuri Pankov } 264a40ea1a7SYuri Pankov 265a40ea1a7SYuri Pankov for (im = 0, ib = 1; im < KEY_MAX; im++, ib <<= 1) { 266a40ea1a7SYuri Pankov if ((e->bits & ib) == 0) 267a40ea1a7SYuri Pankov continue; 268a40ea1a7SYuri Pankov 269a40ea1a7SYuri Pankov switch (ib) { 270a40ea1a7SYuri Pankov case TYPE_arch: 271a40ea1a7SYuri Pankov dbm_page_byarch(&e->match); 272a40ea1a7SYuri Pankov break; 273a40ea1a7SYuri Pankov case TYPE_sec: 274a40ea1a7SYuri Pankov dbm_page_bysect(&e->match); 275a40ea1a7SYuri Pankov break; 276a40ea1a7SYuri Pankov case TYPE_Nm: 277a40ea1a7SYuri Pankov dbm_page_byname(&e->match); 278a40ea1a7SYuri Pankov break; 279a40ea1a7SYuri Pankov case TYPE_Nd: 280a40ea1a7SYuri Pankov dbm_page_bydesc(&e->match); 281a40ea1a7SYuri Pankov break; 282a40ea1a7SYuri Pankov default: 283a40ea1a7SYuri Pankov dbm_page_bymacro(im - 2, &e->match); 284a40ea1a7SYuri Pankov break; 285a40ea1a7SYuri Pankov } 286a40ea1a7SYuri Pankov 287a40ea1a7SYuri Pankov /* 288a40ea1a7SYuri Pankov * When hashing for deduplication, use the unique 289a40ea1a7SYuri Pankov * page ID itself instead of a hash function; 290a40ea1a7SYuri Pankov * that is quite efficient. 291a40ea1a7SYuri Pankov */ 292a40ea1a7SYuri Pankov 293a40ea1a7SYuri Pankov for (;;) { 294a40ea1a7SYuri Pankov res = dbm_page_next(); 295a40ea1a7SYuri Pankov if (res.page == -1) 296a40ea1a7SYuri Pankov break; 297a40ea1a7SYuri Pankov slot = ohash_lookup_memory(htab, 298a40ea1a7SYuri Pankov (char *)&res, sizeof(res.page), res.page); 299a40ea1a7SYuri Pankov if ((rp = ohash_find(htab, slot)) != NULL) { 300a40ea1a7SYuri Pankov rp->bits |= res.bits; 301a40ea1a7SYuri Pankov continue; 302a40ea1a7SYuri Pankov } 303a40ea1a7SYuri Pankov rp = mandoc_malloc(sizeof(*rp)); 304a40ea1a7SYuri Pankov *rp = res; 305a40ea1a7SYuri Pankov ohash_insert(htab, slot, rp); 306a40ea1a7SYuri Pankov } 307a40ea1a7SYuri Pankov } 308a40ea1a7SYuri Pankov return htab; 309a40ea1a7SYuri Pankov } 310a40ea1a7SYuri Pankov 311a40ea1a7SYuri Pankov static struct ohash * 312a40ea1a7SYuri Pankov manmerge_or(struct expr *e, struct ohash *htab) 313a40ea1a7SYuri Pankov { 314a40ea1a7SYuri Pankov while (e != NULL) { 315a40ea1a7SYuri Pankov htab = manmerge(e, htab); 316a40ea1a7SYuri Pankov e = e->next; 317a40ea1a7SYuri Pankov } 318a40ea1a7SYuri Pankov return htab; 319a40ea1a7SYuri Pankov } 320a40ea1a7SYuri Pankov 321a40ea1a7SYuri Pankov static struct ohash * 322a40ea1a7SYuri Pankov manmerge_and(struct expr *e, struct ohash *htab) 323a40ea1a7SYuri Pankov { 324a40ea1a7SYuri Pankov struct ohash *hand, *h1, *h2; 325a40ea1a7SYuri Pankov struct dbm_res *res; 326a40ea1a7SYuri Pankov unsigned int slot1, slot2; 327a40ea1a7SYuri Pankov 328a40ea1a7SYuri Pankov /* Evaluate the first term of the AND clause. */ 329a40ea1a7SYuri Pankov 330a40ea1a7SYuri Pankov hand = manmerge(e, NULL); 331a40ea1a7SYuri Pankov 332a40ea1a7SYuri Pankov while ((e = e->next) != NULL) { 333a40ea1a7SYuri Pankov 334a40ea1a7SYuri Pankov /* Evaluate the next term and prepare for ANDing. */ 335a40ea1a7SYuri Pankov 336a40ea1a7SYuri Pankov h2 = manmerge(e, NULL); 337a40ea1a7SYuri Pankov if (ohash_entries(h2) < ohash_entries(hand)) { 338a40ea1a7SYuri Pankov h1 = h2; 339a40ea1a7SYuri Pankov h2 = hand; 340a40ea1a7SYuri Pankov } else 341a40ea1a7SYuri Pankov h1 = hand; 342a40ea1a7SYuri Pankov hand = mandoc_malloc(sizeof(*hand)); 343a40ea1a7SYuri Pankov mandoc_ohash_init(hand, 4, offsetof(struct dbm_res, page)); 344a40ea1a7SYuri Pankov 345a40ea1a7SYuri Pankov /* Keep all pages that are in both result sets. */ 346a40ea1a7SYuri Pankov 347a40ea1a7SYuri Pankov for (res = ohash_first(h1, &slot1); res != NULL; 348a40ea1a7SYuri Pankov res = ohash_next(h1, &slot1)) { 349a40ea1a7SYuri Pankov if (ohash_find(h2, ohash_lookup_memory(h2, 350a40ea1a7SYuri Pankov (char *)res, sizeof(res->page), 351a40ea1a7SYuri Pankov res->page)) == NULL) 352a40ea1a7SYuri Pankov free(res); 353a40ea1a7SYuri Pankov else 354a40ea1a7SYuri Pankov ohash_insert(hand, ohash_lookup_memory(hand, 355a40ea1a7SYuri Pankov (char *)res, sizeof(res->page), 356a40ea1a7SYuri Pankov res->page), res); 357a40ea1a7SYuri Pankov } 358a40ea1a7SYuri Pankov 359a40ea1a7SYuri Pankov /* Discard the merged results. */ 360a40ea1a7SYuri Pankov 361a40ea1a7SYuri Pankov for (res = ohash_first(h2, &slot2); res != NULL; 362a40ea1a7SYuri Pankov res = ohash_next(h2, &slot2)) 363a40ea1a7SYuri Pankov free(res); 364a40ea1a7SYuri Pankov ohash_delete(h2); 365a40ea1a7SYuri Pankov free(h2); 366a40ea1a7SYuri Pankov ohash_delete(h1); 367a40ea1a7SYuri Pankov free(h1); 368a40ea1a7SYuri Pankov } 369a40ea1a7SYuri Pankov 370a40ea1a7SYuri Pankov /* Merge the result of the AND into htab. */ 371a40ea1a7SYuri Pankov 372a40ea1a7SYuri Pankov if (htab == NULL) 373a40ea1a7SYuri Pankov return hand; 374a40ea1a7SYuri Pankov 375a40ea1a7SYuri Pankov for (res = ohash_first(hand, &slot1); res != NULL; 376a40ea1a7SYuri Pankov res = ohash_next(hand, &slot1)) { 377a40ea1a7SYuri Pankov slot2 = ohash_lookup_memory(htab, 378a40ea1a7SYuri Pankov (char *)res, sizeof(res->page), res->page); 379a40ea1a7SYuri Pankov if (ohash_find(htab, slot2) == NULL) 380a40ea1a7SYuri Pankov ohash_insert(htab, slot2, res); 381a40ea1a7SYuri Pankov else 382a40ea1a7SYuri Pankov free(res); 383a40ea1a7SYuri Pankov } 384a40ea1a7SYuri Pankov 385a40ea1a7SYuri Pankov /* Discard the merged result. */ 386a40ea1a7SYuri Pankov 387a40ea1a7SYuri Pankov ohash_delete(hand); 388a40ea1a7SYuri Pankov free(hand); 389a40ea1a7SYuri Pankov return htab; 390a40ea1a7SYuri Pankov } 391a40ea1a7SYuri Pankov 392a40ea1a7SYuri Pankov void 393a40ea1a7SYuri Pankov mansearch_free(struct manpage *res, size_t sz) 394a40ea1a7SYuri Pankov { 395a40ea1a7SYuri Pankov size_t i; 396a40ea1a7SYuri Pankov 397a40ea1a7SYuri Pankov for (i = 0; i < sz; i++) { 398a40ea1a7SYuri Pankov free(res[i].file); 399a40ea1a7SYuri Pankov free(res[i].names); 400a40ea1a7SYuri Pankov free(res[i].output); 401a40ea1a7SYuri Pankov } 402a40ea1a7SYuri Pankov free(res); 403a40ea1a7SYuri Pankov } 404a40ea1a7SYuri Pankov 405a40ea1a7SYuri Pankov static int 406a40ea1a7SYuri Pankov manpage_compare(const void *vp1, const void *vp2) 407a40ea1a7SYuri Pankov { 408a40ea1a7SYuri Pankov const struct manpage *mp1, *mp2; 409c66b8046SYuri Pankov const char *cp1, *cp2; 410c66b8046SYuri Pankov size_t sz1, sz2; 411a40ea1a7SYuri Pankov int diff; 412a40ea1a7SYuri Pankov 413a40ea1a7SYuri Pankov mp1 = vp1; 414a40ea1a7SYuri Pankov mp2 = vp2; 415c66b8046SYuri Pankov if ((diff = mp2->bits - mp1->bits) || 416c66b8046SYuri Pankov (diff = mp1->sec - mp2->sec)) 417c66b8046SYuri Pankov return diff; 418c66b8046SYuri Pankov 419c66b8046SYuri Pankov /* Fall back to alphabetic ordering of names. */ 420c66b8046SYuri Pankov sz1 = strcspn(mp1->names, "("); 421c66b8046SYuri Pankov sz2 = strcspn(mp2->names, "("); 422c66b8046SYuri Pankov if (sz1 < sz2) 423c66b8046SYuri Pankov sz1 = sz2; 424c66b8046SYuri Pankov if ((diff = strncasecmp(mp1->names, mp2->names, sz1))) 425c66b8046SYuri Pankov return diff; 426c66b8046SYuri Pankov 427c66b8046SYuri Pankov /* For identical names and sections, prefer arch-dependent. */ 428c66b8046SYuri Pankov cp1 = strchr(mp1->names + sz1, '/'); 429c66b8046SYuri Pankov cp2 = strchr(mp2->names + sz2, '/'); 430c66b8046SYuri Pankov return cp1 != NULL && cp2 != NULL ? strcasecmp(cp1, cp2) : 431c66b8046SYuri Pankov cp1 != NULL ? -1 : cp2 != NULL ? 1 : 0; 432a40ea1a7SYuri Pankov } 433a40ea1a7SYuri Pankov 434a40ea1a7SYuri Pankov static char * 435a40ea1a7SYuri Pankov buildnames(const struct dbm_page *page) 436a40ea1a7SYuri Pankov { 437a40ea1a7SYuri Pankov char *buf; 438a40ea1a7SYuri Pankov size_t i, sz; 439a40ea1a7SYuri Pankov 440c66b8046SYuri Pankov sz = lstlen(page->name, 2) + 1 + lstlen(page->sect, 2) + 441c66b8046SYuri Pankov (page->arch == NULL ? 0 : 1 + lstlen(page->arch, 2)) + 2; 442a40ea1a7SYuri Pankov buf = mandoc_malloc(sz); 443a40ea1a7SYuri Pankov i = 0; 444c66b8046SYuri Pankov lstcat(buf, &i, page->name, ", "); 445a40ea1a7SYuri Pankov buf[i++] = '('; 446c66b8046SYuri Pankov lstcat(buf, &i, page->sect, ", "); 447a40ea1a7SYuri Pankov if (page->arch != NULL) { 448a40ea1a7SYuri Pankov buf[i++] = '/'; 449c66b8046SYuri Pankov lstcat(buf, &i, page->arch, ", "); 450a40ea1a7SYuri Pankov } 451a40ea1a7SYuri Pankov buf[i++] = ')'; 452a40ea1a7SYuri Pankov buf[i++] = '\0'; 453a40ea1a7SYuri Pankov assert(i == sz); 454a40ea1a7SYuri Pankov return buf; 455a40ea1a7SYuri Pankov } 456a40ea1a7SYuri Pankov 457a40ea1a7SYuri Pankov /* 458a40ea1a7SYuri Pankov * Count the buffer space needed to print the NUL-terminated 459c66b8046SYuri Pankov * list of NUL-terminated strings, when printing sep separator 460a40ea1a7SYuri Pankov * characters between strings. 461a40ea1a7SYuri Pankov */ 462a40ea1a7SYuri Pankov static size_t 463c66b8046SYuri Pankov lstlen(const char *cp, size_t sep) 464a40ea1a7SYuri Pankov { 465a40ea1a7SYuri Pankov size_t sz; 466a40ea1a7SYuri Pankov 467c66b8046SYuri Pankov for (sz = 0; *cp != '\0'; cp++) { 468c66b8046SYuri Pankov 469c66b8046SYuri Pankov /* Skip names appearing only in the SYNOPSIS. */ 470c66b8046SYuri Pankov if (*cp <= (char)(NAME_SYN & NAME_MASK)) { 471c66b8046SYuri Pankov while (*cp != '\0') 472a40ea1a7SYuri Pankov cp++; 473c66b8046SYuri Pankov continue; 474c66b8046SYuri Pankov } 475c66b8046SYuri Pankov 476c66b8046SYuri Pankov /* Skip name class markers. */ 477c66b8046SYuri Pankov if (*cp < ' ') 478c66b8046SYuri Pankov cp++; 479c66b8046SYuri Pankov 480c66b8046SYuri Pankov /* Print a separator before each but the first string. */ 481c66b8046SYuri Pankov if (sz) 482c66b8046SYuri Pankov sz += sep; 483c66b8046SYuri Pankov 484c66b8046SYuri Pankov /* Copy one string. */ 485c66b8046SYuri Pankov while (*cp != '\0') { 486c66b8046SYuri Pankov sz++; 487c66b8046SYuri Pankov cp++; 488c66b8046SYuri Pankov } 489a40ea1a7SYuri Pankov } 490a40ea1a7SYuri Pankov return sz; 491a40ea1a7SYuri Pankov } 492a40ea1a7SYuri Pankov 493a40ea1a7SYuri Pankov /* 494a40ea1a7SYuri Pankov * Print the NUL-terminated list of NUL-terminated strings 495c66b8046SYuri Pankov * into the buffer, seperating strings with sep. 496a40ea1a7SYuri Pankov */ 497a40ea1a7SYuri Pankov static void 498c66b8046SYuri Pankov lstcat(char *buf, size_t *i, const char *cp, const char *sep) 499a40ea1a7SYuri Pankov { 500c66b8046SYuri Pankov const char *s; 501c66b8046SYuri Pankov size_t i_start; 502c66b8046SYuri Pankov 503c66b8046SYuri Pankov for (i_start = *i; *cp != '\0'; cp++) { 504c66b8046SYuri Pankov 505c66b8046SYuri Pankov /* Skip names appearing only in the SYNOPSIS. */ 506c66b8046SYuri Pankov if (*cp <= (char)(NAME_SYN & NAME_MASK)) { 507c66b8046SYuri Pankov while (*cp != '\0') 508a40ea1a7SYuri Pankov cp++; 509c66b8046SYuri Pankov continue; 510a40ea1a7SYuri Pankov } 511c66b8046SYuri Pankov 512c66b8046SYuri Pankov /* Skip name class markers. */ 513c66b8046SYuri Pankov if (*cp < ' ') 514c66b8046SYuri Pankov cp++; 515c66b8046SYuri Pankov 516c66b8046SYuri Pankov /* Print a separator before each but the first string. */ 517c66b8046SYuri Pankov if (*i > i_start) { 518c66b8046SYuri Pankov s = sep; 519c66b8046SYuri Pankov while (*s != '\0') 520c66b8046SYuri Pankov buf[(*i)++] = *s++; 521c66b8046SYuri Pankov } 522c66b8046SYuri Pankov 523c66b8046SYuri Pankov /* Copy one string. */ 524c66b8046SYuri Pankov while (*cp != '\0') 525c66b8046SYuri Pankov buf[(*i)++] = *cp++; 526c66b8046SYuri Pankov } 527c66b8046SYuri Pankov 528a40ea1a7SYuri Pankov } 529a40ea1a7SYuri Pankov 530a40ea1a7SYuri Pankov /* 531a40ea1a7SYuri Pankov * Return 1 if the string *want occurs in any of the strings 532a40ea1a7SYuri Pankov * in the NUL-terminated string list *have, or 0 otherwise. 533a40ea1a7SYuri Pankov * If either argument is NULL or empty, assume no filtering 534a40ea1a7SYuri Pankov * is desired and return 1. 535a40ea1a7SYuri Pankov */ 536a40ea1a7SYuri Pankov static int 537a40ea1a7SYuri Pankov lstmatch(const char *want, const char *have) 538a40ea1a7SYuri Pankov { 539a40ea1a7SYuri Pankov if (want == NULL || have == NULL || *have == '\0') 540a40ea1a7SYuri Pankov return 1; 541a40ea1a7SYuri Pankov while (*have != '\0') { 542a40ea1a7SYuri Pankov if (strcasestr(have, want) != NULL) 543a40ea1a7SYuri Pankov return 1; 544a40ea1a7SYuri Pankov have = strchr(have, '\0') + 1; 545a40ea1a7SYuri Pankov } 546a40ea1a7SYuri Pankov return 0; 547a40ea1a7SYuri Pankov } 548a40ea1a7SYuri Pankov 549a40ea1a7SYuri Pankov /* 550c66b8046SYuri Pankov * Build a list of values taken by the macro im in the manual page. 551a40ea1a7SYuri Pankov */ 552a40ea1a7SYuri Pankov static char * 553c66b8046SYuri Pankov buildoutput(size_t im, struct dbm_page *page) 554a40ea1a7SYuri Pankov { 555c66b8046SYuri Pankov const char *oldoutput, *sep, *input; 556a40ea1a7SYuri Pankov char *output, *newoutput, *value; 557c66b8046SYuri Pankov size_t sz, i; 558c66b8046SYuri Pankov 559c66b8046SYuri Pankov switch (im) { 560c66b8046SYuri Pankov case KEY_Nd: 561c66b8046SYuri Pankov return mandoc_strdup(page->desc); 562c66b8046SYuri Pankov case KEY_Nm: 563c66b8046SYuri Pankov input = page->name; 564c66b8046SYuri Pankov break; 565c66b8046SYuri Pankov case KEY_sec: 566c66b8046SYuri Pankov input = page->sect; 567c66b8046SYuri Pankov break; 568c66b8046SYuri Pankov case KEY_arch: 569c66b8046SYuri Pankov input = page->arch; 570c66b8046SYuri Pankov if (input == NULL) 571c66b8046SYuri Pankov input = "all\0"; 572c66b8046SYuri Pankov break; 573c66b8046SYuri Pankov default: 574c66b8046SYuri Pankov input = NULL; 575c66b8046SYuri Pankov break; 576c66b8046SYuri Pankov } 577c66b8046SYuri Pankov 578c66b8046SYuri Pankov if (input != NULL) { 579c66b8046SYuri Pankov sz = lstlen(input, 3) + 1; 580c66b8046SYuri Pankov output = mandoc_malloc(sz); 581c66b8046SYuri Pankov i = 0; 582c66b8046SYuri Pankov lstcat(output, &i, input, " # "); 583c66b8046SYuri Pankov output[i++] = '\0'; 584c66b8046SYuri Pankov assert(i == sz); 585c66b8046SYuri Pankov return output; 586c66b8046SYuri Pankov } 587a40ea1a7SYuri Pankov 588a40ea1a7SYuri Pankov output = NULL; 589c66b8046SYuri Pankov dbm_macro_bypage(im - 2, page->addr); 590a40ea1a7SYuri Pankov while ((value = dbm_macro_next()) != NULL) { 591a40ea1a7SYuri Pankov if (output == NULL) { 592a40ea1a7SYuri Pankov oldoutput = ""; 593a40ea1a7SYuri Pankov sep = ""; 594a40ea1a7SYuri Pankov } else { 595a40ea1a7SYuri Pankov oldoutput = output; 596a40ea1a7SYuri Pankov sep = " # "; 597a40ea1a7SYuri Pankov } 598a40ea1a7SYuri Pankov mandoc_asprintf(&newoutput, "%s%s%s", oldoutput, sep, value); 599a40ea1a7SYuri Pankov free(output); 600a40ea1a7SYuri Pankov output = newoutput; 601a40ea1a7SYuri Pankov } 602a40ea1a7SYuri Pankov return output; 603a40ea1a7SYuri Pankov } 604a40ea1a7SYuri Pankov 605a40ea1a7SYuri Pankov /* 606a40ea1a7SYuri Pankov * Compile a set of string tokens into an expression. 607a40ea1a7SYuri Pankov * Tokens in "argv" are assumed to be individual expression atoms (e.g., 608a40ea1a7SYuri Pankov * "(", "foo=bar", etc.). 609a40ea1a7SYuri Pankov */ 610a40ea1a7SYuri Pankov static struct expr * 611a40ea1a7SYuri Pankov exprcomp(const struct mansearch *search, int argc, char *argv[], int *argi) 612a40ea1a7SYuri Pankov { 613a40ea1a7SYuri Pankov struct expr *parent, *child; 614a40ea1a7SYuri Pankov int needterm, nested; 615a40ea1a7SYuri Pankov 616a40ea1a7SYuri Pankov if ((nested = *argi) == argc) 617a40ea1a7SYuri Pankov return NULL; 618a40ea1a7SYuri Pankov needterm = 1; 619a40ea1a7SYuri Pankov parent = child = NULL; 620a40ea1a7SYuri Pankov while (*argi < argc) { 621a40ea1a7SYuri Pankov if (strcmp(")", argv[*argi]) == 0) { 622a40ea1a7SYuri Pankov if (needterm) 623a40ea1a7SYuri Pankov warnx("missing term " 624a40ea1a7SYuri Pankov "before closing parenthesis"); 625a40ea1a7SYuri Pankov needterm = 0; 626a40ea1a7SYuri Pankov if (nested) 627a40ea1a7SYuri Pankov break; 628a40ea1a7SYuri Pankov warnx("ignoring unmatched right parenthesis"); 629a40ea1a7SYuri Pankov ++*argi; 630a40ea1a7SYuri Pankov continue; 631a40ea1a7SYuri Pankov } 632a40ea1a7SYuri Pankov if (strcmp("-o", argv[*argi]) == 0) { 633a40ea1a7SYuri Pankov if (needterm) { 634a40ea1a7SYuri Pankov if (*argi > 0) 635a40ea1a7SYuri Pankov warnx("ignoring -o after %s", 636a40ea1a7SYuri Pankov argv[*argi - 1]); 637a40ea1a7SYuri Pankov else 638a40ea1a7SYuri Pankov warnx("ignoring initial -o"); 639a40ea1a7SYuri Pankov } 640a40ea1a7SYuri Pankov needterm = 1; 641a40ea1a7SYuri Pankov ++*argi; 642a40ea1a7SYuri Pankov continue; 643a40ea1a7SYuri Pankov } 644a40ea1a7SYuri Pankov needterm = 0; 645a40ea1a7SYuri Pankov if (child == NULL) { 646a40ea1a7SYuri Pankov child = expr_and(search, argc, argv, argi); 647a40ea1a7SYuri Pankov continue; 648a40ea1a7SYuri Pankov } 649a40ea1a7SYuri Pankov if (parent == NULL) { 650a40ea1a7SYuri Pankov parent = mandoc_calloc(1, sizeof(*parent)); 651a40ea1a7SYuri Pankov parent->type = EXPR_OR; 652a40ea1a7SYuri Pankov parent->next = NULL; 653a40ea1a7SYuri Pankov parent->child = child; 654a40ea1a7SYuri Pankov } 655a40ea1a7SYuri Pankov child->next = expr_and(search, argc, argv, argi); 656a40ea1a7SYuri Pankov child = child->next; 657a40ea1a7SYuri Pankov } 658a40ea1a7SYuri Pankov if (needterm && *argi) 659a40ea1a7SYuri Pankov warnx("ignoring trailing %s", argv[*argi - 1]); 660a40ea1a7SYuri Pankov return parent == NULL ? child : parent; 661a40ea1a7SYuri Pankov } 662a40ea1a7SYuri Pankov 663a40ea1a7SYuri Pankov static struct expr * 664a40ea1a7SYuri Pankov expr_and(const struct mansearch *search, int argc, char *argv[], int *argi) 665a40ea1a7SYuri Pankov { 666a40ea1a7SYuri Pankov struct expr *parent, *child; 667a40ea1a7SYuri Pankov int needterm; 668a40ea1a7SYuri Pankov 669a40ea1a7SYuri Pankov needterm = 1; 670a40ea1a7SYuri Pankov parent = child = NULL; 671a40ea1a7SYuri Pankov while (*argi < argc) { 672a40ea1a7SYuri Pankov if (strcmp(")", argv[*argi]) == 0) { 673a40ea1a7SYuri Pankov if (needterm) 674a40ea1a7SYuri Pankov warnx("missing term " 675a40ea1a7SYuri Pankov "before closing parenthesis"); 676a40ea1a7SYuri Pankov needterm = 0; 677a40ea1a7SYuri Pankov break; 678a40ea1a7SYuri Pankov } 679a40ea1a7SYuri Pankov if (strcmp("-o", argv[*argi]) == 0) 680a40ea1a7SYuri Pankov break; 681a40ea1a7SYuri Pankov if (strcmp("-a", argv[*argi]) == 0) { 682a40ea1a7SYuri Pankov if (needterm) { 683a40ea1a7SYuri Pankov if (*argi > 0) 684a40ea1a7SYuri Pankov warnx("ignoring -a after %s", 685a40ea1a7SYuri Pankov argv[*argi - 1]); 686a40ea1a7SYuri Pankov else 687a40ea1a7SYuri Pankov warnx("ignoring initial -a"); 688a40ea1a7SYuri Pankov } 689a40ea1a7SYuri Pankov needterm = 1; 690a40ea1a7SYuri Pankov ++*argi; 691a40ea1a7SYuri Pankov continue; 692a40ea1a7SYuri Pankov } 693a40ea1a7SYuri Pankov if (needterm == 0) 694a40ea1a7SYuri Pankov break; 695a40ea1a7SYuri Pankov if (child == NULL) { 696a40ea1a7SYuri Pankov child = exprterm(search, argc, argv, argi); 697a40ea1a7SYuri Pankov if (child != NULL) 698a40ea1a7SYuri Pankov needterm = 0; 699a40ea1a7SYuri Pankov continue; 700a40ea1a7SYuri Pankov } 701a40ea1a7SYuri Pankov needterm = 0; 702a40ea1a7SYuri Pankov if (parent == NULL) { 703a40ea1a7SYuri Pankov parent = mandoc_calloc(1, sizeof(*parent)); 704a40ea1a7SYuri Pankov parent->type = EXPR_AND; 705a40ea1a7SYuri Pankov parent->next = NULL; 706a40ea1a7SYuri Pankov parent->child = child; 707a40ea1a7SYuri Pankov } 708a40ea1a7SYuri Pankov child->next = exprterm(search, argc, argv, argi); 709a40ea1a7SYuri Pankov if (child->next != NULL) { 710a40ea1a7SYuri Pankov child = child->next; 711a40ea1a7SYuri Pankov needterm = 0; 712a40ea1a7SYuri Pankov } 713a40ea1a7SYuri Pankov } 714a40ea1a7SYuri Pankov if (needterm && *argi) 715a40ea1a7SYuri Pankov warnx("ignoring trailing %s", argv[*argi - 1]); 716a40ea1a7SYuri Pankov return parent == NULL ? child : parent; 717a40ea1a7SYuri Pankov } 718a40ea1a7SYuri Pankov 719a40ea1a7SYuri Pankov static struct expr * 720a40ea1a7SYuri Pankov exprterm(const struct mansearch *search, int argc, char *argv[], int *argi) 721a40ea1a7SYuri Pankov { 722a40ea1a7SYuri Pankov char errbuf[BUFSIZ]; 723a40ea1a7SYuri Pankov struct expr *e; 724a40ea1a7SYuri Pankov char *key, *val; 725a40ea1a7SYuri Pankov uint64_t iterbit; 726a40ea1a7SYuri Pankov int cs, i, irc; 727a40ea1a7SYuri Pankov 728a40ea1a7SYuri Pankov if (strcmp("(", argv[*argi]) == 0) { 729a40ea1a7SYuri Pankov ++*argi; 730a40ea1a7SYuri Pankov e = exprcomp(search, argc, argv, argi); 731a40ea1a7SYuri Pankov if (*argi < argc) { 732a40ea1a7SYuri Pankov assert(strcmp(")", argv[*argi]) == 0); 733a40ea1a7SYuri Pankov ++*argi; 734a40ea1a7SYuri Pankov } else 735a40ea1a7SYuri Pankov warnx("unclosed parenthesis"); 736a40ea1a7SYuri Pankov return e; 737a40ea1a7SYuri Pankov } 738a40ea1a7SYuri Pankov 739c66b8046SYuri Pankov if (strcmp("-i", argv[*argi]) == 0 && *argi + 1 < argc) { 740c66b8046SYuri Pankov cs = 0; 741c66b8046SYuri Pankov ++*argi; 742c66b8046SYuri Pankov } else 743c66b8046SYuri Pankov cs = 1; 744c66b8046SYuri Pankov 745a40ea1a7SYuri Pankov e = mandoc_calloc(1, sizeof(*e)); 746a40ea1a7SYuri Pankov e->type = EXPR_TERM; 747a40ea1a7SYuri Pankov e->bits = 0; 748a40ea1a7SYuri Pankov e->next = NULL; 749a40ea1a7SYuri Pankov e->child = NULL; 750a40ea1a7SYuri Pankov 751a40ea1a7SYuri Pankov if (search->argmode == ARG_NAME) { 752a40ea1a7SYuri Pankov e->bits = TYPE_Nm; 753a40ea1a7SYuri Pankov e->match.type = DBM_EXACT; 754a40ea1a7SYuri Pankov e->match.str = argv[(*argi)++]; 755a40ea1a7SYuri Pankov return e; 756a40ea1a7SYuri Pankov } 757a40ea1a7SYuri Pankov 758a40ea1a7SYuri Pankov /* 759a40ea1a7SYuri Pankov * Separate macro keys from search string. 760a40ea1a7SYuri Pankov * If needed, request regular expression handling. 761a40ea1a7SYuri Pankov */ 762a40ea1a7SYuri Pankov 763a40ea1a7SYuri Pankov if (search->argmode == ARG_WORD) { 764a40ea1a7SYuri Pankov e->bits = TYPE_Nm; 765a40ea1a7SYuri Pankov e->match.type = DBM_REGEX; 766a40ea1a7SYuri Pankov #if HAVE_REWB_BSD 767a40ea1a7SYuri Pankov mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", argv[*argi]); 768a40ea1a7SYuri Pankov #elif HAVE_REWB_SYSV 769a40ea1a7SYuri Pankov mandoc_asprintf(&val, "\\<%s\\>", argv[*argi]); 770a40ea1a7SYuri Pankov #else 771a40ea1a7SYuri Pankov mandoc_asprintf(&val, 772a40ea1a7SYuri Pankov "(^|[^a-zA-Z01-9_])%s([^a-zA-Z01-9_]|$)", argv[*argi]); 773a40ea1a7SYuri Pankov #endif 774a40ea1a7SYuri Pankov cs = 0; 775a40ea1a7SYuri Pankov } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) { 776a40ea1a7SYuri Pankov e->bits = TYPE_Nm | TYPE_Nd; 777a40ea1a7SYuri Pankov e->match.type = DBM_SUB; 778a40ea1a7SYuri Pankov e->match.str = argv[*argi]; 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 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