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