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