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