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