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