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