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