mansearch.c revision 1.19
1/* $Id: mansearch.c,v 1.19 2014/04/11 15:45:39 schwarze Exp $ */ 2/* 3 * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv> 4 * Copyright (c) 2013, 2014 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 AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 <sys/mman.h> 19#include <assert.h> 20#include <fcntl.h> 21#include <getopt.h> 22#include <limits.h> 23#include <regex.h> 24#include <stdio.h> 25#include <stdint.h> 26#include <stddef.h> 27#include <stdlib.h> 28#include <string.h> 29#include <unistd.h> 30 31#include <ohash.h> 32#include <sqlite3.h> 33 34#include "mandoc.h" 35#include "mandoc_aux.h" 36#include "manpath.h" 37#include "mansearch.h" 38 39extern int mansearch_keymax; 40extern const char *const mansearch_keynames[]; 41 42#define SQL_BIND_TEXT(_db, _s, _i, _v) \ 43 do { if (SQLITE_OK != sqlite3_bind_text \ 44 ((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \ 45 fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ 46 } while (0) 47#define SQL_BIND_INT64(_db, _s, _i, _v) \ 48 do { if (SQLITE_OK != sqlite3_bind_int64 \ 49 ((_s), (_i)++, (_v))) \ 50 fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ 51 } while (0) 52#define SQL_BIND_BLOB(_db, _s, _i, _v) \ 53 do { if (SQLITE_OK != sqlite3_bind_blob \ 54 ((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \ 55 fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ 56 } while (0) 57 58struct expr { 59 uint64_t bits; /* type-mask */ 60 const char *substr; /* to search for, if applicable */ 61 regex_t regexp; /* compiled regexp, if applicable */ 62 int open; /* opening parentheses before */ 63 int and; /* logical AND before */ 64 int close; /* closing parentheses after */ 65 struct expr *next; /* next in sequence */ 66}; 67 68struct match { 69 uint64_t id; /* identifier in database */ 70 char *desc; /* manual page description */ 71 int form; /* 0 == catpage */ 72}; 73 74static void buildnames(struct manpage *, sqlite3 *, 75 sqlite3_stmt *, uint64_t, 76 const char *, int form); 77static char *buildoutput(sqlite3 *, sqlite3_stmt *, 78 uint64_t, uint64_t); 79static void *hash_alloc(size_t, void *); 80static void hash_free(void *, size_t, void *); 81static void *hash_halloc(size_t, void *); 82static struct expr *exprcomp(const struct mansearch *, 83 int, char *[]); 84static void exprfree(struct expr *); 85static struct expr *exprspec(struct expr *, uint64_t, 86 const char *, const char *); 87static struct expr *exprterm(const struct mansearch *, char *, int); 88static void sql_append(char **sql, size_t *sz, 89 const char *newstr, int count); 90static void sql_match(sqlite3_context *context, 91 int argc, sqlite3_value **argv); 92static void sql_regexp(sqlite3_context *context, 93 int argc, sqlite3_value **argv); 94static char *sql_statement(const struct expr *); 95 96int 97mansearch_setup(int start) 98{ 99 static void *pagecache; 100 int c; 101 102#define PC_PAGESIZE 1280 103#define PC_NUMPAGES 256 104 105 if (start) { 106 if (NULL != pagecache) { 107 fprintf(stderr, "pagecache already enabled\n"); 108 return((int)MANDOCLEVEL_BADARG); 109 } 110 111 pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES, 112 PROT_READ | PROT_WRITE, MAP_ANON, -1, 0); 113 114 if (MAP_FAILED == pagecache) { 115 perror("mmap"); 116 pagecache = NULL; 117 return((int)MANDOCLEVEL_SYSERR); 118 } 119 120 c = sqlite3_config(SQLITE_CONFIG_PAGECACHE, 121 pagecache, PC_PAGESIZE, PC_NUMPAGES); 122 123 if (SQLITE_OK == c) 124 return((int)MANDOCLEVEL_OK); 125 126 fprintf(stderr, "pagecache: %s\n", sqlite3_errstr(c)); 127 128 } else if (NULL == pagecache) { 129 fprintf(stderr, "pagecache missing\n"); 130 return((int)MANDOCLEVEL_BADARG); 131 } 132 133 if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) { 134 perror("munmap"); 135 pagecache = NULL; 136 return((int)MANDOCLEVEL_SYSERR); 137 } 138 139 pagecache = NULL; 140 return((int)MANDOCLEVEL_OK); 141} 142 143int 144mansearch(const struct mansearch *search, 145 const struct manpaths *paths, 146 int argc, char *argv[], 147 const char *outkey, 148 struct manpage **res, size_t *sz) 149{ 150 int fd, rc, c, indexbit; 151 int64_t id; 152 uint64_t outbit, iterbit; 153 char buf[PATH_MAX]; 154 char *sql; 155 struct manpage *mpage; 156 struct expr *e, *ep; 157 sqlite3 *db; 158 sqlite3_stmt *s, *s2; 159 struct match *mp; 160 struct ohash_info info; 161 struct ohash htab; 162 unsigned int idx; 163 size_t i, j, cur, maxres; 164 165 memset(&info, 0, sizeof(struct ohash_info)); 166 167 info.halloc = hash_halloc; 168 info.alloc = hash_alloc; 169 info.hfree = hash_free; 170 info.key_offset = offsetof(struct match, id); 171 172 *sz = cur = maxres = 0; 173 sql = NULL; 174 *res = NULL; 175 fd = -1; 176 e = NULL; 177 rc = 0; 178 179 if (0 == argc) 180 goto out; 181 if (NULL == (e = exprcomp(search, argc, argv))) 182 goto out; 183 184 outbit = 0; 185 if (NULL != outkey) { 186 for (indexbit = 0, iterbit = 1; 187 indexbit < mansearch_keymax; 188 indexbit++, iterbit <<= 1) { 189 if (0 == strcasecmp(outkey, 190 mansearch_keynames[indexbit])) { 191 outbit = iterbit; 192 break; 193 } 194 } 195 } 196 197 /* 198 * Save a descriptor to the current working directory. 199 * Since pathnames in the "paths" variable might be relative, 200 * and we'll be chdir()ing into them, we need to keep a handle 201 * on our current directory from which to start the chdir(). 202 */ 203 204 if (NULL == getcwd(buf, PATH_MAX)) { 205 perror(NULL); 206 goto out; 207 } else if (-1 == (fd = open(buf, O_RDONLY, 0))) { 208 perror(buf); 209 goto out; 210 } 211 212 sql = sql_statement(e); 213 214 /* 215 * Loop over the directories (containing databases) for us to 216 * search. 217 * Don't let missing/bad databases/directories phase us. 218 * In each, try to open the resident database and, if it opens, 219 * scan it for our match expression. 220 */ 221 222 for (i = 0; i < paths->sz; i++) { 223 if (-1 == fchdir(fd)) { 224 perror(buf); 225 free(*res); 226 break; 227 } else if (-1 == chdir(paths->paths[i])) { 228 perror(paths->paths[i]); 229 continue; 230 } 231 232 c = sqlite3_open_v2 233 (MANDOC_DB, &db, 234 SQLITE_OPEN_READONLY, NULL); 235 236 if (SQLITE_OK != c) { 237 perror(MANDOC_DB); 238 sqlite3_close(db); 239 continue; 240 } 241 242 /* 243 * Define the SQL functions for substring 244 * and regular expression matching. 245 */ 246 247 c = sqlite3_create_function(db, "match", 2, 248 SQLITE_ANY, NULL, sql_match, NULL, NULL); 249 assert(SQLITE_OK == c); 250 c = sqlite3_create_function(db, "regexp", 2, 251 SQLITE_ANY, NULL, sql_regexp, NULL, NULL); 252 assert(SQLITE_OK == c); 253 254 j = 1; 255 c = sqlite3_prepare_v2(db, sql, -1, &s, NULL); 256 if (SQLITE_OK != c) 257 fprintf(stderr, "%s\n", sqlite3_errmsg(db)); 258 259 for (ep = e; NULL != ep; ep = ep->next) { 260 if (NULL == ep->substr) { 261 SQL_BIND_BLOB(db, s, j, ep->regexp); 262 } else 263 SQL_BIND_TEXT(db, s, j, ep->substr); 264 if (0 == ((TYPE_Nd | TYPE_Nm) & ep->bits)) 265 SQL_BIND_INT64(db, s, j, ep->bits); 266 } 267 268 memset(&htab, 0, sizeof(struct ohash)); 269 ohash_init(&htab, 4, &info); 270 271 /* 272 * Hash each entry on its [unique] document identifier. 273 * This is a uint64_t. 274 * Instead of using a hash function, simply convert the 275 * uint64_t to a uint32_t, the hash value's type. 276 * This gives good performance and preserves the 277 * distribution of buckets in the table. 278 */ 279 while (SQLITE_ROW == (c = sqlite3_step(s))) { 280 id = sqlite3_column_int64(s, 2); 281 idx = ohash_lookup_memory 282 (&htab, (char *)&id, 283 sizeof(uint64_t), (uint32_t)id); 284 285 if (NULL != ohash_find(&htab, idx)) 286 continue; 287 288 mp = mandoc_calloc(1, sizeof(struct match)); 289 mp->id = id; 290 mp->form = sqlite3_column_int(s, 1); 291 if (TYPE_Nd == outbit) 292 mp->desc = mandoc_strdup( 293 sqlite3_column_text(s, 0)); 294 ohash_insert(&htab, idx, mp); 295 } 296 297 if (SQLITE_DONE != c) 298 fprintf(stderr, "%s\n", sqlite3_errmsg(db)); 299 300 sqlite3_finalize(s); 301 302 c = sqlite3_prepare_v2(db, 303 "SELECT * FROM mlinks WHERE pageid=?" 304 " ORDER BY sec, arch, name", 305 -1, &s, NULL); 306 if (SQLITE_OK != c) 307 fprintf(stderr, "%s\n", sqlite3_errmsg(db)); 308 309 c = sqlite3_prepare_v2(db, 310 "SELECT * FROM keys WHERE pageid=? AND bits & ?", 311 -1, &s2, NULL); 312 if (SQLITE_OK != c) 313 fprintf(stderr, "%s\n", sqlite3_errmsg(db)); 314 315 for (mp = ohash_first(&htab, &idx); 316 NULL != mp; 317 mp = ohash_next(&htab, &idx)) { 318 if (cur + 1 > maxres) { 319 maxres += 1024; 320 *res = mandoc_realloc 321 (*res, maxres * sizeof(struct manpage)); 322 } 323 mpage = *res + cur; 324 mpage->form = mp->form; 325 buildnames(mpage, db, s, mp->id, 326 paths->paths[i], mp->form); 327 mpage->output = TYPE_Nd & outbit ? 328 mp->desc : outbit ? 329 buildoutput(db, s2, mp->id, outbit) : NULL; 330 331 free(mp); 332 cur++; 333 } 334 335 sqlite3_finalize(s); 336 sqlite3_finalize(s2); 337 sqlite3_close(db); 338 ohash_delete(&htab); 339 } 340 rc = 1; 341out: 342 exprfree(e); 343 if (-1 != fd) 344 close(fd); 345 free(sql); 346 *sz = cur; 347 return(rc); 348} 349 350static void 351buildnames(struct manpage *mpage, sqlite3 *db, sqlite3_stmt *s, 352 uint64_t id, const char *path, int form) 353{ 354 char *newnames, *prevsec, *prevarch; 355 const char *oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec; 356 size_t i; 357 int c; 358 359 mpage->file = NULL; 360 mpage->names = NULL; 361 prevsec = prevarch = NULL; 362 i = 1; 363 SQL_BIND_INT64(db, s, i, id); 364 while (SQLITE_ROW == (c = sqlite3_step(s))) { 365 366 /* Decide whether we already have some names. */ 367 368 if (NULL == mpage->names) { 369 oldnames = ""; 370 sep1 = ""; 371 } else { 372 oldnames = mpage->names; 373 sep1 = ", "; 374 } 375 376 /* Fetch the next name. */ 377 378 sec = sqlite3_column_text(s, 0); 379 arch = sqlite3_column_text(s, 1); 380 name = sqlite3_column_text(s, 2); 381 382 /* If the section changed, append the old one. */ 383 384 if (NULL != prevsec && 385 (strcmp(sec, prevsec) || 386 strcmp(arch, prevarch))) { 387 sep2 = '\0' == *prevarch ? "" : "/"; 388 mandoc_asprintf(&newnames, "%s(%s%s%s)", 389 oldnames, prevsec, sep2, prevarch); 390 free(mpage->names); 391 oldnames = mpage->names = newnames; 392 free(prevsec); 393 free(prevarch); 394 prevsec = prevarch = NULL; 395 } 396 397 /* Save the new section, to append it later. */ 398 399 if (NULL == prevsec) { 400 prevsec = mandoc_strdup(sec); 401 prevarch = mandoc_strdup(arch); 402 } 403 404 /* Append the new name. */ 405 406 mandoc_asprintf(&newnames, "%s%s%s", 407 oldnames, sep1, name); 408 free(mpage->names); 409 mpage->names = newnames; 410 411 /* Also save the first file name encountered. */ 412 413 if (NULL != mpage->file) 414 continue; 415 416 if (form) { 417 sep1 = "man"; 418 fsec = sec; 419 } else { 420 sep1 = "cat"; 421 fsec = "0"; 422 } 423 sep2 = '\0' == *arch ? "" : "/"; 424 mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s", 425 path, sep1, sec, sep2, arch, name, fsec); 426 } 427 if (SQLITE_DONE != c) 428 fprintf(stderr, "%s\n", sqlite3_errmsg(db)); 429 sqlite3_reset(s); 430 431 /* Append one final section to the names. */ 432 433 if (NULL != prevsec) { 434 sep2 = '\0' == *prevarch ? "" : "/"; 435 mandoc_asprintf(&newnames, "%s(%s%s%s)", 436 mpage->names, prevsec, sep2, prevarch); 437 free(mpage->names); 438 mpage->names = newnames; 439 free(prevsec); 440 free(prevarch); 441 } 442} 443 444static char * 445buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t id, uint64_t outbit) 446{ 447 char *output, *newoutput; 448 const char *oldoutput, *sep1, *data; 449 size_t i; 450 int c; 451 452 output = NULL; 453 i = 1; 454 SQL_BIND_INT64(db, s, i, id); 455 SQL_BIND_INT64(db, s, i, outbit); 456 while (SQLITE_ROW == (c = sqlite3_step(s))) { 457 if (NULL == output) { 458 oldoutput = ""; 459 sep1 = ""; 460 } else { 461 oldoutput = output; 462 sep1 = " # "; 463 } 464 data = sqlite3_column_text(s, 1); 465 mandoc_asprintf(&newoutput, "%s%s%s", 466 oldoutput, sep1, data); 467 free(output); 468 output = newoutput; 469 } 470 if (SQLITE_DONE != c) 471 fprintf(stderr, "%s\n", sqlite3_errmsg(db)); 472 sqlite3_reset(s); 473 return(output); 474} 475 476/* 477 * Implement substring match as an application-defined SQL function. 478 * Using the SQL LIKE or GLOB operators instead would be a bad idea 479 * because that would require escaping metacharacters in the string 480 * being searched for. 481 */ 482static void 483sql_match(sqlite3_context *context, int argc, sqlite3_value **argv) 484{ 485 486 assert(2 == argc); 487 sqlite3_result_int(context, NULL != strcasestr( 488 (const char *)sqlite3_value_text(argv[1]), 489 (const char *)sqlite3_value_text(argv[0]))); 490} 491 492/* 493 * Implement regular expression match 494 * as an application-defined SQL function. 495 */ 496static void 497sql_regexp(sqlite3_context *context, int argc, sqlite3_value **argv) 498{ 499 500 assert(2 == argc); 501 sqlite3_result_int(context, !regexec( 502 (regex_t *)sqlite3_value_blob(argv[0]), 503 (const char *)sqlite3_value_text(argv[1]), 504 0, NULL, 0)); 505} 506 507static void 508sql_append(char **sql, size_t *sz, const char *newstr, int count) 509{ 510 size_t newsz; 511 512 newsz = 1 < count ? (size_t)count : strlen(newstr); 513 *sql = mandoc_realloc(*sql, *sz + newsz + 1); 514 if (1 < count) 515 memset(*sql + *sz, *newstr, (size_t)count); 516 else 517 memcpy(*sql + *sz, newstr, newsz); 518 *sz += newsz; 519 (*sql)[*sz] = '\0'; 520} 521 522/* 523 * Prepare the search SQL statement. 524 */ 525static char * 526sql_statement(const struct expr *e) 527{ 528 char *sql; 529 size_t sz; 530 int needop; 531 532 sql = mandoc_strdup("SELECT * FROM mpages WHERE "); 533 sz = strlen(sql); 534 535 for (needop = 0; NULL != e; e = e->next) { 536 if (e->and) 537 sql_append(&sql, &sz, " AND ", 1); 538 else if (needop) 539 sql_append(&sql, &sz, " OR ", 1); 540 if (e->open) 541 sql_append(&sql, &sz, "(", e->open); 542 sql_append(&sql, &sz, 543 TYPE_Nd & e->bits 544 ? (NULL == e->substr 545 ? "desc REGEXP ?" 546 : "desc MATCH ?") 547 : TYPE_Nm == e->bits 548 ? (NULL == e->substr 549 ? "id IN (SELECT pageid FROM names " 550 "WHERE name REGEXP ?)" 551 : "id IN (SELECT pageid FROM names " 552 "WHERE name MATCH ?)") 553 : (NULL == e->substr 554 ? "id IN (SELECT pageid FROM keys " 555 "WHERE key REGEXP ? AND bits & ?)" 556 : "id IN (SELECT pageid FROM keys " 557 "WHERE key MATCH ? AND bits & ?)"), 1); 558 if (e->close) 559 sql_append(&sql, &sz, ")", e->close); 560 needop = 1; 561 } 562 563 return(sql); 564} 565 566/* 567 * Compile a set of string tokens into an expression. 568 * Tokens in "argv" are assumed to be individual expression atoms (e.g., 569 * "(", "foo=bar", etc.). 570 */ 571static struct expr * 572exprcomp(const struct mansearch *search, int argc, char *argv[]) 573{ 574 uint64_t mask; 575 int i, toopen, logic, igncase, toclose; 576 struct expr *first, *prev, *cur, *next; 577 578 first = cur = NULL; 579 logic = igncase = toclose = 0; 580 toopen = 1; 581 582 for (i = 0; i < argc; i++) { 583 if (0 == strcmp("(", argv[i])) { 584 if (igncase) 585 goto fail; 586 toopen++; 587 toclose++; 588 continue; 589 } else if (0 == strcmp(")", argv[i])) { 590 if (toopen || logic || igncase || NULL == cur) 591 goto fail; 592 cur->close++; 593 if (0 > --toclose) 594 goto fail; 595 continue; 596 } else if (0 == strcmp("-a", argv[i])) { 597 if (toopen || logic || igncase || NULL == cur) 598 goto fail; 599 logic = 1; 600 continue; 601 } else if (0 == strcmp("-o", argv[i])) { 602 if (toopen || logic || igncase || NULL == cur) 603 goto fail; 604 logic = 2; 605 continue; 606 } else if (0 == strcmp("-i", argv[i])) { 607 if (igncase) 608 goto fail; 609 igncase = 1; 610 continue; 611 } 612 next = exprterm(search, argv[i], !igncase); 613 if (NULL == next) 614 goto fail; 615 if (NULL == first) 616 first = next; 617 else 618 cur->next = next; 619 prev = cur = next; 620 621 /* 622 * Searching for descriptions must be split out 623 * because they are stored in the mpages table, 624 * not in the keys table. 625 */ 626 627 for (mask = TYPE_Nm; mask <= TYPE_Nd; mask <<= 1) { 628 if (mask & cur->bits && ~mask & cur->bits) { 629 next = mandoc_calloc(1, 630 sizeof(struct expr)); 631 memcpy(next, cur, sizeof(struct expr)); 632 prev->open = 1; 633 cur->bits = mask; 634 cur->next = next; 635 cur = next; 636 cur->bits &= ~mask; 637 } 638 } 639 prev->and = (1 == logic); 640 prev->open += toopen; 641 if (cur != prev) 642 cur->close = 1; 643 644 toopen = logic = igncase = 0; 645 } 646 if (toopen || logic || igncase || toclose) 647 goto fail; 648 649 cur->close++; 650 cur = exprspec(cur, TYPE_arch, search->arch, "^(%s|any)$"); 651 exprspec(cur, TYPE_sec, search->sec, "^%s$"); 652 653 return(first); 654 655fail: 656 if (NULL != first) 657 exprfree(first); 658 return(NULL); 659} 660 661static struct expr * 662exprspec(struct expr *cur, uint64_t key, const char *value, 663 const char *format) 664{ 665 char errbuf[BUFSIZ]; 666 char *cp; 667 int irc; 668 669 if (NULL == value) 670 return(cur); 671 672 mandoc_asprintf(&cp, format, value); 673 cur->next = mandoc_calloc(1, sizeof(struct expr)); 674 cur = cur->next; 675 cur->and = 1; 676 cur->bits = key; 677 if (0 != (irc = regcomp(&cur->regexp, cp, 678 REG_EXTENDED | REG_NOSUB | REG_ICASE))) { 679 regerror(irc, &cur->regexp, errbuf, sizeof(errbuf)); 680 fprintf(stderr, "regcomp: %s\n", errbuf); 681 cur->substr = value; 682 } 683 free(cp); 684 return(cur); 685} 686 687static struct expr * 688exprterm(const struct mansearch *search, char *buf, int cs) 689{ 690 char errbuf[BUFSIZ]; 691 struct expr *e; 692 char *key, *v; 693 uint64_t iterbit; 694 int i, irc; 695 696 if ('\0' == *buf) 697 return(NULL); 698 699 e = mandoc_calloc(1, sizeof(struct expr)); 700 701 /*"whatis" mode uses an opaque string and default fields. */ 702 703 if (MANSEARCH_WHATIS & search->flags) { 704 e->substr = buf; 705 e->bits = search->deftype; 706 return(e); 707 } 708 709 /* 710 * If no =~ is specified, search with equality over names and 711 * descriptions. 712 * If =~ begins the phrase, use name and description fields. 713 */ 714 715 if (NULL == (v = strpbrk(buf, "=~"))) { 716 e->substr = buf; 717 e->bits = search->deftype; 718 return(e); 719 } else if (v == buf) 720 e->bits = search->deftype; 721 722 if ('~' == *v++) { 723 if (NULL != strstr(buf, "arch")) 724 cs = 0; 725 if (0 != (irc = regcomp(&e->regexp, v, 726 REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE)))) { 727 regerror(irc, &e->regexp, errbuf, sizeof(errbuf)); 728 fprintf(stderr, "regcomp: %s\n", errbuf); 729 free(e); 730 return(NULL); 731 } 732 } else 733 e->substr = v; 734 v[-1] = '\0'; 735 736 /* 737 * Parse out all possible fields. 738 * If the field doesn't resolve, bail. 739 */ 740 741 while (NULL != (key = strsep(&buf, ","))) { 742 if ('\0' == *key) 743 continue; 744 for (i = 0, iterbit = 1; 745 i < mansearch_keymax; 746 i++, iterbit <<= 1) { 747 if (0 == strcasecmp(key, 748 mansearch_keynames[i])) { 749 e->bits |= iterbit; 750 break; 751 } 752 } 753 if (i == mansearch_keymax) { 754 if (strcasecmp(key, "any")) { 755 free(e); 756 return(NULL); 757 } 758 e->bits |= ~0ULL; 759 } 760 } 761 762 return(e); 763} 764 765static void 766exprfree(struct expr *p) 767{ 768 struct expr *pp; 769 770 while (NULL != p) { 771 pp = p->next; 772 free(p); 773 p = pp; 774 } 775} 776 777static void * 778hash_halloc(size_t sz, void *arg) 779{ 780 781 return(mandoc_calloc(sz, 1)); 782} 783 784static void * 785hash_alloc(size_t sz, void *arg) 786{ 787 788 return(mandoc_malloc(sz)); 789} 790 791static void 792hash_free(void *p, size_t sz, void *arg) 793{ 794 795 free(p); 796} 797