]> git.cameronkatri.com Git - mandoc.git/blob - mandoc-db.c
Have mandoc-db grok `Dt'/`TH' manual title as well.
[mandoc.git] / mandoc-db.c
1 /* $Id: mandoc-db.c,v 1.16 2011/05/03 10:08:09 kristaps Exp $ */
2 /*
3 * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17 #ifdef HAVE_CONFIG_H
18 #include "config.h"
19 #endif
20
21 #include <sys/param.h>
22
23 #include <assert.h>
24 #ifdef __linux__
25 # include <db_185.h>
26 #else
27 # include <db.h>
28 #endif
29 #include <fcntl.h>
30 #include <getopt.h>
31 #include <stdio.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "man.h"
37 #include "mdoc.h"
38 #include "mandoc.h"
39
40 #define MANDOC_DB "mandoc.db"
41 #define MANDOC_IDX "mandoc.index"
42 #define MANDOC_BUFSZ BUFSIZ
43 #define MANDOC_FLAGS O_CREAT|O_TRUNC|O_RDWR
44
45 enum type {
46 MANDOC_NONE = 0,
47 MANDOC_NAME,
48 MANDOC_FUNCTION,
49 MANDOC_UTILITY,
50 MANDOC_INCLUDES,
51 MANDOC_VARIABLE,
52 MANDOC_STANDARD
53 };
54
55 #define MAN_ARGS DB *db, \
56 const char *dbn, \
57 DBT *key, size_t *ksz, \
58 DBT *val, \
59 DBT *rval, size_t *rsz, \
60 const struct man_node *n
61 #define MDOC_ARGS DB *db, \
62 const char *dbn, \
63 DBT *key, size_t *ksz, \
64 DBT *val, \
65 DBT *rval, size_t *rsz, \
66 const struct mdoc_node *n
67
68 static void dbt_append(DBT *, size_t *, const char *);
69 static void dbt_appendb(DBT *, size_t *,
70 const void *, size_t);
71 static void dbt_init(DBT *, size_t *);
72 static void dbt_put(DB *, const char *, DBT *, DBT *);
73 static void usage(void);
74 static void pman(DB *, const char *, DBT *, size_t *,
75 DBT *, DBT *, size_t *, struct man *);
76 static int pman_node(MAN_ARGS);
77 static void pmdoc(DB *, const char *, DBT *, size_t *,
78 DBT *, DBT *, size_t *, struct mdoc *);
79 static void pmdoc_node(MDOC_ARGS);
80 static void pmdoc_Fd(MDOC_ARGS);
81 static void pmdoc_In(MDOC_ARGS);
82 static void pmdoc_Fn(MDOC_ARGS);
83 static void pmdoc_Fo(MDOC_ARGS);
84 static void pmdoc_Nd(MDOC_ARGS);
85 static void pmdoc_Nm(MDOC_ARGS);
86 static void pmdoc_St(MDOC_ARGS);
87 static void pmdoc_Vt(MDOC_ARGS);
88
89 typedef void (*pmdoc_nf)(MDOC_ARGS);
90
91 static const char *progname;
92
93 static const pmdoc_nf mdocs[MDOC_MAX] = {
94 NULL, /* Ap */
95 NULL, /* Dd */
96 NULL, /* Dt */
97 NULL, /* Os */
98 NULL, /* Sh */
99 NULL, /* Ss */
100 NULL, /* Pp */
101 NULL, /* D1 */
102 NULL, /* Dl */
103 NULL, /* Bd */
104 NULL, /* Ed */
105 NULL, /* Bl */
106 NULL, /* El */
107 NULL, /* It */
108 NULL, /* Ad */
109 NULL, /* An */
110 NULL, /* Ar */
111 NULL, /* Cd */
112 NULL, /* Cm */
113 NULL, /* Dv */
114 NULL, /* Er */
115 NULL, /* Ev */
116 NULL, /* Ex */
117 NULL, /* Fa */
118 pmdoc_Fd, /* Fd */
119 NULL, /* Fl */
120 pmdoc_Fn, /* Fn */
121 NULL, /* Ft */
122 NULL, /* Ic */
123 pmdoc_In, /* In */
124 NULL, /* Li */
125 pmdoc_Nd, /* Nd */
126 pmdoc_Nm, /* Nm */
127 NULL, /* Op */
128 NULL, /* Ot */
129 NULL, /* Pa */
130 NULL, /* Rv */
131 pmdoc_St, /* St */
132 pmdoc_Vt, /* Va */
133 pmdoc_Vt, /* Vt */
134 NULL, /* Xr */
135 NULL, /* %A */
136 NULL, /* %B */
137 NULL, /* %D */
138 NULL, /* %I */
139 NULL, /* %J */
140 NULL, /* %N */
141 NULL, /* %O */
142 NULL, /* %P */
143 NULL, /* %R */
144 NULL, /* %T */
145 NULL, /* %V */
146 NULL, /* Ac */
147 NULL, /* Ao */
148 NULL, /* Aq */
149 NULL, /* At */
150 NULL, /* Bc */
151 NULL, /* Bf */
152 NULL, /* Bo */
153 NULL, /* Bq */
154 NULL, /* Bsx */
155 NULL, /* Bx */
156 NULL, /* Db */
157 NULL, /* Dc */
158 NULL, /* Do */
159 NULL, /* Dq */
160 NULL, /* Ec */
161 NULL, /* Ef */
162 NULL, /* Em */
163 NULL, /* Eo */
164 NULL, /* Fx */
165 NULL, /* Ms */
166 NULL, /* No */
167 NULL, /* Ns */
168 NULL, /* Nx */
169 NULL, /* Ox */
170 NULL, /* Pc */
171 NULL, /* Pf */
172 NULL, /* Po */
173 NULL, /* Pq */
174 NULL, /* Qc */
175 NULL, /* Ql */
176 NULL, /* Qo */
177 NULL, /* Qq */
178 NULL, /* Re */
179 NULL, /* Rs */
180 NULL, /* Sc */
181 NULL, /* So */
182 NULL, /* Sq */
183 NULL, /* Sm */
184 NULL, /* Sx */
185 NULL, /* Sy */
186 NULL, /* Tn */
187 NULL, /* Ux */
188 NULL, /* Xc */
189 NULL, /* Xo */
190 pmdoc_Fo, /* Fo */
191 NULL, /* Fc */
192 NULL, /* Oo */
193 NULL, /* Oc */
194 NULL, /* Bk */
195 NULL, /* Ek */
196 NULL, /* Bt */
197 NULL, /* Hf */
198 NULL, /* Fr */
199 NULL, /* Ud */
200 NULL, /* Lb */
201 NULL, /* Lp */
202 NULL, /* Lk */
203 NULL, /* Mt */
204 NULL, /* Brq */
205 NULL, /* Bro */
206 NULL, /* Brc */
207 NULL, /* %C */
208 NULL, /* Es */
209 NULL, /* En */
210 NULL, /* Dx */
211 NULL, /* %Q */
212 NULL, /* br */
213 NULL, /* sp */
214 NULL, /* %U */
215 NULL, /* Ta */
216 };
217
218 int
219 main(int argc, char *argv[])
220 {
221 struct mparse *mp; /* parse sequence */
222 struct mdoc *mdoc; /* resulting mdoc */
223 struct man *man; /* resulting man */
224 char *fn; /* current file being parsed */
225 const char *msec, /* manual section */
226 *mtitle, /* manual title */
227 *dir; /* result dir (default: cwd) */
228 char ibuf[MAXPATHLEN], /* index fname */
229 ibbuf[MAXPATHLEN], /* index backup fname */
230 fbuf[MAXPATHLEN], /* btree fname */
231 fbbuf[MAXPATHLEN]; /* btree backup fname */
232 int ch;
233 DB *idx, /* index database */
234 *db; /* keyword database */
235 DBT rkey, rval, /* recno entries */
236 key, val; /* persistent keyword entries */
237 size_t sv,
238 ksz, rsz; /* entry buffer size */
239 char vbuf[8]; /* stringified record number */
240 BTREEINFO info; /* btree configuration */
241 recno_t rec; /* current record number */
242 extern int optind;
243 extern char *optarg;
244
245 progname = strrchr(argv[0], '/');
246 if (progname == NULL)
247 progname = argv[0];
248 else
249 ++progname;
250
251 dir = "";
252
253 while (-1 != (ch = getopt(argc, argv, "d:")))
254 switch (ch) {
255 case ('d'):
256 dir = optarg;
257 break;
258 default:
259 usage();
260 return((int)MANDOCLEVEL_BADARG);
261 }
262
263 argc -= optind;
264 argv += optind;
265
266 /*
267 * Set up temporary file-names into which we're going to write
268 * all of our data (both for the index and database). These
269 * will be securely renamed to the real file-names after we've
270 * written all of our data.
271 */
272
273 ibuf[0] = ibuf[MAXPATHLEN - 2] =
274 ibbuf[0] = ibbuf[MAXPATHLEN - 2] =
275 fbuf[0] = fbuf[MAXPATHLEN - 2] =
276 fbbuf[0] = fbbuf[MAXPATHLEN - 2] = '\0';
277
278 strlcat(fbuf, dir, MAXPATHLEN);
279 strlcat(fbuf, MANDOC_DB, MAXPATHLEN);
280
281 strlcat(fbbuf, fbuf, MAXPATHLEN);
282 strlcat(fbbuf, "~", MAXPATHLEN);
283
284 strlcat(ibuf, dir, MAXPATHLEN);
285 strlcat(ibuf, MANDOC_IDX, MAXPATHLEN);
286
287 strlcat(ibbuf, ibuf, MAXPATHLEN);
288 strlcat(ibbuf, "~", MAXPATHLEN);
289
290 if ('\0' != fbuf[MAXPATHLEN - 2] ||
291 '\0' != fbbuf[MAXPATHLEN - 2] ||
292 '\0' != ibuf[MAXPATHLEN - 2] ||
293 '\0' != ibbuf[MAXPATHLEN - 2]) {
294 fprintf(stderr, "%s: Path too long\n", progname);
295 exit((int)MANDOCLEVEL_SYSERR);
296 }
297
298 /*
299 * For the keyword database, open a BTREE database that allows
300 * duplicates. For the index database, use a standard RECNO
301 * database type.
302 */
303
304 memset(&info, 0, sizeof(BTREEINFO));
305 info.flags = R_DUP;
306 db = dbopen(fbbuf, MANDOC_FLAGS, 0644, DB_BTREE, &info);
307
308 if (NULL == db) {
309 perror(fbbuf);
310 exit((int)MANDOCLEVEL_SYSERR);
311 }
312
313 idx = dbopen(ibbuf, MANDOC_FLAGS, 0644, DB_RECNO, NULL);
314
315 if (NULL == db) {
316 perror(ibbuf);
317 (*db->close)(db);
318 exit((int)MANDOCLEVEL_SYSERR);
319 }
320
321 /*
322 * Try parsing the manuals given on the command line. If we
323 * totally fail, then just keep on going. Take resulting trees
324 * and push them down into the database code.
325 * Use the auto-parser and don't report any errors.
326 */
327
328 mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
329
330 memset(&key, 0, sizeof(DBT));
331 memset(&val, 0, sizeof(DBT));
332 memset(&rkey, 0, sizeof(DBT));
333 memset(&rval, 0, sizeof(DBT));
334
335 val.size = sizeof(vbuf);
336 val.data = vbuf;
337 rkey.size = sizeof(recno_t);
338
339 rec = 1;
340 ksz = rsz = 0;
341
342 while (NULL != (fn = *argv++)) {
343 mparse_reset(mp);
344
345 /* Parse and get (non-empty) AST. */
346
347 if (mparse_readfd(mp, -1, fn) >= MANDOCLEVEL_FATAL) {
348 fprintf(stderr, "%s: Parse failure\n", fn);
349 continue;
350 }
351 mparse_result(mp, &mdoc, &man);
352 if (NULL == mdoc && NULL == man)
353 continue;
354
355 /* Manual section: can be empty string. */
356
357 msec = NULL != mdoc ?
358 mdoc_meta(mdoc)->msec :
359 man_meta(man)->msec;
360 mtitle = NULL != mdoc ?
361 mdoc_meta(mdoc)->title :
362 man_meta(man)->title;
363
364 assert(msec);
365 assert(mtitle);
366
367 /*
368 * The index record value consists of a nil-terminated
369 * filename, a nil-terminated manual section, and a
370 * nil-terminated description. Since the description
371 * may not be set, we set a sentinel to see if we're
372 * going to write a nil byte in its place.
373 */
374
375 dbt_init(&rval, &rsz);
376 dbt_appendb(&rval, &rsz, fn, strlen(fn) + 1);
377 dbt_appendb(&rval, &rsz, msec, strlen(msec) + 1);
378 dbt_appendb(&rval, &rsz, mtitle, strlen(mtitle) + 1);
379 sv = rval.size;
380
381 /* Fix the record number in the btree value. */
382
383 memset(val.data, 0, sizeof(uint32_t));
384 memcpy(val.data + 4, &rec, sizeof(uint32_t));
385
386 if (mdoc)
387 pmdoc(db, fbbuf, &key, &ksz,
388 &val, &rval, &rsz, mdoc);
389 else
390 pman(db, fbbuf, &key, &ksz,
391 &val, &rval, &rsz, man);
392
393 /*
394 * Apply this to the index. If we haven't had a
395 * description set, put an empty one in now.
396 */
397
398 if (rval.size == sv)
399 dbt_appendb(&rval, &rsz, "", 1);
400
401 rkey.data = &rec;
402 dbt_put(idx, ibbuf, &rkey, &rval);
403
404 printf("Indexed: %s\n", fn);
405 rec++;
406 }
407
408 (*db->close)(db);
409 (*idx->close)(idx);
410
411 mparse_free(mp);
412
413 free(key.data);
414 free(rval.data);
415
416 /* Atomically replace the file with our temporary one. */
417
418 if (-1 == rename(fbbuf, fbuf))
419 perror(fbuf);
420 if (-1 == rename(ibbuf, ibuf))
421 perror(fbuf);
422
423 return((int)MANDOCLEVEL_OK);
424 }
425
426 /*
427 * Initialise the stored database key whose data buffer is shared
428 * between uses (as the key must sometimes be constructed from an array
429 * of
430 */
431 static void
432 dbt_init(DBT *key, size_t *ksz)
433 {
434
435 if (0 == *ksz) {
436 assert(0 == key->size);
437 assert(NULL == key->data);
438 key->data = mandoc_malloc(MANDOC_BUFSZ);
439 *ksz = MANDOC_BUFSZ;
440 }
441
442 key->size = 0;
443 }
444
445 /*
446 * Append a binary value to a database entry. This can be invoked
447 * multiple times; the buffer is automatically resized.
448 */
449 static void
450 dbt_appendb(DBT *key, size_t *ksz, const void *cp, size_t sz)
451 {
452
453 assert(key->data);
454
455 /* Overshoot by MANDOC_BUFSZ. */
456
457 while (key->size + sz >= *ksz) {
458 *ksz = key->size + sz + MANDOC_BUFSZ;
459 key->data = mandoc_realloc(key->data, *ksz);
460 }
461
462 #if 0
463 dstp = key->data + (int)key->size;
464
465 while (NULL != (endp = memchr(cp, '\\', sz))) {
466 ssz = endp - cp;
467 memcpy(dstp, cp, ssz);
468
469 dstp += ssz;
470 key->size += ssz;
471 sz -= ssz;
472
473 cp = endp++;
474 /* FIXME: expects nil-terminated string! */
475 esc = mandoc_escape((const char **)&endp, NULL, NULL);
476
477 switch (esc) {
478 case (ESCAPE_ERROR):
479 /* Nil-terminate this point. */
480 memcpy(dstp, "", 1);
481 key->size++;
482 return;
483 case (ESCAPE_PREDEF):
484 /* FALLTHROUGH */
485 case (ESCAPE_SPECIAL):
486 break;
487 default:
488 sz -= endp - cp;
489 cp = endp;
490 continue;
491 }
492
493 ssz = endp - cp;
494 memcpy(dstp, cp, ssz);
495
496 dstp += ssz;
497 key->size += ssz;
498 sz -= ssz;
499
500 cp = endp;
501 }
502 #endif
503
504 memcpy(key->data + (int)key->size, cp, sz);
505 key->size += sz;
506 }
507
508 /*
509 * Append a nil-terminated string to the database entry. This can be
510 * invoked multiple times. The database entry will be nil-terminated as
511 * well; if invoked multiple times, a space is put between strings.
512 */
513 static void
514 dbt_append(DBT *key, size_t *ksz, const char *cp)
515 {
516 size_t sz;
517
518 if (0 == (sz = strlen(cp)))
519 return;
520
521 assert(key->data);
522
523 if (key->size)
524 ((char *)key->data)[(int)key->size - 1] = ' ';
525
526 dbt_appendb(key, ksz, cp, sz + 1);
527 }
528
529 /* ARGSUSED */
530 static void
531 pmdoc_Fd(MDOC_ARGS)
532 {
533 uint32_t fl;
534 const char *start, *end;
535 size_t sz;
536
537 if (SEC_SYNOPSIS != n->sec)
538 return;
539 if (NULL == (n = n->child) || MDOC_TEXT != n->type)
540 return;
541
542 /*
543 * Only consider those `Fd' macro fields that begin with an
544 * "inclusion" token (versus, e.g., #define).
545 */
546 if (strcmp("#include", n->string))
547 return;
548
549 if (NULL == (n = n->next) || MDOC_TEXT != n->type)
550 return;
551
552 /*
553 * Strip away the enclosing angle brackets and make sure we're
554 * not zero-length.
555 */
556
557 start = n->string;
558 if ('<' == *start || '"' == *start)
559 start++;
560
561 if (0 == (sz = strlen(start)))
562 return;
563
564 end = &start[(int)sz - 1];
565 if ('>' == *end || '"' == *end)
566 end--;
567
568 dbt_appendb(key, ksz, start, end - start + 1);
569 dbt_appendb(key, ksz, "", 1);
570
571 fl = MANDOC_INCLUDES;
572 memcpy(val->data, &fl, 4);
573 }
574
575 /* ARGSUSED */
576 static void
577 pmdoc_In(MDOC_ARGS)
578 {
579 uint32_t fl;
580
581 if (SEC_SYNOPSIS != n->sec)
582 return;
583 if (NULL == n->child || MDOC_TEXT != n->child->type)
584 return;
585
586 dbt_append(key, ksz, n->child->string);
587 fl = MANDOC_INCLUDES;
588 memcpy(val->data, &fl, 4);
589 }
590
591 /* ARGSUSED */
592 static void
593 pmdoc_Fn(MDOC_ARGS)
594 {
595 uint32_t fl;
596 const char *cp;
597
598 if (SEC_SYNOPSIS != n->sec)
599 return;
600 if (NULL == n->child || MDOC_TEXT != n->child->type)
601 return;
602
603 /* .Fn "struct type *arg" "foo" */
604
605 cp = strrchr(n->child->string, ' ');
606 if (NULL == cp)
607 cp = n->child->string;
608
609 /* Strip away pointer symbol. */
610
611 while ('*' == *cp)
612 cp++;
613
614 dbt_append(key, ksz, cp);
615 fl = MANDOC_FUNCTION;
616 memcpy(val->data, &fl, 4);
617 }
618
619 /* ARGSUSED */
620 static void
621 pmdoc_St(MDOC_ARGS)
622 {
623 uint32_t fl;
624
625 if (SEC_STANDARDS != n->sec)
626 return;
627 if (NULL == n->child || MDOC_TEXT != n->child->type)
628 return;
629
630 dbt_append(key, ksz, n->child->string);
631 fl = MANDOC_STANDARD;
632 memcpy(val->data, &fl, 4);
633 }
634
635 /* ARGSUSED */
636 static void
637 pmdoc_Vt(MDOC_ARGS)
638 {
639 uint32_t fl;
640 const char *start;
641 size_t sz;
642
643 if (SEC_SYNOPSIS != n->sec)
644 return;
645 if (MDOC_Vt == n->tok && MDOC_BODY != n->type)
646 return;
647 if (NULL == n->last || MDOC_TEXT != n->last->type)
648 return;
649
650 /*
651 * Strip away leading pointer symbol '*' and trailing ';'.
652 */
653
654 start = n->last->string;
655
656 while ('*' == *start)
657 start++;
658
659 if (0 == (sz = strlen(start)))
660 return;
661
662 if (';' == start[sz - 1])
663 sz--;
664
665 if (0 == sz)
666 return;
667
668 dbt_appendb(key, ksz, start, sz);
669 dbt_appendb(key, ksz, "", 1);
670
671 fl = MANDOC_VARIABLE;
672 memcpy(val->data, &fl, 4);
673 }
674
675 /* ARGSUSED */
676 static void
677 pmdoc_Fo(MDOC_ARGS)
678 {
679 uint32_t fl;
680
681 if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type)
682 return;
683 if (NULL == n->child || MDOC_TEXT != n->child->type)
684 return;
685
686 dbt_append(key, ksz, n->child->string);
687 fl = MANDOC_FUNCTION;
688 memcpy(val->data, &fl, 4);
689 }
690
691
692 /* ARGSUSED */
693 static void
694 pmdoc_Nd(MDOC_ARGS)
695 {
696 int first;
697
698 for (first = 1, n = n->child; n; n = n->next) {
699 if (MDOC_TEXT != n->type)
700 continue;
701 if (first)
702 dbt_appendb(rval, rsz, n->string, strlen(n->string) + 1);
703 else
704 dbt_append(rval, rsz, n->string);
705 first = 0;
706 }
707 }
708
709 /* ARGSUSED */
710 static void
711 pmdoc_Nm(MDOC_ARGS)
712 {
713 uint32_t fl;
714
715 if (SEC_NAME == n->sec) {
716 for (n = n->child; n; n = n->next) {
717 if (MDOC_TEXT != n->type)
718 continue;
719 dbt_append(key, ksz, n->string);
720 }
721 fl = MANDOC_NAME;
722 memcpy(val->data, &fl, 4);
723 return;
724 } else if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type)
725 return;
726
727 for (n = n->child; n; n = n->next) {
728 if (MDOC_TEXT != n->type)
729 continue;
730 dbt_append(key, ksz, n->string);
731 }
732
733 fl = MANDOC_UTILITY;
734 memcpy(val->data, &fl, 4);
735 }
736
737 static void
738 dbt_put(DB *db, const char *dbn, DBT *key, DBT *val)
739 {
740
741 if (0 == key->size)
742 return;
743
744 assert(key->data);
745 assert(val->size);
746 assert(val->data);
747
748 if (0 == (*db->put)(db, key, val, 0))
749 return;
750
751 perror(dbn);
752 exit((int)MANDOCLEVEL_SYSERR);
753 /* NOTREACHED */
754 }
755
756 /*
757 * Call out to per-macro handlers after clearing the persistent database
758 * key. If the macro sets the database key, flush it to the database.
759 */
760 static void
761 pmdoc_node(MDOC_ARGS)
762 {
763
764 if (NULL == n)
765 return;
766
767 switch (n->type) {
768 case (MDOC_HEAD):
769 /* FALLTHROUGH */
770 case (MDOC_BODY):
771 /* FALLTHROUGH */
772 case (MDOC_TAIL):
773 /* FALLTHROUGH */
774 case (MDOC_BLOCK):
775 /* FALLTHROUGH */
776 case (MDOC_ELEM):
777 if (NULL == mdocs[n->tok])
778 break;
779
780 dbt_init(key, ksz);
781
782 (*mdocs[n->tok])(db, dbn, key, ksz, val, rval, rsz, n);
783 dbt_put(db, dbn, key, val);
784 break;
785 default:
786 break;
787 }
788
789 pmdoc_node(db, dbn, key, ksz, val, rval, rsz, n->child);
790 pmdoc_node(db, dbn, key, ksz, val, rval, rsz, n->next);
791 }
792
793 static int
794 pman_node(MAN_ARGS)
795 {
796 const struct man_node *head, *body;
797 const char *start, *sv;
798 size_t sz;
799 uint32_t fl;
800
801 if (NULL == n)
802 return(0);
803
804 /*
805 * We're only searching for one thing: the first text child in
806 * the BODY of a NAME section. Since we don't keep track of
807 * sections in -man, run some hoops to find out whether we're in
808 * the correct section or not.
809 */
810
811 if (MAN_BODY == n->type && MAN_SH == n->tok) {
812 body = n;
813 assert(body->parent);
814 if (NULL != (head = body->parent->head) &&
815 1 == head->nchild &&
816 NULL != (head = (head->child)) &&
817 MAN_TEXT == head->type &&
818 0 == strcmp(head->string, "NAME") &&
819 NULL != (body = body->child) &&
820 MAN_TEXT == body->type) {
821
822 fl = MANDOC_NAME;
823 memcpy(val->data, &fl, 4);
824
825 assert(body->string);
826 start = sv = body->string;
827
828 /*
829 * Go through a special heuristic dance here.
830 * This is why -man manuals are great!
831 * Conventionally, one or more manual names are
832 * comma-specified prior to a whitespace, then a
833 * dash, then a description. Try to puzzle out
834 * the name parts here.
835 */
836
837 for ( ;; ) {
838 sz = strcspn(start, " ,");
839 if ('\0' == start[(int)sz])
840 break;
841
842 dbt_init(key, ksz);
843 dbt_appendb(key, ksz, start, sz);
844 dbt_appendb(key, ksz, "", 1);
845
846 dbt_put(db, dbn, key, val);
847
848 if (' ' == start[(int)sz]) {
849 start += (int)sz + 1;
850 break;
851 }
852
853 assert(',' == start[(int)sz]);
854 start += (int)sz + 1;
855 while (' ' == *start)
856 start++;
857 }
858
859 if (sv == start) {
860 dbt_init(key, ksz);
861 dbt_append(key, ksz, start);
862 return(1);
863 }
864
865 while (' ' == *start)
866 start++;
867
868 if ('\\' == *start && '-' == *(start + 1))
869 start += 2;
870 else if ('-' == *start)
871 start++;
872
873 while (' ' == *start)
874 start++;
875
876 dbt_appendb(rval, rsz, start, strlen(start) + 1);
877 }
878 }
879
880 if (pman_node(db, dbn, key, ksz, val, rval, rsz, n->child))
881 return(1);
882 if (pman_node(db, dbn, key, ksz, val, rval, rsz, n->next))
883 return(1);
884
885 return(0);
886 }
887
888 static void
889 pman(DB *db, const char *dbn, DBT *key, size_t *ksz,
890 DBT *val, DBT *rval, size_t *rsz, struct man *m)
891 {
892
893 pman_node(db, dbn, key, ksz, val, rval, rsz, man_node(m));
894 }
895
896
897 static void
898 pmdoc(DB *db, const char *dbn, DBT *key, size_t *ksz,
899 DBT *val, DBT *rval, size_t *rsz, struct mdoc *m)
900 {
901
902 pmdoc_node(db, dbn, key, ksz, val, rval, rsz, mdoc_node(m));
903 }
904
905 static void
906 usage(void)
907 {
908
909 fprintf(stderr, "usage: %s "
910 "[-d path] "
911 "[file...]\n",
912 progname);
913 }