Annotation of mandoc/mandoc-db.c, Revision 1.2
1.2 ! kristaps 1: /* $Id: mandoc-db.c,v 1.1 2011/04/02 15:40:40 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.2 ! kristaps 41: #define MANDOC_BUFSZ BUFSIZ
1.1 kristaps 42:
43: enum type {
44: MANDOC_NONE = 0,
45: MANDOC_NAME,
46: MANDOC_FUNCTION,
47: MANDOC_UTILITY,
48: MANDOC_INCLUDES,
49: MANDOC_VARIABLE
50: };
51:
52: #define MDOC_ARGS DB *db, \
53: const char *dbn, \
54: DBT *key, size_t *ksz, \
55: DBT *val, \
56: const struct mdoc_node *n
57:
58: static void dbt_append(DBT *, size_t *, const char *);
59: static void dbt_appendb(DBT *, size_t *,
60: const void *, size_t);
61: static void dbt_init(DBT *, size_t *);
62: static void version(void);
63: static void usage(void);
64: static void pmdoc(DB *, const char *,
65: DBT *, size_t *,
66: DBT *, size_t *,
67: const char *, struct mdoc *);
68: static void pmdoc_node(MDOC_ARGS);
69: static void pmdoc_Fd(MDOC_ARGS);
70: static void pmdoc_In(MDOC_ARGS);
71: static void pmdoc_Fn(MDOC_ARGS);
72: static void pmdoc_Fo(MDOC_ARGS);
73: static void pmdoc_Nm(MDOC_ARGS);
74: static void pmdoc_Vt(MDOC_ARGS);
75:
76: typedef void (*pmdoc_nf)(MDOC_ARGS);
77:
78: static const char *progname;
79:
80: static const pmdoc_nf mdocs[MDOC_MAX] = {
81: NULL, /* Ap */
82: NULL, /* Dd */
83: NULL, /* Dt */
84: NULL, /* Os */
85: NULL, /* Sh */
86: NULL, /* Ss */
87: NULL, /* Pp */
88: NULL, /* D1 */
89: NULL, /* Dl */
90: NULL, /* Bd */
91: NULL, /* Ed */
92: NULL, /* Bl */
93: NULL, /* El */
94: NULL, /* It */
95: NULL, /* Ad */
96: NULL, /* An */
97: NULL, /* Ar */
98: NULL, /* Cd */
99: NULL, /* Cm */
100: NULL, /* Dv */
101: NULL, /* Er */
102: NULL, /* Ev */
103: NULL, /* Ex */
104: NULL, /* Fa */
105: pmdoc_Fd, /* Fd */
106: NULL, /* Fl */
107: pmdoc_Fn, /* Fn */
108: NULL, /* Ft */
109: NULL, /* Ic */
110: pmdoc_In, /* In */
111: NULL, /* Li */
112: NULL, /* Nd */
113: pmdoc_Nm, /* Nm */
114: NULL, /* Op */
115: NULL, /* Ot */
116: NULL, /* Pa */
117: NULL, /* Rv */
118: NULL, /* St */
119: pmdoc_Vt, /* Va */
120: pmdoc_Vt, /* Vt */
121: NULL, /* Xr */
122: NULL, /* %A */
123: NULL, /* %B */
124: NULL, /* %D */
125: NULL, /* %I */
126: NULL, /* %J */
127: NULL, /* %N */
128: NULL, /* %O */
129: NULL, /* %P */
130: NULL, /* %R */
131: NULL, /* %T */
132: NULL, /* %V */
133: NULL, /* Ac */
134: NULL, /* Ao */
135: NULL, /* Aq */
136: NULL, /* At */
137: NULL, /* Bc */
138: NULL, /* Bf */
139: NULL, /* Bo */
140: NULL, /* Bq */
141: NULL, /* Bsx */
142: NULL, /* Bx */
143: NULL, /* Db */
144: NULL, /* Dc */
145: NULL, /* Do */
146: NULL, /* Dq */
147: NULL, /* Ec */
148: NULL, /* Ef */
149: NULL, /* Em */
150: NULL, /* Eo */
151: NULL, /* Fx */
152: NULL, /* Ms */
153: NULL, /* No */
154: NULL, /* Ns */
155: NULL, /* Nx */
156: NULL, /* Ox */
157: NULL, /* Pc */
158: NULL, /* Pf */
159: NULL, /* Po */
160: NULL, /* Pq */
161: NULL, /* Qc */
162: NULL, /* Ql */
163: NULL, /* Qo */
164: NULL, /* Qq */
165: NULL, /* Re */
166: NULL, /* Rs */
167: NULL, /* Sc */
168: NULL, /* So */
169: NULL, /* Sq */
170: NULL, /* Sm */
171: NULL, /* Sx */
172: NULL, /* Sy */
173: NULL, /* Tn */
174: NULL, /* Ux */
175: NULL, /* Xc */
176: NULL, /* Xo */
177: pmdoc_Fo, /* Fo */
178: NULL, /* Fc */
179: NULL, /* Oo */
180: NULL, /* Oc */
181: NULL, /* Bk */
182: NULL, /* Ek */
183: NULL, /* Bt */
184: NULL, /* Hf */
185: NULL, /* Fr */
186: NULL, /* Ud */
187: NULL, /* Lb */
188: NULL, /* Lp */
189: NULL, /* Lk */
190: NULL, /* Mt */
191: NULL, /* Brq */
192: NULL, /* Bro */
193: NULL, /* Brc */
194: NULL, /* %C */
195: NULL, /* Es */
196: NULL, /* En */
197: NULL, /* Dx */
198: NULL, /* %Q */
199: NULL, /* br */
200: NULL, /* sp */
201: NULL, /* %U */
202: NULL, /* Ta */
203: };
204:
205: int
206: main(int argc, char *argv[])
207: {
1.2 ! kristaps 208: struct mparse *mp; /* parse sequence */
! 209: struct mdoc *mdoc; /* resulting mdoc */
! 210: const char *fn,
! 211: *dir; /* result dir (default: cwd) */
! 212: char fbuf[MAXPATHLEN], /* btree fname */
! 213: fbbuf[MAXPATHLEN]; /* btree backup fname */
1.1 kristaps 214: int c;
1.2 ! kristaps 215: DB *db; /* open database */
! 216: DBT key, val; /* persistent entries */
! 217: size_t ksz, vsz; /* entry buffer sizes */
! 218: BTREEINFO info; /* btree configuration */
1.1 kristaps 219: extern int optind;
220: extern char *optarg;
221:
222: progname = strrchr(argv[0], '/');
223: if (progname == NULL)
224: progname = argv[0];
225: else
226: ++progname;
227:
1.2 ! kristaps 228: dir = "./";
! 229:
! 230: while (-1 != (c = getopt(argc, argv, "d:V")))
1.1 kristaps 231: switch (c) {
1.2 ! kristaps 232: case ('d'):
! 233: dir = optarg;
1.1 kristaps 234: break;
235: case ('V'):
236: version();
237: return((int)MANDOCLEVEL_OK);
238: default:
239: usage();
240: return((int)MANDOCLEVEL_BADARG);
241: }
242:
243: argc -= optind;
244: argv += optind;
245:
246: /*
247: * Set up a temporary file-name into which we're going to write
248: * all of our data. This is securely renamed to the real
249: * file-name after we've written all of our data.
250: */
251:
1.2 ! kristaps 252: fbuf[0] = fbuf[MAXPATHLEN - 2] =
! 253: fbbuf[0] = fbbuf[MAXPATHLEN - 1] = '\0';
! 254:
! 255: strlcat(fbuf, dir, MAXPATHLEN);
! 256: strlcat(fbuf, MANDOC_DB, MAXPATHLEN);
! 257: strlcat(fbbuf, fbuf, MAXPATHLEN);
! 258: strlcat(fbbuf, "~", MAXPATHLEN);
! 259:
! 260: if ('\0' != fbuf[MAXPATHLEN - 2] ||
! 261: '\0' != fbbuf[MAXPATHLEN - 2]) {
1.1 kristaps 262: fprintf(stderr, "%s: Bad filename\n", progname);
263: exit((int)MANDOCLEVEL_SYSERR);
264: }
265:
266: /*
267: * Open a BTREE database that allows duplicates. If the
268: * database already exists (it's a backup anyway), then blow it
269: * away with O_TRUNC.
270: */
271:
272: memset(&info, 0, sizeof(BTREEINFO));
273: info.flags = R_DUP;
274:
1.2 ! kristaps 275: db = dbopen(fbbuf, O_CREAT|O_TRUNC|O_RDWR,
1.1 kristaps 276: 0644, DB_BTREE, &info);
277:
278: if (NULL == db) {
1.2 ! kristaps 279: perror(fbbuf);
1.1 kristaps 280: exit((int)MANDOCLEVEL_SYSERR);
281: }
282:
283: /* Use the auto-parser and don't report any errors. */
284:
285: mp = mparse_alloc(MPARSE_AUTO, MANDOCLEVEL_FATAL, NULL, NULL);
286:
287: /*
288: * Try parsing the manuals given on the command line. If we
289: * totally fail, then just keep on going. Take resulting trees
290: * and push them down into the database code.
291: */
292:
293: memset(&key, 0, sizeof(DBT));
294: memset(&val, 0, sizeof(DBT));
295: ksz = vsz = 0;
296:
297: while (NULL != (fn = *argv++)) {
298: mparse_reset(mp);
299: if (mparse_readfd(mp, -1, fn) >= MANDOCLEVEL_FATAL)
300: continue;
1.2 ! kristaps 301: mparse_result(mp, &mdoc, NULL);
1.1 kristaps 302: if (mdoc)
1.2 ! kristaps 303: pmdoc(db, fbbuf, &key, &ksz,
1.1 kristaps 304: &val, &vsz, fn, mdoc);
305: }
306:
307: (*db->close)(db);
308: mparse_free(mp);
309:
310: free(key.data);
311: free(val.data);
312:
313: /* Atomically replace the file with our temporary one. */
314:
1.2 ! kristaps 315: if (-1 == rename(fbbuf, fbuf))
! 316: perror(fbuf);
1.1 kristaps 317:
318: return((int)MANDOCLEVEL_OK);
319: }
320:
321: /*
322: * Initialise the stored database key whose data buffer is shared
323: * between uses (as the key must sometimes be constructed from an array
324: * of
325: */
326: static void
327: dbt_init(DBT *key, size_t *ksz)
328: {
329:
330: if (0 == *ksz) {
331: assert(0 == key->size);
332: assert(NULL == key->data);
333: key->data = mandoc_malloc(MANDOC_BUFSZ);
334: *ksz = MANDOC_BUFSZ;
335: }
336:
337: key->size = 0;
338: }
339:
340: /*
341: * Append a binary value to a database entry. This can be invoked
342: * multiple times; the buffer is automatically resized.
343: */
344: static void
345: dbt_appendb(DBT *key, size_t *ksz, const void *cp, size_t sz)
346: {
347:
348: assert(key->data);
349:
350: /* Overshoot by MANDOC_BUFSZ. */
351:
352: while (key->size + sz >= *ksz) {
353: *ksz = key->size + sz + MANDOC_BUFSZ;
354: *ksz = *ksz + (4 - (*ksz % 4));
355: key->data = mandoc_realloc(key->data, *ksz);
356: }
357:
358: memcpy(key->data + (int)key->size, cp, sz);
359: key->size += sz;
360: }
361:
362: /*
363: * Append a nil-terminated string to the database entry. This can be
364: * invoked multiple times. The database entry will be nil-terminated as
365: * well; if invoked multiple times, a space is put between strings.
366: */
367: static void
368: dbt_append(DBT *key, size_t *ksz, const char *cp)
369: {
370: size_t sz;
371:
372: assert(key->data);
373: assert(key->size <= *ksz);
374:
375: if (0 == (sz = strlen(cp)))
376: return;
377:
378: /* Overshoot by MANDOC_BUFSZ (and nil terminator). */
379:
380: while (key->size + sz + 1 >= *ksz) {
381: *ksz = key->size + sz + 1 + MANDOC_BUFSZ;
382: *ksz = *ksz + (4 - (*ksz % 4));
383: key->data = mandoc_realloc(key->data, *ksz);
384: }
385:
386: /* Space-separate appended tokens. */
387:
388: if (key->size)
389: ((char *)key->data)[(int)key->size - 1] = ' ';
390:
391: memcpy(key->data + (int)key->size, cp, sz + 1);
392: key->size += sz + 1;
393: }
394:
395: /* ARGSUSED */
396: static void
397: pmdoc_Fd(MDOC_ARGS)
398: {
399: uint32_t fl;
400: const char *start, *end;
401: size_t sz;
402: char nil;
403:
404: if (SEC_SYNOPSIS != n->sec)
405: return;
406: if (NULL == (n = n->child) || MDOC_TEXT != n->type)
407: return;
408: if (strcmp("#include", n->string))
409: return;
410: if (NULL == (n = n->next) || MDOC_TEXT != n->type)
411: return;
412:
413: start = n->string;
414: if ('<' == *start)
415: start++;
416:
417: if (0 == (sz = strlen(start)))
418: return;
419:
420: end = &start[(int)sz - 1];
421: if ('>' == *end)
422: end--;
423:
424: nil = '\0';
425: dbt_appendb(key, ksz, start, end - start + 1);
426: dbt_appendb(key, ksz, &nil, 1);
427:
428: fl = MANDOC_INCLUDES;
429: memcpy(val->data, &fl, 4);
430: }
431:
432: /* ARGSUSED */
433: static void
434: pmdoc_In(MDOC_ARGS)
435: {
436: uint32_t fl;
437:
438: if (SEC_SYNOPSIS != n->sec)
439: return;
440: if (NULL == n->child || MDOC_TEXT != n->child->type)
441: return;
442:
443: dbt_append(key, ksz, n->child->string);
444: fl = MANDOC_INCLUDES;
445: memcpy(val->data, &fl, 4);
446: }
447:
448: /* ARGSUSED */
449: static void
450: pmdoc_Fn(MDOC_ARGS)
451: {
452: uint32_t fl;
453: const char *cp;
454:
455: if (SEC_SYNOPSIS != n->sec)
456: return;
457: if (NULL == n->child || MDOC_TEXT != n->child->type)
458: return;
459:
460: /* .Fn "struct type *arg" "foo" */
461:
462: cp = strrchr(n->child->string, ' ');
463: if (NULL == cp)
464: cp = n->child->string;
465:
466: /* Ignore pointers. */
467:
468: while ('*' == *cp)
469: cp++;
470:
471: dbt_append(key, ksz, cp);
472: fl = MANDOC_FUNCTION;
473: memcpy(val->data, &fl, 4);
474: }
475:
476: /* ARGSUSED */
477: static void
478: pmdoc_Vt(MDOC_ARGS)
479: {
480: uint32_t fl;
481: const char *start, *end;
482: size_t sz;
483: char nil;
484:
485: if (SEC_SYNOPSIS != n->sec)
486: return;
487: if (MDOC_Vt == n->tok && MDOC_BODY != n->type)
488: return;
489: if (NULL == n->child || MDOC_TEXT != n->child->type)
490: return;
491:
492: /*
493: * Strip away leading '*' and trailing ';'.
494: */
495:
496: start = n->last->string;
497:
498: while ('*' == *start)
499: start++;
500:
501: if (0 == (sz = strlen(start)))
502: return;
503:
504: end = &start[sz - 1];
505: while (end > start && ';' == *end)
506: end--;
507:
508: if (end == start)
509: return;
510:
511: nil = '\0';
512: dbt_appendb(key, ksz, start, end - start + 1);
513: dbt_appendb(key, ksz, &nil, 1);
514: fl = MANDOC_VARIABLE;
515: memcpy(val->data, &fl, 4);
516: }
517:
518: /* ARGSUSED */
519: static void
520: pmdoc_Fo(MDOC_ARGS)
521: {
522: uint32_t fl;
523:
524: if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type)
525: return;
526: if (NULL == n->child || MDOC_TEXT != n->child->type)
527: return;
528:
529: dbt_append(key, ksz, n->child->string);
530: fl = MANDOC_FUNCTION;
531: memcpy(val->data, &fl, 4);
532: }
533:
534: /* ARGSUSED */
535: static void
536: pmdoc_Nm(MDOC_ARGS)
537: {
538: uint32_t fl;
539:
540: if (SEC_NAME == n->sec) {
541: for (n = n->child; n; n = n->next) {
542: if (MDOC_TEXT != n->type)
543: continue;
544: dbt_append(key, ksz, n->string);
545: }
546: fl = MANDOC_NAME;
547: memcpy(val->data, &fl, 4);
548: return;
549: } else if (SEC_SYNOPSIS != n->sec || MDOC_HEAD != n->type)
550: return;
551:
552: for (n = n->child; n; n = n->next) {
553: if (MDOC_TEXT != n->type)
554: continue;
555: dbt_append(key, ksz, n->string);
556: }
557:
558: fl = MANDOC_UTILITY;
559: memcpy(val->data, &fl, 4);
560: }
561:
562: /*
563: * Call out to per-macro handlers after clearing the persistent database
564: * key. If the macro sets the database key, flush it to the database.
565: */
566: static void
567: pmdoc_node(MDOC_ARGS)
568: {
569:
570: if (NULL == n)
571: return;
572:
573: switch (n->type) {
574: case (MDOC_HEAD):
575: /* FALLTHROUGH */
576: case (MDOC_BODY):
577: /* FALLTHROUGH */
578: case (MDOC_TAIL):
579: /* FALLTHROUGH */
580: case (MDOC_BLOCK):
581: /* FALLTHROUGH */
582: case (MDOC_ELEM):
583: if (NULL == mdocs[n->tok])
584: break;
585:
586: dbt_init(key, ksz);
587: (*mdocs[n->tok])(db, dbn, key, ksz, val, n);
588:
589: if (0 == key->size)
590: break;
591: if (0 == (*db->put)(db, key, val, 0))
592: break;
593:
594: perror(dbn);
595: exit((int)MANDOCLEVEL_SYSERR);
596: /* NOTREACHED */
597: default:
598: break;
599: }
600:
601: pmdoc_node(db, dbn, key, ksz, val, n->child);
602: pmdoc_node(db, dbn, key, ksz, val, n->next);
603: }
604:
605: static void
606: pmdoc(DB *db, const char *dbn,
607: DBT *key, size_t *ksz,
608: DBT *val, size_t *valsz,
609: const char *path, struct mdoc *m)
610: {
611: uint32_t flag;
612:
613: flag = MANDOC_NONE;
614:
615: /*
616: * Database values are a 4-byte bit-field followed by the path
617: * of the manual. Allocate all the space we'll need now; we
618: * change the bit-field depending on the key type.
619: */
620:
621: dbt_init(val, valsz);
622: dbt_appendb(val, valsz, &flag, 4);
623: dbt_append(val, valsz, path);
624:
625: pmdoc_node(db, dbn, key, ksz, val, mdoc_node(m));
626: }
627:
628: static void
629: version(void)
630: {
631:
632: printf("%s %s\n", progname, VERSION);
633: }
634:
635: static void
636: usage(void)
637: {
638:
639: fprintf(stderr, "usage: %s "
640: "[-V] "
1.2 ! kristaps 641: "[-d path] "
1.1 kristaps 642: "[file...]\n",
643: progname);
644: }
CVSweb