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