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