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