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