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