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