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