Annotation of mandoc/main.c, Revision 1.19
1.19 ! kristaps 1: /* $Id: main.c,v 1.18 2009/03/31 13:50:19 kristaps Exp $ */
1.1 kristaps 2: /*
3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@openbsd.org>
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
7: * above copyright notice and this permission notice appear in all
8: * copies.
9: *
10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11: * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12: * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13: * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14: * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15: * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16: * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17: * PERFORMANCE OF THIS SOFTWARE.
18: */
19: #include <sys/stat.h>
20:
21: #include <assert.h>
22: #include <err.h>
23: #include <fcntl.h>
24: #include <stdio.h>
25: #include <stdlib.h>
26: #include <string.h>
27: #include <unistd.h>
28:
29: #include "mdoc.h"
1.10 kristaps 30: #include "man.h"
1.1 kristaps 31:
1.16 kristaps 32: /* Account for FreeBSD and Linux in our declarations. */
33:
1.3 kristaps 34: #ifdef __linux__
35: extern int getsubopt(char **, char * const *, char **);
36: # ifndef __dead
37: # define __dead __attribute__((__noreturn__))
38: # endif
1.13 kristaps 39: #elif defined(__dead2)
1.10 kristaps 40: # ifndef __dead
41: # define __dead __dead2
42: # endif
1.3 kristaps 43: #endif
44:
1.5 kristaps 45: struct buf {
46: char *buf;
47: size_t sz;
48: };
49:
1.19 ! kristaps 50: enum intt {
! 51: INTT_AUTO,
! 52: INTT_MDOC,
! 53: INTT_MAN
! 54: };
! 55:
! 56: enum outt {
! 57: OUTT_ASCII = 0,
! 58: OUTT_TREE,
! 59: OUTT_LINT
! 60: };
! 61:
1.5 kristaps 62: struct curparse {
63: const char *file;
1.19 ! kristaps 64: int fd;
1.5 kristaps 65: int wflags;
1.1 kristaps 66: #define WARN_WALL 0x03 /* All-warnings mask. */
67: #define WARN_WCOMPAT (1 << 0) /* Compatibility warnings. */
68: #define WARN_WSYNTAX (1 << 1) /* Syntax warnings. */
69: #define WARN_WERR (1 << 2) /* Warnings->errors. */
1.19 ! kristaps 70: int fflags;
! 71: enum intt inttype;
! 72: struct man *man;
! 73: struct mdoc *mdoc;
1.5 kristaps 74: };
1.1 kristaps 75:
1.16 kristaps 76: #define IGN_SCOPE (1 << 0) /* Ignore scope errors. */
77: #define IGN_ESCAPE (1 << 1) /* Ignore bad escapes. */
78: #define IGN_MACRO (1 << 2) /* Ignore unknown macros. */
1.15 kristaps 79:
1.11 kristaps 80: typedef int (*out_run)(void *, const struct man *,
81: const struct mdoc *);
1.1 kristaps 82: typedef void (*out_free)(void *);
83:
84: extern char *__progname;
85:
86: extern void *ascii_alloc(void);
1.11 kristaps 87: extern int terminal_run(void *, const struct man *,
88: const struct mdoc *);
89: extern int tree_run(void *, const struct man *,
90: const struct mdoc *);
1.1 kristaps 91: extern void terminal_free(void *);
92:
93: static int foptions(int *, char *);
94: static int toptions(enum outt *, char *);
1.10 kristaps 95: static int moptions(enum intt *, char *);
1.1 kristaps 96: static int woptions(int *, char *);
97: static int merr(void *, int, int, const char *);
1.14 kristaps 98: static int manwarn(void *, int, int, const char *);
99: static int mdocwarn(void *, int, int,
1.1 kristaps 100: enum mdoc_warn, const char *);
1.19 ! kristaps 101: static int fstdin(struct buf *, struct buf *,
! 102: struct curparse *);
! 103: static int ffile(struct buf *, struct buf *,
! 104: const char *, struct curparse *);
1.5 kristaps 105: static int fdesc(struct buf *, struct buf *,
1.19 ! kristaps 106: struct curparse *);
! 107: static int pset(const char *, size_t, struct curparse *,
! 108: struct man **, struct mdoc **);
! 109: static struct man *man_init(struct curparse *);
! 110: static struct mdoc *mdoc_init(struct curparse *);
1.10 kristaps 111: __dead static void version(void);
112: __dead static void usage(void);
1.1 kristaps 113:
114:
115: int
116: main(int argc, char *argv[])
117: {
1.19 ! kristaps 118: int c, rc;
1.1 kristaps 119: void *outdata;
120: enum outt outtype;
1.5 kristaps 121: struct buf ln, blk;
1.1 kristaps 122: out_run outrun;
123: out_free outfree;
1.5 kristaps 124: struct curparse curp;
1.1 kristaps 125:
126: outtype = OUTT_ASCII;
127:
1.5 kristaps 128: bzero(&curp, sizeof(struct curparse));
129:
1.19 ! kristaps 130: curp.inttype = INTT_AUTO;
! 131:
1.1 kristaps 132: /* LINTED */
1.10 kristaps 133: while (-1 != (c = getopt(argc, argv, "f:m:VW:T:")))
1.1 kristaps 134: switch (c) {
135: case ('f'):
1.19 ! kristaps 136: if ( ! foptions(&curp.fflags, optarg))
1.1 kristaps 137: return(0);
138: break;
1.10 kristaps 139: case ('m'):
1.19 ! kristaps 140: if ( ! moptions(&curp.inttype, optarg))
1.10 kristaps 141: return(0);
142: break;
1.1 kristaps 143: case ('T'):
144: if ( ! toptions(&outtype, optarg))
145: return(0);
146: break;
147: case ('W'):
1.5 kristaps 148: if ( ! woptions(&curp.wflags, optarg))
1.1 kristaps 149: return(0);
150: break;
151: case ('V'):
152: version();
153: /* NOTREACHED */
154: default:
155: usage();
156: /* NOTREACHED */
157: }
158:
159: argc -= optind;
160: argv += optind;
161:
162: /*
1.16 kristaps 163: * Allocate the appropriate front-end. Note that utf8, latin1
164: * (both not yet implemented) and ascii all resolve to the
165: * terminal front-end with different encodings (see terminal.c).
166: * Not all frontends have cleanup or alloc routines.
1.1 kristaps 167: */
168:
169: switch (outtype) {
170: case (OUTT_TREE):
171: outdata = NULL;
172: outrun = tree_run;
173: outfree = NULL;
174: break;
175: case (OUTT_LINT):
176: outdata = NULL;
177: outrun = NULL;
178: outfree = NULL;
179: break;
180: default:
181: outdata = ascii_alloc();
182: outrun = terminal_run;
183: outfree = terminal_free;
184: break;
185: }
186:
1.16 kristaps 187: /* Configure buffers. */
188:
1.5 kristaps 189: bzero(&ln, sizeof(struct buf));
190: bzero(&blk, sizeof(struct buf));
1.1 kristaps 191:
1.4 kristaps 192: /*
1.16 kristaps 193: * Main loop around available files.
1.4 kristaps 194: */
1.1 kristaps 195:
1.4 kristaps 196: if (NULL == *argv) {
197: rc = 0;
1.19 ! kristaps 198: c = fstdin(&blk, &ln, &curp);
1.10 kristaps 199:
1.4 kristaps 200: if (c && NULL == outrun)
201: rc = 1;
1.19 ! kristaps 202: /*else if (c && outrun && (*outrun)(outdata, curp.man, curp.mdoc))
! 203: rc = 1;*/
1.4 kristaps 204: } else {
205: while (*argv) {
1.19 ! kristaps 206: c = ffile(&blk, &ln, *argv, &curp);
1.4 kristaps 207: if ( ! c)
208: break;
1.19 ! kristaps 209: /*if (outrun && ! (*outrun)(outdata, curp.man, curp.mdoc))
! 210: break;*/
! 211: if (curp.man)
! 212: man_reset(curp.man);
! 213: if (curp.mdoc && ! mdoc_reset(curp.mdoc)) {
1.18 kristaps 214: warnx("memory exhausted");
215: break;
216: }
1.4 kristaps 217: argv++;
218: }
219: rc = NULL == *argv;
1.1 kristaps 220: }
221:
1.5 kristaps 222: if (blk.buf)
223: free(blk.buf);
224: if (ln.buf)
225: free(ln.buf);
1.1 kristaps 226: if (outfree)
227: (*outfree)(outdata);
1.19 ! kristaps 228: if (curp.mdoc)
! 229: mdoc_free(curp.mdoc);
! 230: if (curp.man)
! 231: man_free(curp.man);
1.1 kristaps 232:
233: return(rc ? EXIT_SUCCESS : EXIT_FAILURE);
234: }
235:
236:
237: __dead static void
238: version(void)
239: {
240:
241: (void)printf("%s %s\n", __progname, VERSION);
1.18 kristaps 242: exit(EXIT_SUCCESS);
1.1 kristaps 243: }
244:
245:
246: __dead static void
247: usage(void)
248: {
249:
1.12 kristaps 250: (void)fprintf(stderr, "usage: %s [-V] [-foption...] "
251: "[-mformat] [-Toutput] [-Werr...]\n",
252: __progname);
1.18 kristaps 253: exit(EXIT_FAILURE);
1.1 kristaps 254: }
255:
256:
1.19 ! kristaps 257: static struct man *
! 258: man_init(struct curparse *curp)
! 259: {
! 260: int pflags;
! 261: struct man *man;
! 262: struct man_cb mancb;
! 263:
! 264: mancb.man_err = merr;
! 265: mancb.man_warn = manwarn;
! 266:
! 267: pflags = 0;
! 268:
! 269: if (curp->fflags & IGN_MACRO)
! 270: pflags |= MAN_IGN_MACRO;
! 271:
! 272: if (NULL == (man = man_alloc(curp, pflags, &mancb)))
! 273: warnx("memory exhausted");
! 274:
! 275: return(man);
! 276: }
! 277:
! 278:
! 279: static struct mdoc *
! 280: mdoc_init(struct curparse *curp)
! 281: {
! 282: int pflags;
! 283: struct mdoc *mdoc;
! 284: struct mdoc_cb mdoccb;
! 285:
! 286: mdoccb.mdoc_msg = NULL;
! 287: mdoccb.mdoc_err = merr;
! 288: mdoccb.mdoc_warn = mdocwarn;
! 289:
! 290: pflags = 0;
! 291:
! 292: if (curp->fflags & IGN_SCOPE)
! 293: pflags |= MDOC_IGN_SCOPE;
! 294: if (curp->fflags & IGN_ESCAPE)
! 295: pflags |= MDOC_IGN_ESCAPE;
! 296: if (curp->fflags & IGN_MACRO)
! 297: pflags |= MDOC_IGN_MACRO;
! 298:
! 299: if (NULL == (mdoc = mdoc_alloc(curp, pflags, &mdoccb)))
! 300: warnx("memory allocated");
! 301:
! 302: return(mdoc);
! 303: }
! 304:
! 305:
! 306: static int
! 307: fstdin(struct buf *blk, struct buf *ln, struct curparse *curp)
! 308: {
! 309:
! 310: curp->file = "<stdin>";
! 311: curp->fd = STDIN_FILENO;
! 312: return(fdesc(blk, ln, curp));
! 313: }
! 314:
! 315:
1.1 kristaps 316: static int
1.19 ! kristaps 317: ffile(struct buf *blk, struct buf *ln,
! 318: const char *file, struct curparse *curp)
1.1 kristaps 319: {
1.19 ! kristaps 320: int c;
1.1 kristaps 321:
1.19 ! kristaps 322: curp->file = file;
! 323: if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
! 324: warn("%s", curp->file);
1.1 kristaps 325: return(0);
326: }
327:
1.19 ! kristaps 328: c = fdesc(blk, ln, curp);
1.1 kristaps 329:
1.19 ! kristaps 330: if (-1 == close(curp->fd))
! 331: warn("%s", curp->file);
1.1 kristaps 332:
333: return(c);
334: }
335:
336:
337: static int
1.19 ! kristaps 338: fdesc(struct buf *blk, struct buf *ln, struct curparse *curp)
1.1 kristaps 339: {
340: size_t sz;
341: ssize_t ssz;
342: struct stat st;
343: int j, i, pos, lnn;
1.19 ! kristaps 344: struct man *man;
! 345: struct mdoc *mdoc;
1.10 kristaps 346:
1.19 ! kristaps 347: sz = BUFSIZ;
! 348: man = NULL;
! 349: mdoc = NULL;
1.1 kristaps 350:
351: /*
1.19 ! kristaps 352: * Two buffers: ln and buf. buf is the input buffer optimised
! 353: * here for each file's block size. ln is a line buffer. Both
1.1 kristaps 354: * growable, hence passed in by ptr-ptr.
355: */
356:
1.19 ! kristaps 357: if (-1 == fstat(curp->fd, &st))
! 358: warnx("%s", curp->file);
1.6 kristaps 359: else if ((size_t)st.st_blksize > sz)
360: sz = st.st_blksize;
1.1 kristaps 361:
1.5 kristaps 362: if (sz > blk->sz) {
363: blk->buf = realloc(blk->buf, sz);
1.19 ! kristaps 364: if (NULL == blk->buf) {
! 365: warn("realloc");
! 366: return(0);
! 367: }
1.5 kristaps 368: blk->sz = sz;
1.1 kristaps 369: }
370:
1.19 ! kristaps 371: /* Fill buf with file blocksize. */
1.1 kristaps 372:
1.19 ! kristaps 373: for (lnn = 0, pos = 0; ; ) {
! 374: if (-1 == (ssz = read(curp->fd, blk->buf, sz))) {
! 375: warn("%s", curp->file);
1.1 kristaps 376: return(0);
377: } else if (0 == ssz)
378: break;
379:
1.19 ! kristaps 380: /* Parse the read block into partial or full lines. */
! 381:
1.1 kristaps 382: for (i = 0; i < (int)ssz; i++) {
1.5 kristaps 383: if (pos >= (int)ln->sz) {
384: ln->sz += 256; /* Step-size. */
385: ln->buf = realloc(ln->buf, ln->sz);
1.19 ! kristaps 386: if (NULL == ln->buf) {
! 387: warn("realloc");
! 388: return(0);
! 389: }
1.1 kristaps 390: }
391:
1.5 kristaps 392: if ('\n' != blk->buf[i]) {
393: ln->buf[pos++] = blk->buf[i];
1.1 kristaps 394: continue;
395: }
396:
397: /* Check for CPP-escaped newline. */
398:
1.5 kristaps 399: if (pos > 0 && '\\' == ln->buf[pos - 1]) {
1.1 kristaps 400: for (j = pos - 1; j >= 0; j--)
1.5 kristaps 401: if ('\\' != ln->buf[j])
1.1 kristaps 402: break;
403:
404: if ( ! ((pos - j) % 2)) {
405: pos--;
406: lnn++;
407: continue;
408: }
409: }
410:
1.5 kristaps 411: ln->buf[pos] = 0;
1.19 ! kristaps 412: lnn++;
! 413:
! 414: /*
! 415: * If no manual parser has been assigned, then
! 416: * try to assign one in pset(), which may do
! 417: * nothing at all. After this, parse the manual
! 418: * line accordingly.
! 419: */
! 420:
! 421: if ( ! (man || mdoc) && ! pset(ln->buf,
! 422: pos, curp, &man, &mdoc))
1.10 kristaps 423: return(0);
1.19 ! kristaps 424:
! 425: pos = 0;
! 426:
1.10 kristaps 427: if (man && ! man_parseln(man, lnn, ln->buf))
1.1 kristaps 428: return(0);
1.19 ! kristaps 429: if (mdoc && ! mdoc_parseln(mdoc, lnn, ln->buf))
! 430: return(0);
1.1 kristaps 431: }
432: }
433:
1.19 ! kristaps 434: /* Note that a parser may not have been assigned, yet. */
! 435:
1.10 kristaps 436: if (mdoc)
437: return(mdoc_endparse(mdoc));
1.19 ! kristaps 438: if (man)
! 439: return(man_endparse(man));
! 440:
! 441: warnx("%s: not a manual", curp->file);
! 442: return(0);
! 443: }
! 444:
! 445:
! 446: static int
! 447: pset(const char *buf, size_t pos, struct curparse *curp,
! 448: struct man **man, struct mdoc **mdoc)
! 449: {
! 450:
! 451: /*
! 452: * Try to intuit which kind of manual parser should be used. If
! 453: * passed in by command-line (-man, -mdoc), then use that
! 454: * explicitly. If passed as -mandoc, then try to guess from the
! 455: * line: either skip comments, use -mdoc when finding `.Dt', or
! 456: * default to -man, which is more lenient.
! 457: */
! 458:
! 459: if (pos >= 3 && 0 == memcmp(buf, ".\\\"", 3))
! 460: return(1);
1.10 kristaps 461:
1.19 ! kristaps 462: switch (curp->inttype) {
! 463: case (INTT_MDOC):
! 464: if (NULL == curp->mdoc)
! 465: curp->mdoc = mdoc_init(curp);
! 466: if (NULL == (*mdoc = curp->mdoc))
! 467: return(0);
! 468: warnx("inheriting -mdoc parser");
! 469: return(1);
! 470: case (INTT_MAN):
! 471: if (NULL == curp->man)
! 472: curp->man = man_init(curp);
! 473: if (NULL == (*man = curp->man))
! 474: return(0);
! 475: warnx("inheriting -man parser");
! 476: return(1);
! 477: default:
! 478: break;
! 479: }
! 480:
! 481: if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
! 482: if (NULL == curp->mdoc)
! 483: curp->mdoc = mdoc_init(curp);
! 484: if (NULL == (*mdoc = curp->mdoc))
! 485: return(0);
! 486: return(1);
! 487: }
! 488:
! 489: if (NULL == curp->man)
! 490: curp->man = man_init(curp);
! 491: if (NULL == (*man = curp->man))
! 492: return(0);
! 493: return(1);
1.10 kristaps 494: }
495:
496:
497: static int
498: moptions(enum intt *tflags, char *arg)
499: {
500:
1.17 kristaps 501: if (0 == strcmp(arg, "doc"))
1.10 kristaps 502: *tflags = INTT_MDOC;
1.19 ! kristaps 503: else if (0 == strcmp(arg, "andoc"))
! 504: *tflags = INTT_AUTO;
1.17 kristaps 505: else if (0 == strcmp(arg, "an"))
1.10 kristaps 506: *tflags = INTT_MAN;
507: else {
508: warnx("bad argument: -m%s", arg);
509: return(0);
510: }
511:
512: return(1);
1.1 kristaps 513: }
514:
515:
516: static int
517: toptions(enum outt *tflags, char *arg)
518: {
519:
520: if (0 == strcmp(arg, "ascii"))
521: *tflags = OUTT_ASCII;
522: else if (0 == strcmp(arg, "lint"))
523: *tflags = OUTT_LINT;
524: else if (0 == strcmp(arg, "tree"))
525: *tflags = OUTT_TREE;
526: else {
527: warnx("bad argument: -T%s", arg);
528: return(0);
529: }
530:
531: return(1);
532: }
533:
534:
535: /*
536: * Parse out the options for [-fopt...] setting compiler options. These
537: * can be comma-delimited or called again.
538: */
539: static int
540: foptions(int *fflags, char *arg)
541: {
542: char *v;
543: char *toks[4];
544:
545: toks[0] = "ign-scope";
546: toks[1] = "ign-escape";
547: toks[2] = "ign-macro";
548: toks[3] = NULL;
549:
550: while (*arg)
551: switch (getsubopt(&arg, toks, &v)) {
552: case (0):
1.15 kristaps 553: *fflags |= IGN_SCOPE;
1.1 kristaps 554: break;
555: case (1):
1.15 kristaps 556: *fflags |= IGN_ESCAPE;
1.1 kristaps 557: break;
558: case (2):
1.15 kristaps 559: *fflags |= IGN_MACRO;
1.1 kristaps 560: break;
561: default:
562: warnx("bad argument: -f%s", arg);
563: return(0);
564: }
565:
566: return(1);
567: }
568:
569:
570: /*
571: * Parse out the options for [-Werr...], which sets warning modes.
572: * These can be comma-delimited or called again.
573: */
574: static int
575: woptions(int *wflags, char *arg)
576: {
577: char *v;
578: char *toks[5];
579:
580: toks[0] = "all";
581: toks[1] = "compat";
582: toks[2] = "syntax";
583: toks[3] = "error";
584: toks[4] = NULL;
585:
586: while (*arg)
587: switch (getsubopt(&arg, toks, &v)) {
588: case (0):
589: *wflags |= WARN_WALL;
590: break;
591: case (1):
592: *wflags |= WARN_WCOMPAT;
593: break;
594: case (2):
595: *wflags |= WARN_WSYNTAX;
596: break;
597: case (3):
598: *wflags |= WARN_WERR;
599: break;
600: default:
601: warnx("bad argument: -W%s", arg);
602: return(0);
603: }
604:
605: return(1);
606: }
607:
608:
1.2 kristaps 609: /* ARGSUSED */
1.1 kristaps 610: static int
611: merr(void *arg, int line, int col, const char *msg)
612: {
1.5 kristaps 613: struct curparse *curp;
614:
615: curp = (struct curparse *)arg;
1.1 kristaps 616:
1.5 kristaps 617: warnx("%s:%d: error: %s (column %d)",
618: curp->file, line, msg, col);
1.1 kristaps 619: return(0);
620: }
621:
622:
623: static int
1.14 kristaps 624: mdocwarn(void *arg, int line, int col,
1.1 kristaps 625: enum mdoc_warn type, const char *msg)
626: {
1.5 kristaps 627: struct curparse *curp;
1.1 kristaps 628: char *wtype;
629:
1.5 kristaps 630: curp = (struct curparse *)arg;
1.1 kristaps 631: wtype = NULL;
632:
633: switch (type) {
634: case (WARN_COMPAT):
635: wtype = "compat";
1.5 kristaps 636: if (curp->wflags & WARN_WCOMPAT)
1.1 kristaps 637: break;
638: return(1);
639: case (WARN_SYNTAX):
640: wtype = "syntax";
1.5 kristaps 641: if (curp->wflags & WARN_WSYNTAX)
1.1 kristaps 642: break;
643: return(1);
644: }
645:
646: assert(wtype);
1.5 kristaps 647: warnx("%s:%d: %s warning: %s (column %d)",
648: curp->file, line, wtype, msg, col);
1.1 kristaps 649:
1.5 kristaps 650: if ( ! (curp->wflags & WARN_WERR))
1.1 kristaps 651: return(1);
652:
653: warnx("%s: considering warnings as errors",
654: __progname);
655: return(0);
656: }
657:
658:
1.14 kristaps 659: static int
660: manwarn(void *arg, int line, int col, const char *msg)
661: {
662: struct curparse *curp;
663:
664: curp = (struct curparse *)arg;
665:
666: if ( ! (curp->wflags & WARN_WSYNTAX))
667: return(1);
668:
669: warnx("%s:%d: syntax warning: %s (column %d)",
670: curp->file, line, msg, col);
671:
672: if ( ! (curp->wflags & WARN_WERR))
673: return(1);
674:
675: warnx("%s: considering warnings as errors",
676: __progname);
677: return(0);
678: }
CVSweb