Removing warning about OSNAME.
[minix.git] / external / bsd / mdocml / dist / main.c
blob8418744f276448ebfd2abba5061bbfbcadcf522a
1 /* $Vendor-Id: main.c,v 1.135 2011/01/04 15:02:00 kristaps Exp $ */
2 /*
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
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.
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.
18 #ifdef HAVE_CONFIG_H
19 #include "config.h"
20 #endif
22 #ifndef __minix
23 #include <sys/mman.h>
24 #endif
25 #include <sys/stat.h>
27 #include <assert.h>
28 #include <ctype.h>
29 #include <fcntl.h>
30 #include <stdio.h>
31 #include <stdint.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
36 #include "mandoc.h"
37 #include "main.h"
38 #include "mdoc.h"
39 #include "man.h"
40 #include "roff.h"
42 #ifndef MAP_FILE
43 #define MAP_FILE 0
44 #endif
46 #define REPARSE_LIMIT 1000
47 #define UNCONST(a) ((void *)(uintptr_t)(const void *)(a))
49 /* FIXME: Intel's compiler? LLVM? pcc? */
51 #if !defined(__GNUC__) || (__GNUC__ < 2)
52 # if !defined(lint)
53 # define __attribute__(x)
54 # endif
55 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
57 typedef void (*out_mdoc)(void *, const struct mdoc *);
58 typedef void (*out_man)(void *, const struct man *);
59 typedef void (*out_free)(void *);
61 struct buf {
62 char *buf;
63 size_t sz;
66 enum intt {
67 INTT_AUTO,
68 INTT_MDOC,
69 INTT_MAN
72 enum outt {
73 OUTT_ASCII = 0,
74 OUTT_TREE,
75 OUTT_HTML,
76 OUTT_XHTML,
77 OUTT_LINT,
78 OUTT_PS,
79 OUTT_PDF
82 struct curparse {
83 const char *file; /* Current parse. */
84 int fd; /* Current parse. */
85 int line; /* Line number in the file. */
86 enum mandoclevel wlevel; /* Ignore messages below this. */
87 int wstop; /* Stop after a file with a warning. */
88 enum intt inttype; /* which parser to use */
89 struct man *pman; /* persistent man parser */
90 struct mdoc *pmdoc; /* persistent mdoc parser */
91 struct man *man; /* man parser */
92 struct mdoc *mdoc; /* mdoc parser */
93 struct roff *roff; /* roff parser (!NULL) */
94 struct regset regs; /* roff registers */
95 int reparse_count; /* finite interpolation stack */
96 enum outt outtype; /* which output to use */
97 out_mdoc outmdoc; /* mdoc output ptr */
98 out_man outman; /* man output ptr */
99 out_free outfree; /* free output ptr */
100 void *outdata; /* data for output */
101 char outopts[BUFSIZ]; /* buf of output opts */
104 static const char * const mandoclevels[MANDOCLEVEL_MAX] = {
105 "SUCCESS",
106 "RESERVED",
107 "WARNING",
108 "ERROR",
109 "FATAL",
110 "BADARG",
111 "SYSERR"
114 static const enum mandocerr mandoclimits[MANDOCLEVEL_MAX] = {
115 MANDOCERR_OK,
116 MANDOCERR_WARNING,
117 MANDOCERR_WARNING,
118 MANDOCERR_ERROR,
119 MANDOCERR_FATAL,
120 MANDOCERR_MAX,
121 MANDOCERR_MAX
124 static const char * const mandocerrs[MANDOCERR_MAX] = {
125 "ok",
127 "generic warning",
129 /* related to the prologue */
130 "no title in document",
131 "document title should be all caps",
132 "unknown manual section",
133 "cannot parse date argument",
134 "prologue macros out of order",
135 "duplicate prologue macro",
136 "macro not allowed in prologue",
137 "macro not allowed in body",
139 /* related to document structure */
140 ".so is fragile, better use ln(1)",
141 "NAME section must come first",
142 "bad NAME section contents",
143 "manual name not yet set",
144 "sections out of conventional order",
145 "duplicate section name",
146 "section not in conventional manual section",
148 /* related to macros and nesting */
149 "skipping obsolete macro",
150 "skipping paragraph macro",
151 "blocks badly nested",
152 "child violates parent syntax",
153 "nested displays are not portable",
154 "already in literal mode",
156 /* related to missing macro arguments */
157 "skipping empty macro",
158 "argument count wrong",
159 "missing display type",
160 "list type must come first",
161 "tag lists require a width argument",
162 "missing font type",
164 /* related to bad macro arguments */
165 "skipping argument",
166 "duplicate argument",
167 "duplicate display type",
168 "duplicate list type",
169 "unknown AT&T UNIX version",
170 "bad Boolean value",
171 "unknown font",
172 "unknown standard specifier",
173 "bad width argument",
175 /* related to plain text */
176 "blank line in non-literal context",
177 "tab in non-literal context",
178 "end of line whitespace",
179 "bad comment style",
180 "unknown escape sequence",
181 "unterminated quoted string",
183 /* related to tables */
184 "extra data cells",
186 "generic error",
188 /* related to tables */
189 "bad table syntax",
190 "bad table option",
191 "bad table layout",
192 "no table layout cells specified",
193 "no table data cells specified",
194 "ignore data in cell",
195 "data block still open",
197 "input stack limit exceeded, infinite loop?",
198 "skipping bad character",
199 "skipping text before the first section header",
200 "skipping unknown macro",
201 "NOT IMPLEMENTED: skipping request",
202 "line scope broken",
203 "argument count wrong",
204 "skipping end of block that is not open",
205 "missing end of block",
206 "scope open on exit",
207 "uname(3) system call failed",
208 "macro requires line argument(s)",
209 "macro requires body argument(s)",
210 "macro requires argument(s)",
211 "missing list type",
212 "line argument(s) will be lost",
213 "body argument(s) will be lost",
215 "generic fatal error",
217 "column syntax is inconsistent",
218 "NOT IMPLEMENTED: .Bd -file",
219 "line scope broken, syntax violated",
220 "argument count wrong, violates syntax",
221 "child violates parent syntax",
222 "argument count wrong, violates syntax",
223 "NOT IMPLEMENTED: .so with absolute path or \"..\"",
224 "no document body",
225 "no document prologue",
226 "static buffer exhausted",
229 static void parsebuf(struct curparse *, struct buf, int);
230 static void pdesc(struct curparse *);
231 static void fdesc(struct curparse *);
232 static void ffile(const char *, struct curparse *);
233 static int pfile(const char *, struct curparse *);
234 static int moptions(enum intt *, char *);
235 static int mmsg(enum mandocerr, void *,
236 int, int, const char *);
237 static void pset(const char *, int, struct curparse *);
238 static int toptions(struct curparse *, char *);
239 static void usage(void) __attribute__((noreturn));
240 static void version(void) __attribute__((noreturn));
241 static int woptions(struct curparse *, char *);
243 static const char *progname;
244 static enum mandoclevel file_status = MANDOCLEVEL_OK;
245 static enum mandoclevel exit_status = MANDOCLEVEL_OK;
248 main(int argc, char *argv[])
250 int c;
251 struct curparse curp;
253 progname = strrchr(argv[0], '/');
254 if (progname == NULL)
255 progname = argv[0];
256 else
257 ++progname;
259 memset(&curp, 0, sizeof(struct curparse));
261 curp.inttype = INTT_AUTO;
262 curp.outtype = OUTT_ASCII;
263 curp.wlevel = MANDOCLEVEL_FATAL;
265 /* LINTED */
266 while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
267 switch (c) {
268 case ('m'):
269 if ( ! moptions(&curp.inttype, optarg))
270 return((int)MANDOCLEVEL_BADARG);
271 break;
272 case ('O'):
273 (void)strlcat(curp.outopts, optarg, BUFSIZ);
274 (void)strlcat(curp.outopts, ",", BUFSIZ);
275 break;
276 case ('T'):
277 if ( ! toptions(&curp, optarg))
278 return((int)MANDOCLEVEL_BADARG);
279 break;
280 case ('W'):
281 if ( ! woptions(&curp, optarg))
282 return((int)MANDOCLEVEL_BADARG);
283 break;
284 case ('V'):
285 version();
286 /* NOTREACHED */
287 default:
288 usage();
289 /* NOTREACHED */
292 argc -= optind;
293 argv += optind;
295 if (NULL == *argv) {
296 curp.file = "<stdin>";
297 curp.fd = STDIN_FILENO;
299 fdesc(&curp);
302 while (*argv) {
303 ffile(*argv, &curp);
304 if (MANDOCLEVEL_OK != exit_status && curp.wstop)
305 break;
306 ++argv;
309 if (curp.outfree)
310 (*curp.outfree)(curp.outdata);
311 if (curp.pmdoc)
312 mdoc_free(curp.pmdoc);
313 if (curp.pman)
314 man_free(curp.pman);
315 if (curp.roff)
316 roff_free(curp.roff);
318 return((int)exit_status);
322 static void
323 version(void)
326 (void)printf("%s %s\n", progname, VERSION);
327 exit((int)MANDOCLEVEL_OK);
331 static void
332 usage(void)
335 (void)fprintf(stderr, "usage: %s "
336 "[-V] "
337 "[-foption] "
338 "[-mformat] "
339 "[-Ooption] "
340 "[-Toutput] "
341 "[-Werr] "
342 "[file...]\n",
343 progname);
345 exit((int)MANDOCLEVEL_BADARG);
348 static void
349 ffile(const char *file, struct curparse *curp)
353 * Called once per input file. Get the file ready for reading,
354 * pass it through to the parser-driver, then close it out.
355 * XXX: don't do anything special as this is only called for
356 * files; stdin goes directly to fdesc().
359 curp->file = file;
361 if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
362 perror(curp->file);
363 exit_status = MANDOCLEVEL_SYSERR;
364 return;
367 fdesc(curp);
369 if (-1 == close(curp->fd))
370 perror(curp->file);
373 static int
374 pfile(const char *file, struct curparse *curp)
376 const char *savefile;
377 int fd, savefd;
379 if (-1 == (fd = open(file, O_RDONLY, 0))) {
380 perror(file);
381 file_status = MANDOCLEVEL_SYSERR;
382 return(0);
385 savefile = curp->file;
386 savefd = curp->fd;
388 curp->file = file;
389 curp->fd = fd;
391 pdesc(curp);
393 curp->file = savefile;
394 curp->fd = savefd;
396 if (-1 == close(fd))
397 perror(file);
399 return(MANDOCLEVEL_FATAL > file_status ? 1 : 0);
403 static void
404 resize_buf(struct buf *buf, size_t initial)
407 buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
408 buf->buf = realloc(buf->buf, buf->sz);
409 if (NULL == buf->buf) {
410 perror(NULL);
411 exit((int)MANDOCLEVEL_SYSERR);
416 static int
417 read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
419 struct stat st;
420 size_t off;
421 ssize_t ssz;
423 if (-1 == fstat(curp->fd, &st)) {
424 perror(curp->file);
425 return(0);
429 * If we're a regular file, try just reading in the whole entry
430 * via mmap(). This is faster than reading it into blocks, and
431 * since each file is only a few bytes to begin with, I'm not
432 * concerned that this is going to tank any machines.
435 #ifndef __minix
436 if (S_ISREG(st.st_mode)) {
437 if (st.st_size >= (1U << 31)) {
438 fprintf(stderr, "%s: input too large\n",
439 curp->file);
440 return(0);
442 *with_mmap = 1;
443 fb->sz = (size_t)st.st_size;
444 fb->buf = mmap(NULL, fb->sz, PROT_READ,
445 MAP_FILE|MAP_SHARED, curp->fd, 0);
446 if (fb->buf != MAP_FAILED)
447 return(1);
449 #endif
452 * If this isn't a regular file (like, say, stdin), then we must
453 * go the old way and just read things in bit by bit.
456 *with_mmap = 0;
457 off = 0;
458 fb->sz = 0;
459 fb->buf = NULL;
460 for (;;) {
461 if (off == fb->sz) {
462 if (fb->sz == (1U << 31)) {
463 fprintf(stderr, "%s: input too large\n",
464 curp->file);
465 break;
467 resize_buf(fb, 65536);
469 ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
470 if (ssz == 0) {
471 fb->sz = off;
472 return(1);
474 if (ssz == -1) {
475 perror(curp->file);
476 break;
478 off += (size_t)ssz;
481 free(fb->buf);
482 fb->buf = NULL;
483 return(0);
487 static void
488 fdesc(struct curparse *curp)
492 * Called once per file with an opened file descriptor. All
493 * pre-file-parse operations (whether stdin or a file) should go
494 * here.
496 * This calls down into the nested parser, which drills down and
497 * fully parses a file and all its dependences (i.e., `so'). It
498 * then runs the cleanup validators and pushes to output.
501 /* Zero the parse type. */
503 curp->mdoc = NULL;
504 curp->man = NULL;
505 file_status = MANDOCLEVEL_OK;
507 /* Make sure the mandotory roff parser is initialised. */
509 if (NULL == curp->roff) {
510 curp->roff = roff_alloc(&curp->regs, curp, mmsg);
511 assert(curp->roff);
514 /* Fully parse the file. */
516 pdesc(curp);
518 if (MANDOCLEVEL_FATAL <= file_status)
519 goto cleanup;
521 /* NOTE a parser may not have been assigned, yet. */
523 if ( ! (curp->man || curp->mdoc)) {
524 fprintf(stderr, "%s: Not a manual\n", curp->file);
525 file_status = MANDOCLEVEL_FATAL;
526 goto cleanup;
529 /* Clean up the parse routine ASTs. */
531 if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
532 assert(MANDOCLEVEL_FATAL <= file_status);
533 goto cleanup;
536 if (curp->man && ! man_endparse(curp->man)) {
537 assert(MANDOCLEVEL_FATAL <= file_status);
538 goto cleanup;
541 assert(curp->roff);
542 roff_endparse(curp->roff);
545 * With -Wstop and warnings or errors of at least
546 * the requested level, do not produce output.
549 if (MANDOCLEVEL_OK != file_status && curp->wstop)
550 goto cleanup;
552 /* If unset, allocate output dev now (if applicable). */
554 if ( ! (curp->outman && curp->outmdoc)) {
555 switch (curp->outtype) {
556 case (OUTT_XHTML):
557 curp->outdata = xhtml_alloc(curp->outopts);
558 break;
559 case (OUTT_HTML):
560 curp->outdata = html_alloc(curp->outopts);
561 break;
562 case (OUTT_ASCII):
563 curp->outdata = ascii_alloc(curp->outopts);
564 curp->outfree = ascii_free;
565 break;
566 case (OUTT_PDF):
567 curp->outdata = pdf_alloc(curp->outopts);
568 curp->outfree = pspdf_free;
569 break;
570 case (OUTT_PS):
571 curp->outdata = ps_alloc(curp->outopts);
572 curp->outfree = pspdf_free;
573 break;
574 default:
575 break;
578 switch (curp->outtype) {
579 case (OUTT_HTML):
580 /* FALLTHROUGH */
581 case (OUTT_XHTML):
582 curp->outman = html_man;
583 curp->outmdoc = html_mdoc;
584 curp->outfree = html_free;
585 break;
586 case (OUTT_TREE):
587 curp->outman = tree_man;
588 curp->outmdoc = tree_mdoc;
589 break;
590 case (OUTT_PDF):
591 /* FALLTHROUGH */
592 case (OUTT_ASCII):
593 /* FALLTHROUGH */
594 case (OUTT_PS):
595 curp->outman = terminal_man;
596 curp->outmdoc = terminal_mdoc;
597 break;
598 default:
599 break;
603 /* Execute the out device, if it exists. */
605 if (curp->man && curp->outman)
606 (*curp->outman)(curp->outdata, curp->man);
607 if (curp->mdoc && curp->outmdoc)
608 (*curp->outmdoc)(curp->outdata, curp->mdoc);
610 cleanup:
612 memset(&curp->regs, 0, sizeof(struct regset));
614 /* Reset the current-parse compilers. */
616 if (curp->mdoc)
617 mdoc_reset(curp->mdoc);
618 if (curp->man)
619 man_reset(curp->man);
621 assert(curp->roff);
622 roff_reset(curp->roff);
624 if (exit_status < file_status)
625 exit_status = file_status;
627 return;
630 static void
631 pdesc(struct curparse *curp)
633 struct buf blk;
634 int with_mmap;
637 * Run for each opened file; may be called more than once for
638 * each full parse sequence if the opened file is nested (i.e.,
639 * from `so'). Simply sucks in the whole file and moves into
640 * the parse phase for the file.
643 if ( ! read_whole_file(curp, &blk, &with_mmap)) {
644 file_status = MANDOCLEVEL_SYSERR;
645 return;
648 /* Line number is per-file. */
650 curp->line = 1;
652 parsebuf(curp, blk, 1);
654 #ifndef __minix
655 if (with_mmap)
656 munmap(blk.buf, blk.sz);
657 else
658 #endif
659 free(blk.buf);
662 static void
663 parsebuf(struct curparse *curp, struct buf blk, int start)
665 struct buf ln;
666 enum rofferr rr;
667 int i, of, rc;
668 int pos; /* byte number in the ln buffer */
669 int lnn; /* line number in the real file */
670 unsigned char c;
673 * Main parse routine for an opened file. This is called for
674 * each opened file and simply loops around the full input file,
675 * possibly nesting (i.e., with `so').
678 memset(&ln, 0, sizeof(struct buf));
680 lnn = curp->line;
681 pos = 0;
683 for (i = 0; i < (int)blk.sz; ) {
684 if (0 == pos && '\0' == blk.buf[i])
685 break;
687 if (start) {
688 curp->line = lnn;
689 curp->reparse_count = 0;
692 while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
693 if ('\n' == blk.buf[i]) {
694 ++i;
695 ++lnn;
696 break;
700 * Warn about bogus characters. If you're using
701 * non-ASCII encoding, you're screwing your
702 * readers. Since I'd rather this not happen,
703 * I'll be helpful and drop these characters so
704 * we don't display gibberish. Note to manual
705 * writers: use special characters.
708 c = (unsigned char) blk.buf[i];
710 if ( ! (isascii(c) &&
711 (isgraph(c) || isblank(c)))) {
712 mmsg(MANDOCERR_BADCHAR, curp,
713 curp->line, pos, "ignoring byte");
714 i++;
715 continue;
718 /* Trailing backslash = a plain char. */
720 if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
721 if (pos >= (int)ln.sz)
722 resize_buf(&ln, 256);
723 ln.buf[pos++] = blk.buf[i++];
724 continue;
727 /* Found escape & at least one other char. */
729 if ('\n' == blk.buf[i + 1]) {
730 i += 2;
731 /* Escaped newlines are skipped over */
732 ++lnn;
733 continue;
736 if ('"' == blk.buf[i + 1]) {
737 i += 2;
738 /* Comment, skip to end of line */
739 for (; i < (int)blk.sz; ++i) {
740 if ('\n' == blk.buf[i]) {
741 ++i;
742 ++lnn;
743 break;
747 /* Backout trailing whitespaces */
748 for (; pos > 0; --pos) {
749 if (ln.buf[pos - 1] != ' ')
750 break;
751 if (pos > 2 && ln.buf[pos - 2] == '\\')
752 break;
754 break;
757 /* Some other escape sequence, copy & cont. */
759 if (pos + 1 >= (int)ln.sz)
760 resize_buf(&ln, 256);
762 ln.buf[pos++] = blk.buf[i++];
763 ln.buf[pos++] = blk.buf[i++];
766 if (pos >= (int)ln.sz)
767 resize_buf(&ln, 256);
769 ln.buf[pos] = '\0';
772 * A significant amount of complexity is contained by
773 * the roff preprocessor. It's line-oriented but can be
774 * expressed on one line, so we need at times to
775 * readjust our starting point and re-run it. The roff
776 * preprocessor can also readjust the buffers with new
777 * data, so we pass them in wholesale.
780 of = 0;
782 rerun:
783 rr = roff_parseln
784 (curp->roff, curp->line,
785 &ln.buf, &ln.sz, of, &of);
787 switch (rr) {
788 case (ROFF_REPARSE):
789 if (REPARSE_LIMIT >= ++curp->reparse_count)
790 parsebuf(curp, ln, 0);
791 else
792 mmsg(MANDOCERR_ROFFLOOP, curp,
793 curp->line, pos, NULL);
794 pos = 0;
795 continue;
796 case (ROFF_APPEND):
797 pos = strlen(ln.buf);
798 continue;
799 case (ROFF_RERUN):
800 goto rerun;
801 case (ROFF_IGN):
802 pos = 0;
803 continue;
804 case (ROFF_ERR):
805 assert(MANDOCLEVEL_FATAL <= file_status);
806 break;
807 case (ROFF_SO):
808 if (pfile(ln.buf + of, curp)) {
809 pos = 0;
810 continue;
811 } else
812 break;
813 default:
814 break;
818 * If we encounter errors in the recursive parsebuf()
819 * call, make sure we don't continue parsing.
822 if (MANDOCLEVEL_FATAL <= file_status)
823 break;
826 * If input parsers have not been allocated, do so now.
827 * We keep these instanced betwen parsers, but set them
828 * locally per parse routine since we can use different
829 * parsers with each one.
832 if ( ! (curp->man || curp->mdoc))
833 pset(ln.buf + of, pos - of, curp);
836 * Lastly, push down into the parsers themselves. One
837 * of these will have already been set in the pset()
838 * routine.
839 * If libroff returns ROFF_TBL, then add it to the
840 * currently open parse. Since we only get here if
841 * there does exist data (see tbl_data.c), we're
842 * guaranteed that something's been allocated.
845 if (ROFF_TBL == rr) {
846 assert(curp->man || curp->mdoc);
847 if (curp->man)
848 man_addspan(curp->man, roff_span(curp->roff));
849 else
850 mdoc_addspan(curp->mdoc, roff_span(curp->roff));
852 } else if (curp->man || curp->mdoc) {
853 rc = curp->man ?
854 man_parseln(curp->man,
855 curp->line, ln.buf, of) :
856 mdoc_parseln(curp->mdoc,
857 curp->line, ln.buf, of);
859 if ( ! rc) {
860 assert(MANDOCLEVEL_FATAL <= file_status);
861 break;
865 /* Temporary buffers typically are not full. */
867 if (0 == start && '\0' == blk.buf[i])
868 break;
870 /* Start the next input line. */
872 pos = 0;
875 free(ln.buf);
878 static void
879 pset(const char *buf, int pos, struct curparse *curp)
881 int i;
884 * Try to intuit which kind of manual parser should be used. If
885 * passed in by command-line (-man, -mdoc), then use that
886 * explicitly. If passed as -mandoc, then try to guess from the
887 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
888 * default to -man, which is more lenient.
890 * Separate out pmdoc/pman from mdoc/man: the first persists
891 * through all parsers, while the latter is used per-parse.
894 if ('.' == buf[0] || '\'' == buf[0]) {
895 for (i = 1; buf[i]; i++)
896 if (' ' != buf[i] && '\t' != buf[i])
897 break;
898 if ('\0' == buf[i])
899 return;
902 switch (curp->inttype) {
903 case (INTT_MDOC):
904 if (NULL == curp->pmdoc)
905 curp->pmdoc = mdoc_alloc
906 (&curp->regs, curp, mmsg);
907 assert(curp->pmdoc);
908 curp->mdoc = curp->pmdoc;
909 return;
910 case (INTT_MAN):
911 if (NULL == curp->pman)
912 curp->pman = man_alloc
913 (&curp->regs, curp, mmsg);
914 assert(curp->pman);
915 curp->man = curp->pman;
916 return;
917 default:
918 break;
921 if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3)) {
922 if (NULL == curp->pmdoc)
923 curp->pmdoc = mdoc_alloc
924 (&curp->regs, curp, mmsg);
925 assert(curp->pmdoc);
926 curp->mdoc = curp->pmdoc;
927 return;
930 if (NULL == curp->pman)
931 curp->pman = man_alloc(&curp->regs, curp, mmsg);
932 assert(curp->pman);
933 curp->man = curp->pman;
936 static int
937 moptions(enum intt *tflags, char *arg)
940 if (0 == strcmp(arg, "doc"))
941 *tflags = INTT_MDOC;
942 else if (0 == strcmp(arg, "andoc"))
943 *tflags = INTT_AUTO;
944 else if (0 == strcmp(arg, "an"))
945 *tflags = INTT_MAN;
946 else {
947 fprintf(stderr, "%s: Bad argument\n", arg);
948 return(0);
951 return(1);
954 static int
955 toptions(struct curparse *curp, char *arg)
958 if (0 == strcmp(arg, "ascii"))
959 curp->outtype = OUTT_ASCII;
960 else if (0 == strcmp(arg, "lint")) {
961 curp->outtype = OUTT_LINT;
962 curp->wlevel = MANDOCLEVEL_WARNING;
964 else if (0 == strcmp(arg, "tree"))
965 curp->outtype = OUTT_TREE;
966 else if (0 == strcmp(arg, "html"))
967 curp->outtype = OUTT_HTML;
968 else if (0 == strcmp(arg, "xhtml"))
969 curp->outtype = OUTT_XHTML;
970 else if (0 == strcmp(arg, "ps"))
971 curp->outtype = OUTT_PS;
972 else if (0 == strcmp(arg, "pdf"))
973 curp->outtype = OUTT_PDF;
974 else {
975 fprintf(stderr, "%s: Bad argument\n", arg);
976 return(0);
979 return(1);
982 static int
983 woptions(struct curparse *curp, char *arg)
985 char *v, *o;
986 const char *toks[6];
988 toks[0] = "stop";
989 toks[1] = "all";
990 toks[2] = "warning";
991 toks[3] = "error";
992 toks[4] = "fatal";
993 toks[5] = NULL;
995 while (*arg) {
996 o = arg;
997 switch (getsubopt(&arg, UNCONST(toks), &v)) {
998 case (0):
999 curp->wstop = 1;
1000 break;
1001 case (1):
1002 /* FALLTHROUGH */
1003 case (2):
1004 curp->wlevel = MANDOCLEVEL_WARNING;
1005 break;
1006 case (3):
1007 curp->wlevel = MANDOCLEVEL_ERROR;
1008 break;
1009 case (4):
1010 curp->wlevel = MANDOCLEVEL_FATAL;
1011 break;
1012 default:
1013 fprintf(stderr, "-W%s: Bad argument\n", o);
1014 return(0);
1018 return(1);
1021 static int
1022 mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
1024 struct curparse *cp;
1025 enum mandoclevel level;
1027 level = MANDOCLEVEL_FATAL;
1028 while (t < mandoclimits[level])
1029 /* LINTED */
1030 level--;
1032 cp = (struct curparse *)arg;
1033 if (level < cp->wlevel)
1034 return(1);
1036 fprintf(stderr, "%s:%d:%d: %s: %s",
1037 cp->file, ln, col + 1, mandoclevels[level], mandocerrs[t]);
1038 if (msg)
1039 fprintf(stderr, ": %s", msg);
1040 fputc('\n', stderr);
1042 if (file_status < level)
1043 file_status = level;
1045 return(level < MANDOCLEVEL_FATAL);