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