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