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