Annotation of src/usr.bin/mandoc/main.c, Revision 1.41
1.41 ! schwarze 1: /* $Id: main.c,v 1.40 2010/06/30 20:29:44 schwarze Exp $ */
1.1 kristaps 2: /*
1.37 schwarze 3: * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps@bsd.lv>
1.1 kristaps 4: *
5: * Permission to use, copy, modify, and distribute this software for any
1.2 schwarze 6: * purpose with or without fee is hereby granted, provided that the above
7: * copyright notice and this permission notice appear in all copies.
1.1 kristaps 8: *
1.2 schwarze 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.
1.1 kristaps 16: */
1.27 schwarze 17: #include <sys/types.h>
18: #include <sys/mman.h>
1.1 kristaps 19: #include <sys/stat.h>
20:
21: #include <assert.h>
22: #include <fcntl.h>
23: #include <stdio.h>
1.17 schwarze 24: #include <stdint.h>
1.1 kristaps 25: #include <stdlib.h>
26: #include <string.h>
27: #include <unistd.h>
28:
1.30 schwarze 29: #include "mandoc.h"
1.38 schwarze 30: #include "regs.h"
31: #include "main.h"
1.1 kristaps 32: #include "mdoc.h"
33: #include "man.h"
1.30 schwarze 34: #include "roff.h"
1.17 schwarze 35:
36: #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
1.1 kristaps 37:
1.16 schwarze 38: typedef void (*out_mdoc)(void *, const struct mdoc *);
39: typedef void (*out_man)(void *, const struct man *);
1.1 kristaps 40: typedef void (*out_free)(void *);
41:
42: struct buf {
43: char *buf;
44: size_t sz;
45: };
46:
47: enum intt {
48: INTT_AUTO,
49: INTT_MDOC,
50: INTT_MAN
51: };
52:
53: enum outt {
54: OUTT_ASCII = 0,
55: OUTT_TREE,
1.17 schwarze 56: OUTT_HTML,
1.21 schwarze 57: OUTT_XHTML,
1.36 schwarze 58: OUTT_LINT,
59: OUTT_PS
1.1 kristaps 60: };
61:
62: struct curparse {
63: const char *file; /* Current parse. */
64: int fd; /* Current parse. */
65: int wflags;
1.30 schwarze 66: /* FIXME: set by max error */
1.13 schwarze 67: #define WARN_WALL (1 << 0) /* All-warnings mask. */
1.1 kristaps 68: #define WARN_WERR (1 << 2) /* Warnings->errors. */
69: int fflags;
1.22 schwarze 70: #define FL_IGN_SCOPE (1 << 0) /* Ignore scope errors. */
71: #define FL_NIGN_ESCAPE (1 << 1) /* Don't ignore bad escapes. */
72: #define FL_NIGN_MACRO (1 << 2) /* Don't ignore bad macros. */
73: #define FL_IGN_ERRORS (1 << 4) /* Ignore failed parse. */
1.27 schwarze 74: #define FL_STRICT FL_NIGN_ESCAPE | \
1.33 schwarze 75: FL_NIGN_MACRO /* ignore nothing */
76: enum intt inttype; /* which parser to use */
77: struct man *man; /* man parser */
78: struct mdoc *mdoc; /* mdoc parser */
79: struct roff *roff; /* roff parser (!NULL) */
1.38 schwarze 80: struct regset regs; /* roff registers */
1.33 schwarze 81: enum outt outtype; /* which output to use */
82: out_mdoc outmdoc; /* mdoc output ptr */
83: out_man outman; /* man output ptr */
84: out_free outfree; /* free output ptr */
85: void *outdata; /* data for output */
86: char outopts[BUFSIZ]; /* buf of output opts */
87: };
88:
89: static const char * const mandocerrs[MANDOCERR_MAX] = {
90: "ok",
1.40 schwarze 91:
92: "generic warning",
93:
1.33 schwarze 94: "text should be uppercase",
1.34 schwarze 95: "sections out of conventional order",
1.33 schwarze 96: "section name repeats",
97: "out of order prologue",
98: "repeated prologue entry",
99: "list type must come first",
100: "bad standard",
101: "bad library",
102: "bad escape sequence",
103: "unterminated quoted string",
104: "argument requires the width argument",
105: "superfluous width argument",
1.37 schwarze 106: "ignoring argument",
1.33 schwarze 107: "bad date argument",
108: "bad width argument",
1.34 schwarze 109: "unknown manual section",
1.33 schwarze 110: "section not in conventional manual section",
111: "end of line whitespace",
1.41 ! schwarze 112: "blocks badly nested",
1.33 schwarze 113: "scope open on exit",
1.40 schwarze 114:
115: "generic error",
116:
1.33 schwarze 117: "NAME section must come first",
118: "bad Boolean value",
119: "child violates parent syntax",
120: "bad AT&T symbol",
121: "list type repeated",
122: "display type repeated",
123: "argument repeated",
124: "manual name not yet set",
125: "obsolete macro ignored",
126: "empty macro ignored",
127: "macro not allowed in body",
128: "macro not allowed in prologue",
129: "bad character",
130: "bad NAME section contents",
131: "no blank lines",
132: "no text in this context",
133: "bad comment style",
134: "unknown macro will be lost",
135: "line scope broken",
136: "argument count wrong",
137: "request scope close w/none open",
138: "scope already open",
139: "macro requires line argument(s)",
140: "macro requires body argument(s)",
141: "macro requires argument(s)",
142: "no title in document",
1.34 schwarze 143: "missing list type",
1.37 schwarze 144: "missing display type",
1.33 schwarze 145: "line argument(s) will be lost",
146: "body argument(s) will be lost",
1.40 schwarze 147:
148: "generic fatal error",
149:
1.34 schwarze 150: "column syntax is inconsistent",
1.33 schwarze 151: "missing font type",
152: "displays may not be nested",
1.37 schwarze 153: "unsupported display type",
1.41 ! schwarze 154: "blocks badly nested",
! 155: "no such block is open",
1.33 schwarze 156: "scope broken, syntax violated",
157: "line scope broken, syntax violated",
158: "argument count wrong, violates syntax",
159: "child violates parent syntax",
160: "argument count wrong, violates syntax",
161: "no document body",
162: "no document prologue",
163: "utsname system call failed",
164: "memory exhausted",
1.1 kristaps 165: };
166:
1.27 schwarze 167: static void fdesc(struct curparse *);
168: static void ffile(const char *, struct curparse *);
1.1 kristaps 169: static int foptions(int *, char *);
1.27 schwarze 170: static struct man *man_init(struct curparse *);
171: static struct mdoc *mdoc_init(struct curparse *);
1.30 schwarze 172: static struct roff *roff_init(struct curparse *);
1.1 kristaps 173: static int moptions(enum intt *, char *);
1.30 schwarze 174: static int mmsg(enum mandocerr, void *,
175: int, int, const char *);
1.1 kristaps 176: static int pset(const char *, int, struct curparse *,
177: struct man **, struct mdoc **);
1.27 schwarze 178: static int toptions(struct curparse *, char *);
179: static void usage(void) __attribute__((noreturn));
1.20 schwarze 180: static void version(void) __attribute__((noreturn));
1.27 schwarze 181: static int woptions(int *, char *);
1.1 kristaps 182:
1.19 schwarze 183: static const char *progname;
1.40 schwarze 184: static int with_fatal;
185: static int with_error;
1.1 kristaps 186:
187: int
188: main(int argc, char *argv[])
189: {
1.27 schwarze 190: int c;
1.1 kristaps 191: struct curparse curp;
192:
1.19 schwarze 193: progname = strrchr(argv[0], '/');
194: if (progname == NULL)
195: progname = argv[0];
196: else
197: ++progname;
198:
199: memset(&curp, 0, sizeof(struct curparse));
1.1 kristaps 200:
201: curp.inttype = INTT_AUTO;
202: curp.outtype = OUTT_ASCII;
203:
204: /* LINTED */
1.18 schwarze 205: while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:")))
1.1 kristaps 206: switch (c) {
207: case ('f'):
208: if ( ! foptions(&curp.fflags, optarg))
1.9 schwarze 209: return(EXIT_FAILURE);
1.1 kristaps 210: break;
211: case ('m'):
212: if ( ! moptions(&curp.inttype, optarg))
1.9 schwarze 213: return(EXIT_FAILURE);
1.1 kristaps 214: break;
1.18 schwarze 215: case ('O'):
216: (void)strlcat(curp.outopts, optarg, BUFSIZ);
217: (void)strlcat(curp.outopts, ",", BUFSIZ);
1.17 schwarze 218: break;
1.1 kristaps 219: case ('T'):
1.22 schwarze 220: if ( ! toptions(&curp, optarg))
1.9 schwarze 221: return(EXIT_FAILURE);
1.1 kristaps 222: break;
223: case ('W'):
224: if ( ! woptions(&curp.wflags, optarg))
1.9 schwarze 225: return(EXIT_FAILURE);
1.1 kristaps 226: break;
1.3 schwarze 227: case ('V'):
228: version();
229: /* NOTREACHED */
1.1 kristaps 230: default:
231: usage();
232: /* NOTREACHED */
233: }
234:
235: argc -= optind;
236: argv += optind;
237:
1.7 schwarze 238: if (NULL == *argv) {
239: curp.file = "<stdin>";
240: curp.fd = STDIN_FILENO;
1.14 schwarze 241:
1.27 schwarze 242: fdesc(&curp);
243: }
244:
245: while (*argv) {
246: ffile(*argv, &curp);
247:
1.40 schwarze 248: if (with_fatal && !(curp.fflags & FL_IGN_ERRORS))
1.27 schwarze 249: break;
250: ++argv;
1.1 kristaps 251: }
252:
253: if (curp.outfree)
254: (*curp.outfree)(curp.outdata);
1.30 schwarze 255: if (curp.mdoc)
256: mdoc_free(curp.mdoc);
257: if (curp.man)
258: man_free(curp.man);
259: if (curp.roff)
260: roff_free(curp.roff);
1.1 kristaps 261:
1.40 schwarze 262: return((with_fatal || with_error) ?
1.27 schwarze 263: EXIT_FAILURE : EXIT_SUCCESS);
1.1 kristaps 264: }
265:
266:
1.20 schwarze 267: static void
1.3 schwarze 268: version(void)
269: {
270:
1.19 schwarze 271: (void)printf("%s %s\n", progname, VERSION);
1.3 schwarze 272: exit(EXIT_SUCCESS);
273: }
274:
275:
1.20 schwarze 276: static void
1.1 kristaps 277: usage(void)
278: {
279:
1.23 jmc 280: (void)fprintf(stderr, "usage: %s [-V] [-foption] "
1.18 schwarze 281: "[-mformat] [-Ooption] [-Toutput] "
1.23 jmc 282: "[-Werr] [file...]\n", progname);
1.1 kristaps 283: exit(EXIT_FAILURE);
284: }
285:
286:
287: static struct man *
288: man_init(struct curparse *curp)
289: {
290: int pflags;
291:
1.6 schwarze 292: /* Defaults from mandoc.1. */
1.2 schwarze 293:
1.24 schwarze 294: pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE;
1.2 schwarze 295:
1.22 schwarze 296: if (curp->fflags & FL_NIGN_MACRO)
1.1 kristaps 297: pflags &= ~MAN_IGN_MACRO;
1.22 schwarze 298: if (curp->fflags & FL_NIGN_ESCAPE)
1.11 schwarze 299: pflags &= ~MAN_IGN_ESCAPE;
1.1 kristaps 300:
1.38 schwarze 301: return(man_alloc(&curp->regs, curp, pflags, mmsg));
1.1 kristaps 302: }
303:
304:
1.30 schwarze 305: static struct roff *
306: roff_init(struct curparse *curp)
307: {
308:
1.38 schwarze 309: return(roff_alloc(&curp->regs, mmsg, curp));
1.30 schwarze 310: }
311:
312:
1.1 kristaps 313: static struct mdoc *
314: mdoc_init(struct curparse *curp)
315: {
316: int pflags;
317:
1.6 schwarze 318: /* Defaults from mandoc.1. */
1.2 schwarze 319:
1.24 schwarze 320: pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE;
1.1 kristaps 321:
1.22 schwarze 322: if (curp->fflags & FL_IGN_SCOPE)
1.1 kristaps 323: pflags |= MDOC_IGN_SCOPE;
1.22 schwarze 324: if (curp->fflags & FL_NIGN_ESCAPE)
1.1 kristaps 325: pflags &= ~MDOC_IGN_ESCAPE;
1.22 schwarze 326: if (curp->fflags & FL_NIGN_MACRO)
1.1 kristaps 327: pflags &= ~MDOC_IGN_MACRO;
328:
1.38 schwarze 329: return(mdoc_alloc(&curp->regs, curp, pflags, mmsg));
1.1 kristaps 330: }
331:
332:
1.27 schwarze 333: static void
334: ffile(const char *file, struct curparse *curp)
1.1 kristaps 335: {
336:
337: curp->file = file;
338: if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
1.19 schwarze 339: perror(curp->file);
1.40 schwarze 340: with_fatal = 1;
1.27 schwarze 341: return;
1.1 kristaps 342: }
343:
1.27 schwarze 344: fdesc(curp);
1.1 kristaps 345:
346: if (-1 == close(curp->fd))
1.19 schwarze 347: perror(curp->file);
1.27 schwarze 348: }
1.1 kristaps 349:
1.27 schwarze 350:
351: static int
352: resize_buf(struct buf *buf, size_t initial)
353: {
354: void *tmp;
355: size_t sz;
356:
357: if (buf->sz == 0)
358: sz = initial;
359: else
360: sz = 2 * buf->sz;
361: tmp = realloc(buf->buf, sz);
362: if (NULL == tmp) {
363: perror(NULL);
364: return(0);
365: }
366: buf->buf = tmp;
367: buf->sz = sz;
368: return(1);
1.1 kristaps 369: }
370:
371:
372: static int
1.27 schwarze 373: read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
1.1 kristaps 374: {
1.27 schwarze 375: struct stat st;
376: size_t off;
1.1 kristaps 377: ssize_t ssz;
1.27 schwarze 378:
379: if (-1 == fstat(curp->fd, &st)) {
380: perror(curp->file);
1.40 schwarze 381: with_fatal = 1;
1.27 schwarze 382: return(0);
383: }
384:
385: /*
386: * If we're a regular file, try just reading in the whole entry
387: * via mmap(). This is faster than reading it into blocks, and
388: * since each file is only a few bytes to begin with, I'm not
389: * concerned that this is going to tank any machines.
390: */
391:
392: if (S_ISREG(st.st_mode)) {
393: if (st.st_size >= (1U << 31)) {
394: fprintf(stderr, "%s: input too large\n",
395: curp->file);
1.40 schwarze 396: with_fatal = 1;
1.27 schwarze 397: return(0);
398: }
399: *with_mmap = 1;
1.30 schwarze 400: fb->sz = (size_t)st.st_size;
1.27 schwarze 401: fb->buf = mmap(NULL, fb->sz, PROT_READ,
1.35 schwarze 402: MAP_FILE|MAP_SHARED, curp->fd, 0);
1.27 schwarze 403: if (fb->buf != MAP_FAILED)
404: return(1);
405: }
406:
407: /*
408: * If this isn't a regular file (like, say, stdin), then we must
409: * go the old way and just read things in bit by bit.
410: */
411:
412: *with_mmap = 0;
413: off = 0;
414: fb->sz = 0;
415: fb->buf = NULL;
416: for (;;) {
417: if (off == fb->sz) {
418: if (fb->sz == (1U << 31)) {
419: fprintf(stderr, "%s: input too large\n",
420: curp->file);
421: break;
422: }
423: if (! resize_buf(fb, 65536))
424: break;
425: }
1.30 schwarze 426: ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
1.27 schwarze 427: if (ssz == 0) {
428: fb->sz = off;
429: return(1);
430: }
431: if (ssz == -1) {
432: perror(curp->file);
433: break;
434: }
1.30 schwarze 435: off += (size_t)ssz;
1.27 schwarze 436: }
437:
438: free(fb->buf);
439: fb->buf = NULL;
1.40 schwarze 440: with_fatal = 1;
1.27 schwarze 441: return(0);
442: }
443:
444:
445: static void
446: fdesc(struct curparse *curp)
447: {
448: struct buf ln, blk;
1.32 schwarze 449: int i, pos, lnn, lnn_start, with_mmap, of;
1.30 schwarze 450: enum rofferr re;
1.1 kristaps 451: struct man *man;
452: struct mdoc *mdoc;
1.30 schwarze 453: struct roff *roff;
1.1 kristaps 454:
455: man = NULL;
456: mdoc = NULL;
1.30 schwarze 457: roff = NULL;
1.38 schwarze 458:
1.27 schwarze 459: memset(&ln, 0, sizeof(struct buf));
1.1 kristaps 460:
461: /*
1.29 schwarze 462: * Two buffers: ln and buf. buf is the input file and may be
463: * memory mapped. ln is a line buffer and grows on-demand.
1.1 kristaps 464: */
465:
1.30 schwarze 466: if ( ! read_whole_file(curp, &blk, &with_mmap))
1.27 schwarze 467: return;
468:
1.30 schwarze 469: if (NULL == curp->roff)
470: curp->roff = roff_init(curp);
471: if (NULL == (roff = curp->roff))
472: goto bailout;
473:
1.29 schwarze 474: for (i = 0, lnn = 1; i < (int)blk.sz;) {
475: pos = 0;
476: lnn_start = lnn;
477: while (i < (int)blk.sz) {
478: if ('\n' == blk.buf[i]) {
479: ++i;
480: ++lnn;
481: break;
482: }
483: /* Trailing backslash is like a plain character. */
484: if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
485: if (pos >= (int)ln.sz)
486: if (! resize_buf(&ln, 256))
487: goto bailout;
488: ln.buf[pos++] = blk.buf[i++];
1.27 schwarze 489: continue;
1.29 schwarze 490: }
491: /* Found an escape and at least one other character. */
492: if ('\n' == blk.buf[i + 1]) {
493: /* Escaped newlines are skipped over */
494: i += 2;
495: ++lnn;
1.27 schwarze 496: continue;
497: }
1.29 schwarze 498: if ('"' == blk.buf[i + 1]) {
499: i += 2;
500: /* Comment, skip to end of line */
501: for (; i < (int)blk.sz; ++i) {
502: if ('\n' == blk.buf[i]) {
503: ++i;
504: ++lnn;
505: break;
506: }
507: }
508: /* Backout trailing whitespaces */
509: for (; pos > 0; --pos) {
510: if (ln.buf[pos - 1] != ' ')
511: break;
512: if (pos > 2 && ln.buf[pos - 2] == '\\')
513: break;
514: }
515: break;
516: }
517: /* Some other escape sequence, copy and continue. */
518: if (pos + 1 >= (int)ln.sz)
519: if (! resize_buf(&ln, 256))
520: goto bailout;
1.1 kristaps 521:
1.29 schwarze 522: ln.buf[pos++] = blk.buf[i++];
523: ln.buf[pos++] = blk.buf[i++];
1.27 schwarze 524: }
1.1 kristaps 525:
1.29 schwarze 526: if (pos >= (int)ln.sz)
527: if (! resize_buf(&ln, 256))
528: goto bailout;
1.30 schwarze 529: ln.buf[pos] = '\0';
530:
1.32 schwarze 531: /*
532: * A significant amount of complexity is contained by
533: * the roff preprocessor. It's line-oriented but can be
534: * expressed on one line, so we need at times to
535: * readjust our starting point and re-run it. The roff
536: * preprocessor can also readjust the buffers with new
537: * data, so we pass them in wholesale.
538: */
539:
540: of = 0;
541: do {
542: re = roff_parseln(roff, lnn_start,
543: &ln.buf, &ln.sz, of, &of);
544: } while (ROFF_RERUN == re);
545:
1.30 schwarze 546: if (ROFF_IGN == re)
547: continue;
548: else if (ROFF_ERR == re)
549: goto bailout;
1.5 schwarze 550:
1.32 schwarze 551: /*
552: * If input parsers have not been allocated, do so now.
553: * We keep these instanced betwen parsers, but set them
554: * locally per parse routine since we can use different
555: * parsers with each one.
556: */
1.1 kristaps 557:
1.32 schwarze 558: if ( ! (man || mdoc))
559: if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc))
560: goto bailout;
1.5 schwarze 561:
1.32 schwarze 562: /* Lastly, push down into the parsers themselves. */
1.1 kristaps 563:
1.32 schwarze 564: if (man && ! man_parseln(man, lnn_start, ln.buf, of))
1.27 schwarze 565: goto bailout;
1.32 schwarze 566: if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of))
1.27 schwarze 567: goto bailout;
1.1 kristaps 568: }
569:
1.5 schwarze 570: /* NOTE a parser may not have been assigned, yet. */
1.1 kristaps 571:
572: if ( ! (man || mdoc)) {
1.19 schwarze 573: fprintf(stderr, "%s: Not a manual\n", curp->file);
1.27 schwarze 574: goto bailout;
1.1 kristaps 575: }
576:
1.32 schwarze 577: /* Clean up the parse routine ASTs. */
578:
1.1 kristaps 579: if (mdoc && ! mdoc_endparse(mdoc))
1.27 schwarze 580: goto bailout;
1.1 kristaps 581: if (man && ! man_endparse(man))
1.27 schwarze 582: goto bailout;
1.30 schwarze 583: if (roff && ! roff_endparse(roff))
584: goto bailout;
1.1 kristaps 585:
1.5 schwarze 586: /* If unset, allocate output dev now (if applicable). */
1.1 kristaps 587:
588: if ( ! (curp->outman && curp->outmdoc)) {
589: switch (curp->outtype) {
1.21 schwarze 590: case (OUTT_XHTML):
591: curp->outdata = xhtml_alloc(curp->outopts);
592: break;
1.17 schwarze 593: case (OUTT_HTML):
594: curp->outdata = html_alloc(curp->outopts);
1.36 schwarze 595: break;
596: case (OUTT_ASCII):
597: curp->outdata = ascii_alloc(curp->outopts);
598: curp->outfree = ascii_free;
599: break;
600: case (OUTT_PS):
1.39 schwarze 601: curp->outdata = ps_alloc(curp->outopts);
1.36 schwarze 602: curp->outfree = ps_free;
603: break;
604: default:
605: break;
606: }
607:
608: switch (curp->outtype) {
609: case (OUTT_HTML):
610: /* FALLTHROUGH */
611: case (OUTT_XHTML):
1.17 schwarze 612: curp->outman = html_man;
613: curp->outmdoc = html_mdoc;
614: curp->outfree = html_free;
615: break;
1.1 kristaps 616: case (OUTT_TREE):
617: curp->outman = tree_man;
618: curp->outmdoc = tree_mdoc;
619: break;
1.36 schwarze 620: case (OUTT_ASCII):
621: /* FALLTHROUGH */
622: case (OUTT_PS):
623: curp->outman = terminal_man;
624: curp->outmdoc = terminal_mdoc;
1.1 kristaps 625: break;
626: default:
627: break;
628: }
629: }
630:
631: /* Execute the out device, if it exists. */
632:
633: if (man && curp->outman)
1.16 schwarze 634: (*curp->outman)(curp->outdata, man);
1.1 kristaps 635: if (mdoc && curp->outmdoc)
1.16 schwarze 636: (*curp->outmdoc)(curp->outdata, mdoc);
1.1 kristaps 637:
1.27 schwarze 638: cleanup:
1.38 schwarze 639: memset(&curp->regs, 0, sizeof(struct regset));
1.30 schwarze 640: if (mdoc)
641: mdoc_reset(mdoc);
642: if (man)
643: man_reset(man);
644: if (roff)
645: roff_reset(roff);
1.27 schwarze 646: if (ln.buf)
647: free(ln.buf);
648: if (with_mmap)
649: munmap(blk.buf, blk.sz);
650: else
651: free(blk.buf);
1.30 schwarze 652:
1.27 schwarze 653: return;
654:
655: bailout:
1.40 schwarze 656: with_fatal = 1;
1.27 schwarze 657: goto cleanup;
1.1 kristaps 658: }
659:
660:
661: static int
662: pset(const char *buf, int pos, struct curparse *curp,
663: struct man **man, struct mdoc **mdoc)
664: {
1.5 schwarze 665: int i;
1.1 kristaps 666:
667: /*
668: * Try to intuit which kind of manual parser should be used. If
669: * passed in by command-line (-man, -mdoc), then use that
670: * explicitly. If passed as -mandoc, then try to guess from the
1.5 schwarze 671: * line: either skip dot-lines, use -mdoc when finding `.Dt', or
1.1 kristaps 672: * default to -man, which is more lenient.
673: */
674:
1.31 schwarze 675: if ('.' == buf[0] || '\'' == buf[0]) {
1.5 schwarze 676: for (i = 1; buf[i]; i++)
677: if (' ' != buf[i] && '\t' != buf[i])
678: break;
679: if (0 == buf[i])
680: return(1);
681: }
1.1 kristaps 682:
683: switch (curp->inttype) {
684: case (INTT_MDOC):
685: if (NULL == curp->mdoc)
686: curp->mdoc = mdoc_init(curp);
687: if (NULL == (*mdoc = curp->mdoc))
688: return(0);
689: return(1);
690: case (INTT_MAN):
691: if (NULL == curp->man)
692: curp->man = man_init(curp);
693: if (NULL == (*man = curp->man))
694: return(0);
695: return(1);
696: default:
697: break;
698: }
699:
700: if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
701: if (NULL == curp->mdoc)
702: curp->mdoc = mdoc_init(curp);
703: if (NULL == (*mdoc = curp->mdoc))
704: return(0);
705: return(1);
706: }
707:
708: if (NULL == curp->man)
709: curp->man = man_init(curp);
710: if (NULL == (*man = curp->man))
711: return(0);
712: return(1);
713: }
714:
715:
716: static int
717: moptions(enum intt *tflags, char *arg)
718: {
719:
720: if (0 == strcmp(arg, "doc"))
721: *tflags = INTT_MDOC;
722: else if (0 == strcmp(arg, "andoc"))
723: *tflags = INTT_AUTO;
724: else if (0 == strcmp(arg, "an"))
725: *tflags = INTT_MAN;
726: else {
1.20 schwarze 727: fprintf(stderr, "%s: Bad argument\n", arg);
1.1 kristaps 728: return(0);
729: }
730:
731: return(1);
732: }
733:
734:
735: static int
1.22 schwarze 736: toptions(struct curparse *curp, char *arg)
1.1 kristaps 737: {
738:
739: if (0 == strcmp(arg, "ascii"))
1.22 schwarze 740: curp->outtype = OUTT_ASCII;
741: else if (0 == strcmp(arg, "lint")) {
742: curp->outtype = OUTT_LINT;
743: curp->wflags |= WARN_WALL;
744: curp->fflags |= FL_STRICT;
745: }
1.1 kristaps 746: else if (0 == strcmp(arg, "tree"))
1.22 schwarze 747: curp->outtype = OUTT_TREE;
1.17 schwarze 748: else if (0 == strcmp(arg, "html"))
1.22 schwarze 749: curp->outtype = OUTT_HTML;
1.21 schwarze 750: else if (0 == strcmp(arg, "xhtml"))
1.22 schwarze 751: curp->outtype = OUTT_XHTML;
1.36 schwarze 752: else if (0 == strcmp(arg, "ps"))
753: curp->outtype = OUTT_PS;
1.1 kristaps 754: else {
1.20 schwarze 755: fprintf(stderr, "%s: Bad argument\n", arg);
1.1 kristaps 756: return(0);
757: }
758:
759: return(1);
760: }
761:
762:
763: static int
764: foptions(int *fflags, char *arg)
765: {
1.10 schwarze 766: char *v, *o;
1.19 schwarze 767: const char *toks[8];
1.1 kristaps 768:
769: toks[0] = "ign-scope";
770: toks[1] = "no-ign-escape";
771: toks[2] = "no-ign-macro";
1.24 schwarze 772: toks[3] = "ign-errors";
773: toks[4] = "strict";
774: toks[5] = "ign-escape";
775: toks[6] = NULL;
1.1 kristaps 776:
1.10 schwarze 777: while (*arg) {
778: o = arg;
1.17 schwarze 779: switch (getsubopt(&arg, UNCONST(toks), &v)) {
1.1 kristaps 780: case (0):
1.22 schwarze 781: *fflags |= FL_IGN_SCOPE;
1.1 kristaps 782: break;
783: case (1):
1.22 schwarze 784: *fflags |= FL_NIGN_ESCAPE;
1.1 kristaps 785: break;
786: case (2):
1.22 schwarze 787: *fflags |= FL_NIGN_MACRO;
1.1 kristaps 788: break;
789: case (3):
1.24 schwarze 790: *fflags |= FL_IGN_ERRORS;
1.1 kristaps 791: break;
792: case (4):
1.24 schwarze 793: *fflags |= FL_STRICT;
1.14 schwarze 794: break;
795: case (5):
1.22 schwarze 796: *fflags &= ~FL_NIGN_ESCAPE;
1.19 schwarze 797: break;
1.1 kristaps 798: default:
1.20 schwarze 799: fprintf(stderr, "%s: Bad argument\n", o);
1.1 kristaps 800: return(0);
801: }
1.10 schwarze 802: }
1.1 kristaps 803:
804: return(1);
805: }
806:
807:
808: static int
809: woptions(int *wflags, char *arg)
810: {
1.10 schwarze 811: char *v, *o;
1.17 schwarze 812: const char *toks[3];
1.1 kristaps 813:
814: toks[0] = "all";
1.13 schwarze 815: toks[1] = "error";
816: toks[2] = NULL;
1.1 kristaps 817:
1.10 schwarze 818: while (*arg) {
819: o = arg;
1.17 schwarze 820: switch (getsubopt(&arg, UNCONST(toks), &v)) {
1.1 kristaps 821: case (0):
822: *wflags |= WARN_WALL;
823: break;
824: case (1):
825: *wflags |= WARN_WERR;
826: break;
827: default:
1.20 schwarze 828: fprintf(stderr, "%s: Bad argument\n", o);
1.1 kristaps 829: return(0);
830: }
1.10 schwarze 831: }
1.1 kristaps 832:
833: return(1);
834: }
835:
836:
1.30 schwarze 837: static int
838: mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
839: {
840: struct curparse *cp;
1.40 schwarze 841: const char *level;
842: int rc;
1.30 schwarze 843:
844: cp = (struct curparse *)arg;
1.40 schwarze 845: level = NULL;
846: rc = 1;
1.30 schwarze 847:
1.40 schwarze 848: if (t >= MANDOCERR_FATAL) {
849: with_fatal = 1;
850: level = "FATAL";
851: rc = 0;
852: } else {
853: if ( ! (WARN_WALL & cp->wflags))
1.33 schwarze 854: return(1);
1.40 schwarze 855: if (t >= MANDOCERR_ERROR) {
856: with_error = 1;
857: level = "ERROR";
858: }
859: if (WARN_WERR & cp->wflags) {
860: with_fatal = 1;
861: rc = 0;
862: }
863: }
1.30 schwarze 864:
1.40 schwarze 865: fprintf(stderr, "%s:%d:%d:", cp->file, ln, col + 1);
866: if (level)
867: fprintf(stderr, " %s:", level);
868: fprintf(stderr, " %s", mandocerrs[t]);
1.30 schwarze 869: if (msg)
870: fprintf(stderr, ": %s", msg);
871: fputc('\n', stderr);
1.33 schwarze 872:
1.40 schwarze 873: return(rc);
1.30 schwarze 874: }