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