Home | History | Annotate | Line # | Download | only in dist
main.c revision 1.1.1.6
      1 /*	$Vendor-Id: main.c,v 1.79 2010/05/17 22:11:42 kristaps Exp $ */
      2 /*
      3  * Copyright (c) 2008, 2009 Kristaps Dzonsons <kristaps (at) kth.se>
      4  *
      5  * Permission to use, copy, modify, and distribute this software for any
      6  * purpose with or without fee is hereby granted, provided that the above
      7  * copyright notice and this permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  */
     17 #ifdef HAVE_CONFIG_H
     18 #include "config.h"
     19 #endif
     20 
     21 #include <sys/mman.h>
     22 #include <sys/stat.h>
     23 
     24 #include <assert.h>
     25 #include <fcntl.h>
     26 #include <stdio.h>
     27 #include <stdint.h>
     28 #include <stdlib.h>
     29 #include <string.h>
     30 #include <unistd.h>
     31 
     32 #include "mandoc.h"
     33 #include "mdoc.h"
     34 #include "man.h"
     35 #include "roff.h"
     36 #include "main.h"
     37 
     38 #define	UNCONST(a)	((void *)(uintptr_t)(const void *)(a))
     39 
     40 /* FIXME: Intel's compiler?  LLVM?  pcc?  */
     41 
     42 #if !defined(__GNUC__) || (__GNUC__ < 2)
     43 # if !defined(lint)
     44 #  define __attribute__(x)
     45 # endif
     46 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
     47 
     48 typedef	void		(*out_mdoc)(void *, const struct mdoc *);
     49 typedef	void		(*out_man)(void *, const struct man *);
     50 typedef	void		(*out_free)(void *);
     51 
     52 struct	buf {
     53 	char	 	 *buf;
     54 	size_t		  sz;
     55 };
     56 
     57 enum	intt {
     58 	INTT_AUTO,
     59 	INTT_MDOC,
     60 	INTT_MAN
     61 };
     62 
     63 enum	outt {
     64 	OUTT_ASCII = 0,
     65 	OUTT_TREE,
     66 	OUTT_HTML,
     67 	OUTT_XHTML,
     68 	OUTT_LINT
     69 };
     70 
     71 struct	curparse {
     72 	const char	 *file;		/* Current parse. */
     73 	int		  fd;		/* Current parse. */
     74 	int		  wflags;
     75 	/* FIXME: set by max error */
     76 #define	WARN_WALL	 (1 << 0)	/* All-warnings mask. */
     77 #define	WARN_WERR	 (1 << 2)	/* Warnings->errors. */
     78 	int		  fflags;
     79 #define	FL_IGN_SCOPE	 (1 << 0) 	/* Ignore scope errors. */
     80 #define	FL_NIGN_ESCAPE	 (1 << 1) 	/* Don't ignore bad escapes. */
     81 #define	FL_NIGN_MACRO	 (1 << 2) 	/* Don't ignore bad macros. */
     82 #define	FL_IGN_ERRORS	 (1 << 4)	/* Ignore failed parse. */
     83 #define	FL_STRICT	  FL_NIGN_ESCAPE | \
     84 			  FL_NIGN_MACRO /* ignore nothing */
     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 	enum outt	  outtype; 	/* which output to use */
     90 	out_mdoc	  outmdoc;	/* mdoc output ptr */
     91 	out_man	  	  outman;	/* man output ptr */
     92 	out_free	  outfree;	/* free output ptr */
     93 	void		 *outdata;	/* data for output */
     94 	char		  outopts[BUFSIZ]; /* buf of output opts */
     95 };
     96 
     97 static	const char * const	mandocerrs[MANDOCERR_MAX] = {
     98 	"ok",
     99 	"text should be uppercase",
    100 	"sections out of conentional order",
    101 	"section name repeats",
    102 	"out of order prologue",
    103 	"repeated prologue entry",
    104 	"list type must come first",
    105 	"column syntax is inconsistent",
    106 	"bad standard",
    107 	"bad library",
    108 	"bad escape sequence",
    109 	"unterminated quoted string",
    110 	"argument requires the width argument",
    111 	"superfluous width argument",
    112 	"bad date argument",
    113 	"bad width argument",
    114 	"unknown manual sction",
    115 	"section not in conventional manual section",
    116 	"end of line whitespace",
    117 	"scope open on exit",
    118 	"NAME section must come first",
    119 	"bad Boolean value",
    120 	"child violates parent syntax",
    121 	"bad AT&T symbol",
    122 	"list type repeated",
    123 	"display type repeated",
    124 	"argument repeated",
    125 	"manual name not yet set",
    126 	"obsolete macro ignored",
    127 	"empty macro ignored",
    128 	"macro not allowed in body",
    129 	"macro not allowed in prologue",
    130 	"bad character",
    131 	"bad NAME section contents",
    132 	"no blank lines",
    133 	"no text in this context",
    134 	"bad comment style",
    135 	"unknown macro will be lost",
    136 	"line scope broken",
    137 	"scope broken",
    138 	"argument count wrong",
    139 	"request scope close w/none open",
    140 	"scope already open",
    141 	"macro requires line argument(s)",
    142 	"macro requires body argument(s)",
    143 	"macro requires argument(s)",
    144 	"no title in document",
    145 	"line argument(s) will be lost",
    146 	"body argument(s) will be lost",
    147 	"missing font type",
    148 	"missing display type",
    149 	"missing list type",
    150 	"displays may not be nested",
    151 	"no scope to rewind: syntax violated",
    152 	"scope broken, syntax violated",
    153 	"line scope broken, syntax violated",
    154 	"argument count wrong, violates syntax",
    155 	"child violates parent syntax",
    156 	"argument count wrong, violates syntax",
    157 	"no document body",
    158 	"no document prologue",
    159 	"utsname system call failed",
    160 	"memory exhausted",
    161 };
    162 
    163 static	void		  fdesc(struct curparse *);
    164 static	void		  ffile(const char *, struct curparse *);
    165 static	int		  foptions(int *, char *);
    166 static	struct man	 *man_init(struct curparse *);
    167 static	struct mdoc	 *mdoc_init(struct curparse *);
    168 static	struct roff	 *roff_init(struct curparse *);
    169 static	int		  moptions(enum intt *, char *);
    170 static	int		  mmsg(enum mandocerr, void *,
    171 				int, int, const char *);
    172 static	int		  pset(const char *, int, struct curparse *,
    173 				struct man **, struct mdoc **);
    174 static	int		  toptions(struct curparse *, char *);
    175 static	void		  usage(void) __attribute__((noreturn));
    176 static	void		  version(void) __attribute__((noreturn));
    177 static	int		  woptions(int *, char *);
    178 
    179 static	const char	 *progname;
    180 static 	int		  with_error;
    181 static	int		  with_warning;
    182 
    183 int
    184 main(int argc, char *argv[])
    185 {
    186 	int		 c;
    187 	struct curparse	 curp;
    188 
    189 	progname = strrchr(argv[0], '/');
    190 	if (progname == NULL)
    191 		progname = argv[0];
    192 	else
    193 		++progname;
    194 
    195 	memset(&curp, 0, sizeof(struct curparse));
    196 
    197 	curp.inttype = INTT_AUTO;
    198 	curp.outtype = OUTT_ASCII;
    199 
    200 	/* LINTED */
    201 	while (-1 != (c = getopt(argc, argv, "f:m:O:T:VW:")))
    202 		switch (c) {
    203 		case ('f'):
    204 			if ( ! foptions(&curp.fflags, optarg))
    205 				return(EXIT_FAILURE);
    206 			break;
    207 		case ('m'):
    208 			if ( ! moptions(&curp.inttype, optarg))
    209 				return(EXIT_FAILURE);
    210 			break;
    211 		case ('O'):
    212 			(void)strlcat(curp.outopts, optarg, BUFSIZ);
    213 			(void)strlcat(curp.outopts, ",", BUFSIZ);
    214 			break;
    215 		case ('T'):
    216 			if ( ! toptions(&curp, optarg))
    217 				return(EXIT_FAILURE);
    218 			break;
    219 		case ('W'):
    220 			if ( ! woptions(&curp.wflags, optarg))
    221 				return(EXIT_FAILURE);
    222 			break;
    223 		case ('V'):
    224 			version();
    225 			/* NOTREACHED */
    226 		default:
    227 			usage();
    228 			/* NOTREACHED */
    229 		}
    230 
    231 	argc -= optind;
    232 	argv += optind;
    233 
    234 	if (NULL == *argv) {
    235 		curp.file = "<stdin>";
    236 		curp.fd = STDIN_FILENO;
    237 
    238 		fdesc(&curp);
    239 	}
    240 
    241 	while (*argv) {
    242 		ffile(*argv, &curp);
    243 
    244 		if (with_error && !(curp.fflags & FL_IGN_ERRORS))
    245 			break;
    246 		++argv;
    247 	}
    248 
    249 	if (curp.outfree)
    250 		(*curp.outfree)(curp.outdata);
    251 	if (curp.mdoc)
    252 		mdoc_free(curp.mdoc);
    253 	if (curp.man)
    254 		man_free(curp.man);
    255 	if (curp.roff)
    256 		roff_free(curp.roff);
    257 
    258 	return((with_warning || with_error) ?
    259 			EXIT_FAILURE :  EXIT_SUCCESS);
    260 }
    261 
    262 
    263 static void
    264 version(void)
    265 {
    266 
    267 	(void)printf("%s %s\n", progname, VERSION);
    268 	exit(EXIT_SUCCESS);
    269 }
    270 
    271 
    272 static void
    273 usage(void)
    274 {
    275 
    276 	(void)fprintf(stderr, "usage: %s [-V] [-foption] "
    277 			"[-mformat] [-Ooption] [-Toutput] "
    278 			"[-Werr] [file...]\n", progname);
    279 	exit(EXIT_FAILURE);
    280 }
    281 
    282 
    283 static struct man *
    284 man_init(struct curparse *curp)
    285 {
    286 	int		 pflags;
    287 
    288 	/* Defaults from mandoc.1. */
    289 
    290 	pflags = MAN_IGN_MACRO | MAN_IGN_ESCAPE;
    291 
    292 	if (curp->fflags & FL_NIGN_MACRO)
    293 		pflags &= ~MAN_IGN_MACRO;
    294 	if (curp->fflags & FL_NIGN_ESCAPE)
    295 		pflags &= ~MAN_IGN_ESCAPE;
    296 
    297 	return(man_alloc(curp, pflags, mmsg));
    298 }
    299 
    300 
    301 static struct roff *
    302 roff_init(struct curparse *curp)
    303 {
    304 
    305 	return(roff_alloc(mmsg, curp));
    306 }
    307 
    308 
    309 static struct mdoc *
    310 mdoc_init(struct curparse *curp)
    311 {
    312 	int		 pflags;
    313 
    314 	/* Defaults from mandoc.1. */
    315 
    316 	pflags = MDOC_IGN_MACRO | MDOC_IGN_ESCAPE;
    317 
    318 	if (curp->fflags & FL_IGN_SCOPE)
    319 		pflags |= MDOC_IGN_SCOPE;
    320 	if (curp->fflags & FL_NIGN_ESCAPE)
    321 		pflags &= ~MDOC_IGN_ESCAPE;
    322 	if (curp->fflags & FL_NIGN_MACRO)
    323 		pflags &= ~MDOC_IGN_MACRO;
    324 
    325 	return(mdoc_alloc(curp, pflags, mmsg));
    326 }
    327 
    328 
    329 static void
    330 ffile(const char *file, struct curparse *curp)
    331 {
    332 
    333 	curp->file = file;
    334 	if (-1 == (curp->fd = open(curp->file, O_RDONLY, 0))) {
    335 		perror(curp->file);
    336 		with_error = 1;
    337 		return;
    338 	}
    339 
    340 	fdesc(curp);
    341 
    342 	if (-1 == close(curp->fd))
    343 		perror(curp->file);
    344 }
    345 
    346 
    347 static int
    348 resize_buf(struct buf *buf, size_t initial)
    349 {
    350 	void *tmp;
    351 	size_t sz;
    352 
    353 	if (buf->sz == 0)
    354 		sz = initial;
    355 	else
    356 		sz = 2 * buf->sz;
    357 	tmp = realloc(buf->buf, sz);
    358 	if (NULL == tmp) {
    359 		perror(NULL);
    360 		return(0);
    361 	}
    362 	buf->buf = tmp;
    363 	buf->sz = sz;
    364 	return(1);
    365 }
    366 
    367 
    368 static int
    369 read_whole_file(struct curparse *curp, struct buf *fb, int *with_mmap)
    370 {
    371 	struct stat	 st;
    372 	size_t		 off;
    373 	ssize_t		 ssz;
    374 
    375 	if (-1 == fstat(curp->fd, &st)) {
    376 		perror(curp->file);
    377 		with_error = 1;
    378 		return(0);
    379 	}
    380 
    381 	/*
    382 	 * If we're a regular file, try just reading in the whole entry
    383 	 * via mmap().  This is faster than reading it into blocks, and
    384 	 * since each file is only a few bytes to begin with, I'm not
    385 	 * concerned that this is going to tank any machines.
    386 	 */
    387 
    388 	if (S_ISREG(st.st_mode)) {
    389 		if (st.st_size >= (1U << 31)) {
    390 			fprintf(stderr, "%s: input too large\n",
    391 					curp->file);
    392 			with_error = 1;
    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, 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 			if (! resize_buf(fb, 65536))
    420 				break;
    421 		}
    422 		ssz = read(curp->fd, fb->buf + (int)off, fb->sz - off);
    423 		if (ssz == 0) {
    424 			fb->sz = off;
    425 			return(1);
    426 		}
    427 		if (ssz == -1) {
    428 			perror(curp->file);
    429 			break;
    430 		}
    431 		off += (size_t)ssz;
    432 	}
    433 
    434 	free(fb->buf);
    435 	fb->buf = NULL;
    436 	with_error = 1;
    437 	return(0);
    438 }
    439 
    440 
    441 static void
    442 fdesc(struct curparse *curp)
    443 {
    444 	struct buf	 ln, blk;
    445 	int		 i, pos, lnn, lnn_start, with_mmap, of;
    446 	enum rofferr	 re;
    447 	struct man	*man;
    448 	struct mdoc	*mdoc;
    449 	struct roff	*roff;
    450 
    451 	man = NULL;
    452 	mdoc = NULL;
    453 	roff = NULL;
    454 	memset(&ln, 0, sizeof(struct buf));
    455 
    456 	/*
    457 	 * Two buffers: ln and buf.  buf is the input file and may be
    458 	 * memory mapped.  ln is a line buffer and grows on-demand.
    459 	 */
    460 
    461 	if ( ! read_whole_file(curp, &blk, &with_mmap))
    462 		return;
    463 
    464 	if (NULL == curp->roff)
    465 		curp->roff = roff_init(curp);
    466 	if (NULL == (roff = curp->roff))
    467 		goto bailout;
    468 
    469 	for (i = 0, lnn = 1; i < (int)blk.sz;) {
    470 		pos = 0;
    471 		lnn_start = lnn;
    472 		while (i < (int)blk.sz) {
    473 			if ('\n' == blk.buf[i]) {
    474 				++i;
    475 				++lnn;
    476 				break;
    477 			}
    478 			/* Trailing backslash is like a plain character. */
    479 			if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
    480 				if (pos >= (int)ln.sz)
    481 					if (! resize_buf(&ln, 256))
    482 						goto bailout;
    483 				ln.buf[pos++] = blk.buf[i++];
    484 				continue;
    485 			}
    486 			/* Found an escape and at least one other character. */
    487 			if ('\n' == blk.buf[i + 1]) {
    488 				/* Escaped newlines are skipped over */
    489 				i += 2;
    490 				++lnn;
    491 				continue;
    492 			}
    493 			if ('"' == blk.buf[i + 1]) {
    494 				i += 2;
    495 				/* Comment, skip to end of line */
    496 				for (; i < (int)blk.sz; ++i) {
    497 					if ('\n' == blk.buf[i]) {
    498 						++i;
    499 						++lnn;
    500 						break;
    501 					}
    502 				}
    503 				/* Backout trailing whitespaces */
    504 				for (; pos > 0; --pos) {
    505 					if (ln.buf[pos - 1] != ' ')
    506 						break;
    507 					if (pos > 2 && ln.buf[pos - 2] == '\\')
    508 						break;
    509 				}
    510 				break;
    511 			}
    512 			/* Some other escape sequence, copy and continue. */
    513 			if (pos + 1 >= (int)ln.sz)
    514 				if (! resize_buf(&ln, 256))
    515 					goto bailout;
    516 
    517 			ln.buf[pos++] = blk.buf[i++];
    518 			ln.buf[pos++] = blk.buf[i++];
    519 		}
    520 
    521  		if (pos >= (int)ln.sz)
    522 			if (! resize_buf(&ln, 256))
    523 				goto bailout;
    524 		ln.buf[pos] = '\0';
    525 
    526 		/*
    527 		 * A significant amount of complexity is contained by
    528 		 * the roff preprocessor.  It's line-oriented but can be
    529 		 * expressed on one line, so we need at times to
    530 		 * readjust our starting point and re-run it.  The roff
    531 		 * preprocessor can also readjust the buffers with new
    532 		 * data, so we pass them in wholesale.
    533 		 */
    534 
    535 		of = 0;
    536 		do {
    537 			re = roff_parseln(roff, lnn_start,
    538 					&ln.buf, &ln.sz, of, &of);
    539 		} while (ROFF_RERUN == re);
    540 
    541 		if (ROFF_IGN == re)
    542 			continue;
    543 		else if (ROFF_ERR == re)
    544 			goto bailout;
    545 
    546 		/*
    547 		 * If input parsers have not been allocated, do so now.
    548 		 * We keep these instanced betwen parsers, but set them
    549 		 * locally per parse routine since we can use different
    550 		 * parsers with each one.
    551 		 */
    552 
    553 		if ( ! (man || mdoc))
    554 			if ( ! pset(ln.buf + of, pos - of, curp, &man, &mdoc))
    555 				goto bailout;
    556 
    557 		/* Lastly, push down into the parsers themselves. */
    558 
    559 		if (man && ! man_parseln(man, lnn_start, ln.buf, of))
    560 			goto bailout;
    561 		if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of))
    562 			goto bailout;
    563 	}
    564 
    565 	/* NOTE a parser may not have been assigned, yet. */
    566 
    567 	if ( ! (man || mdoc)) {
    568 		fprintf(stderr, "%s: Not a manual\n", curp->file);
    569 		goto bailout;
    570 	}
    571 
    572 	/* Clean up the parse routine ASTs. */
    573 
    574 	if (mdoc && ! mdoc_endparse(mdoc))
    575 		goto bailout;
    576 	if (man && ! man_endparse(man))
    577 		goto bailout;
    578 	if (roff && ! roff_endparse(roff))
    579 		goto bailout;
    580 
    581 	/* If unset, allocate output dev now (if applicable). */
    582 
    583 	if ( ! (curp->outman && curp->outmdoc)) {
    584 		switch (curp->outtype) {
    585 		case (OUTT_XHTML):
    586 			curp->outdata = xhtml_alloc(curp->outopts);
    587 			curp->outman = html_man;
    588 			curp->outmdoc = html_mdoc;
    589 			curp->outfree = html_free;
    590 			break;
    591 		case (OUTT_HTML):
    592 			curp->outdata = html_alloc(curp->outopts);
    593 			curp->outman = html_man;
    594 			curp->outmdoc = html_mdoc;
    595 			curp->outfree = html_free;
    596 			break;
    597 		case (OUTT_TREE):
    598 			curp->outman = tree_man;
    599 			curp->outmdoc = tree_mdoc;
    600 			break;
    601 		case (OUTT_LINT):
    602 			break;
    603 		default:
    604 			curp->outdata = ascii_alloc(80);
    605 			curp->outman = terminal_man;
    606 			curp->outmdoc = terminal_mdoc;
    607 			curp->outfree = terminal_free;
    608 			break;
    609 		}
    610 	}
    611 
    612 	/* Execute the out device, if it exists. */
    613 
    614 	if (man && curp->outman)
    615 		(*curp->outman)(curp->outdata, man);
    616 	if (mdoc && curp->outmdoc)
    617 		(*curp->outmdoc)(curp->outdata, mdoc);
    618 
    619  cleanup:
    620 	if (mdoc)
    621 		mdoc_reset(mdoc);
    622 	if (man)
    623 		man_reset(man);
    624 	if (roff)
    625 		roff_reset(roff);
    626 	if (ln.buf)
    627 		free(ln.buf);
    628 	if (with_mmap)
    629 		munmap(blk.buf, blk.sz);
    630 	else
    631 		free(blk.buf);
    632 
    633 	return;
    634 
    635  bailout:
    636 	with_error = 1;
    637 	goto cleanup;
    638 }
    639 
    640 
    641 static int
    642 pset(const char *buf, int pos, struct curparse *curp,
    643 		struct man **man, struct mdoc **mdoc)
    644 {
    645 	int		 i;
    646 
    647 	/*
    648 	 * Try to intuit which kind of manual parser should be used.  If
    649 	 * passed in by command-line (-man, -mdoc), then use that
    650 	 * explicitly.  If passed as -mandoc, then try to guess from the
    651 	 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
    652 	 * default to -man, which is more lenient.
    653 	 */
    654 
    655 	if ('.' == buf[0] || '\'' == buf[0]) {
    656 		for (i = 1; buf[i]; i++)
    657 			if (' ' != buf[i] && '\t' != buf[i])
    658 				break;
    659 		if (0 == buf[i])
    660 			return(1);
    661 	}
    662 
    663 	switch (curp->inttype) {
    664 	case (INTT_MDOC):
    665 		if (NULL == curp->mdoc)
    666 			curp->mdoc = mdoc_init(curp);
    667 		if (NULL == (*mdoc = curp->mdoc))
    668 			return(0);
    669 		return(1);
    670 	case (INTT_MAN):
    671 		if (NULL == curp->man)
    672 			curp->man = man_init(curp);
    673 		if (NULL == (*man = curp->man))
    674 			return(0);
    675 		return(1);
    676 	default:
    677 		break;
    678 	}
    679 
    680 	if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
    681 		if (NULL == curp->mdoc)
    682 			curp->mdoc = mdoc_init(curp);
    683 		if (NULL == (*mdoc = curp->mdoc))
    684 			return(0);
    685 		return(1);
    686 	}
    687 
    688 	if (NULL == curp->man)
    689 		curp->man = man_init(curp);
    690 	if (NULL == (*man = curp->man))
    691 		return(0);
    692 	return(1);
    693 }
    694 
    695 
    696 static int
    697 moptions(enum intt *tflags, char *arg)
    698 {
    699 
    700 	if (0 == strcmp(arg, "doc"))
    701 		*tflags = INTT_MDOC;
    702 	else if (0 == strcmp(arg, "andoc"))
    703 		*tflags = INTT_AUTO;
    704 	else if (0 == strcmp(arg, "an"))
    705 		*tflags = INTT_MAN;
    706 	else {
    707 		fprintf(stderr, "%s: Bad argument\n", arg);
    708 		return(0);
    709 	}
    710 
    711 	return(1);
    712 }
    713 
    714 
    715 static int
    716 toptions(struct curparse *curp, char *arg)
    717 {
    718 
    719 	if (0 == strcmp(arg, "ascii"))
    720 		curp->outtype = OUTT_ASCII;
    721 	else if (0 == strcmp(arg, "lint")) {
    722 		curp->outtype = OUTT_LINT;
    723 		curp->wflags |= WARN_WALL;
    724 		curp->fflags |= FL_STRICT;
    725 	}
    726 	else if (0 == strcmp(arg, "tree"))
    727 		curp->outtype = OUTT_TREE;
    728 	else if (0 == strcmp(arg, "html"))
    729 		curp->outtype = OUTT_HTML;
    730 	else if (0 == strcmp(arg, "xhtml"))
    731 		curp->outtype = OUTT_XHTML;
    732 	else {
    733 		fprintf(stderr, "%s: Bad argument\n", arg);
    734 		return(0);
    735 	}
    736 
    737 	return(1);
    738 }
    739 
    740 
    741 static int
    742 foptions(int *fflags, char *arg)
    743 {
    744 	char		*v, *o;
    745 	const char	*toks[8];
    746 
    747 	toks[0] = "ign-scope";
    748 	toks[1] = "no-ign-escape";
    749 	toks[2] = "no-ign-macro";
    750 	toks[3] = "ign-errors";
    751 	toks[4] = "strict";
    752 	toks[5] = "ign-escape";
    753 	toks[6] = NULL;
    754 
    755 	while (*arg) {
    756 		o = arg;
    757 		switch (getsubopt(&arg, UNCONST(toks), &v)) {
    758 		case (0):
    759 			*fflags |= FL_IGN_SCOPE;
    760 			break;
    761 		case (1):
    762 			*fflags |= FL_NIGN_ESCAPE;
    763 			break;
    764 		case (2):
    765 			*fflags |= FL_NIGN_MACRO;
    766 			break;
    767 		case (3):
    768 			*fflags |= FL_IGN_ERRORS;
    769 			break;
    770 		case (4):
    771 			*fflags |= FL_STRICT;
    772 			break;
    773 		case (5):
    774 			*fflags &= ~FL_NIGN_ESCAPE;
    775 			break;
    776 		default:
    777 			fprintf(stderr, "%s: Bad argument\n", o);
    778 			return(0);
    779 		}
    780 	}
    781 
    782 	return(1);
    783 }
    784 
    785 
    786 static int
    787 woptions(int *wflags, char *arg)
    788 {
    789 	char		*v, *o;
    790 	const char	*toks[3];
    791 
    792 	toks[0] = "all";
    793 	toks[1] = "error";
    794 	toks[2] = NULL;
    795 
    796 	while (*arg) {
    797 		o = arg;
    798 		switch (getsubopt(&arg, UNCONST(toks), &v)) {
    799 		case (0):
    800 			*wflags |= WARN_WALL;
    801 			break;
    802 		case (1):
    803 			*wflags |= WARN_WERR;
    804 			break;
    805 		default:
    806 			fprintf(stderr, "%s: Bad argument\n", o);
    807 			return(0);
    808 		}
    809 	}
    810 
    811 	return(1);
    812 }
    813 
    814 
    815 static int
    816 mmsg(enum mandocerr t, void *arg, int ln, int col, const char *msg)
    817 {
    818 	struct curparse *cp;
    819 
    820 	cp = (struct curparse *)arg;
    821 
    822 	if (t <= MANDOCERR_ERROR) {
    823 		if ( ! (cp->wflags & WARN_WALL))
    824 			return(1);
    825 		with_warning = 1;
    826 	} else
    827 		with_error = 1;
    828 
    829 	fprintf(stderr, "%s:%d:%d: %s", cp->file,
    830 			ln, col + 1, mandocerrs[t]);
    831 
    832 	if (msg)
    833 		fprintf(stderr, ": %s", msg);
    834 
    835 	fputc('\n', stderr);
    836 
    837 	/* This is superfluous, but whatever. */
    838 	if (t > MANDOCERR_ERROR)
    839 		return(0);
    840 	if (cp->wflags & WARN_WERR) {
    841 		with_error = 1;
    842 		return(0);
    843 	}
    844 	return(1);
    845 }
    846