xref: /titanic_44/usr/src/cmd/mandoc/read.c (revision 698f87a48e2e945bfe5493ce168e0d0ae1cedd5c)
1*698f87a4SGarrett D'Amore /*	$Id: read.c,v 1.39 2013/09/16 00:25:07 schwarze Exp $ */
295c635efSGarrett D'Amore /*
395c635efSGarrett D'Amore  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4*698f87a4SGarrett D'Amore  * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
595c635efSGarrett D'Amore  *
695c635efSGarrett D'Amore  * Permission to use, copy, modify, and distribute this software for any
795c635efSGarrett D'Amore  * purpose with or without fee is hereby granted, provided that the above
895c635efSGarrett D'Amore  * copyright notice and this permission notice appear in all copies.
995c635efSGarrett D'Amore  *
1095c635efSGarrett D'Amore  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1195c635efSGarrett D'Amore  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1295c635efSGarrett D'Amore  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1395c635efSGarrett D'Amore  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1495c635efSGarrett D'Amore  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1595c635efSGarrett D'Amore  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1695c635efSGarrett D'Amore  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1795c635efSGarrett D'Amore  */
1895c635efSGarrett D'Amore #ifdef HAVE_CONFIG_H
1995c635efSGarrett D'Amore #include "config.h"
2095c635efSGarrett D'Amore #endif
2195c635efSGarrett D'Amore 
2295c635efSGarrett D'Amore #ifdef HAVE_MMAP
2395c635efSGarrett D'Amore # include <sys/stat.h>
2495c635efSGarrett D'Amore # include <sys/mman.h>
2595c635efSGarrett D'Amore #endif
2695c635efSGarrett D'Amore 
2795c635efSGarrett D'Amore #include <assert.h>
2895c635efSGarrett D'Amore #include <ctype.h>
2995c635efSGarrett D'Amore #include <fcntl.h>
3095c635efSGarrett D'Amore #include <stdarg.h>
3195c635efSGarrett D'Amore #include <stdint.h>
3295c635efSGarrett D'Amore #include <stdio.h>
3395c635efSGarrett D'Amore #include <stdlib.h>
3495c635efSGarrett D'Amore #include <string.h>
3595c635efSGarrett D'Amore #include <unistd.h>
3695c635efSGarrett D'Amore 
3795c635efSGarrett D'Amore #include "mandoc.h"
3895c635efSGarrett D'Amore #include "libmandoc.h"
3995c635efSGarrett D'Amore #include "mdoc.h"
4095c635efSGarrett D'Amore #include "man.h"
4195c635efSGarrett D'Amore #include "main.h"
4295c635efSGarrett D'Amore 
4395c635efSGarrett D'Amore #define	REPARSE_LIMIT	1000
4495c635efSGarrett D'Amore 
4595c635efSGarrett D'Amore struct	buf {
4695c635efSGarrett D'Amore 	char	 	 *buf; /* binary input buffer */
4795c635efSGarrett D'Amore 	size_t		  sz; /* size of binary buffer */
4895c635efSGarrett D'Amore };
4995c635efSGarrett D'Amore 
5095c635efSGarrett D'Amore struct	mparse {
5195c635efSGarrett D'Amore 	enum mandoclevel  file_status; /* status of current parse */
5295c635efSGarrett D'Amore 	enum mandoclevel  wlevel; /* ignore messages below this */
5395c635efSGarrett D'Amore 	int		  line; /* line number in the file */
5495c635efSGarrett D'Amore 	enum mparset	  inttype; /* which parser to use */
5595c635efSGarrett D'Amore 	struct man	 *pman; /* persistent man parser */
5695c635efSGarrett D'Amore 	struct mdoc	 *pmdoc; /* persistent mdoc parser */
5795c635efSGarrett D'Amore 	struct man	 *man; /* man parser */
5895c635efSGarrett D'Amore 	struct mdoc	 *mdoc; /* mdoc parser */
5995c635efSGarrett D'Amore 	struct roff	 *roff; /* roff parser (!NULL) */
6095c635efSGarrett D'Amore 	int		  reparse_count; /* finite interp. stack */
6195c635efSGarrett D'Amore 	mandocmsg	  mmsg; /* warning/error message handler */
6295c635efSGarrett D'Amore 	void		 *arg; /* argument to mmsg */
6395c635efSGarrett D'Amore 	const char	 *file;
6495c635efSGarrett D'Amore 	struct buf	 *secondary;
65*698f87a4SGarrett D'Amore 	char		 *defos; /* default operating system */
6695c635efSGarrett D'Amore };
6795c635efSGarrett D'Amore 
6895c635efSGarrett D'Amore static	void	  resize_buf(struct buf *, size_t);
6995c635efSGarrett D'Amore static	void	  mparse_buf_r(struct mparse *, struct buf, int);
7095c635efSGarrett D'Amore static	void	  pset(const char *, int, struct mparse *);
7195c635efSGarrett D'Amore static	int	  read_whole_file(const char *, int, struct buf *, int *);
7295c635efSGarrett D'Amore static	void	  mparse_end(struct mparse *);
73*698f87a4SGarrett D'Amore static	void	  mparse_parse_buffer(struct mparse *, struct buf,
74*698f87a4SGarrett D'Amore 			const char *);
7595c635efSGarrett D'Amore 
7695c635efSGarrett D'Amore static	const enum mandocerr	mandoclimits[MANDOCLEVEL_MAX] = {
7795c635efSGarrett D'Amore 	MANDOCERR_OK,
7895c635efSGarrett D'Amore 	MANDOCERR_WARNING,
7995c635efSGarrett D'Amore 	MANDOCERR_WARNING,
8095c635efSGarrett D'Amore 	MANDOCERR_ERROR,
8195c635efSGarrett D'Amore 	MANDOCERR_FATAL,
8295c635efSGarrett D'Amore 	MANDOCERR_MAX,
8395c635efSGarrett D'Amore 	MANDOCERR_MAX
8495c635efSGarrett D'Amore };
8595c635efSGarrett D'Amore 
8695c635efSGarrett D'Amore static	const char * const	mandocerrs[MANDOCERR_MAX] = {
8795c635efSGarrett D'Amore 	"ok",
8895c635efSGarrett D'Amore 
8995c635efSGarrett D'Amore 	"generic warning",
9095c635efSGarrett D'Amore 
9195c635efSGarrett D'Amore 	/* related to the prologue */
9295c635efSGarrett D'Amore 	"no title in document",
9395c635efSGarrett D'Amore 	"document title should be all caps",
9495c635efSGarrett D'Amore 	"unknown manual section",
95*698f87a4SGarrett D'Amore 	"unknown manual volume or arch",
9695c635efSGarrett D'Amore 	"date missing, using today's date",
9795c635efSGarrett D'Amore 	"cannot parse date, using it verbatim",
9895c635efSGarrett D'Amore 	"prologue macros out of order",
9995c635efSGarrett D'Amore 	"duplicate prologue macro",
10095c635efSGarrett D'Amore 	"macro not allowed in prologue",
10195c635efSGarrett D'Amore 	"macro not allowed in body",
10295c635efSGarrett D'Amore 
10395c635efSGarrett D'Amore 	/* related to document structure */
10495c635efSGarrett D'Amore 	".so is fragile, better use ln(1)",
10595c635efSGarrett D'Amore 	"NAME section must come first",
10695c635efSGarrett D'Amore 	"bad NAME section contents",
10795c635efSGarrett D'Amore 	"sections out of conventional order",
10895c635efSGarrett D'Amore 	"duplicate section name",
109*698f87a4SGarrett D'Amore 	"section header suited to sections 2, 3, and 9 only",
11095c635efSGarrett D'Amore 
11195c635efSGarrett D'Amore 	/* related to macros and nesting */
11295c635efSGarrett D'Amore 	"skipping obsolete macro",
11395c635efSGarrett D'Amore 	"skipping paragraph macro",
114*698f87a4SGarrett D'Amore 	"moving paragraph macro out of list",
11595c635efSGarrett D'Amore 	"skipping no-space macro",
11695c635efSGarrett D'Amore 	"blocks badly nested",
11795c635efSGarrett D'Amore 	"child violates parent syntax",
11895c635efSGarrett D'Amore 	"nested displays are not portable",
11995c635efSGarrett D'Amore 	"already in literal mode",
12095c635efSGarrett D'Amore 	"line scope broken",
12195c635efSGarrett D'Amore 
12295c635efSGarrett D'Amore 	/* related to missing macro arguments */
12395c635efSGarrett D'Amore 	"skipping empty macro",
12495c635efSGarrett D'Amore 	"argument count wrong",
12595c635efSGarrett D'Amore 	"missing display type",
12695c635efSGarrett D'Amore 	"list type must come first",
12795c635efSGarrett D'Amore 	"tag lists require a width argument",
12895c635efSGarrett D'Amore 	"missing font type",
12995c635efSGarrett D'Amore 	"skipping end of block that is not open",
13095c635efSGarrett D'Amore 
13195c635efSGarrett D'Amore 	/* related to bad macro arguments */
13295c635efSGarrett D'Amore 	"skipping argument",
13395c635efSGarrett D'Amore 	"duplicate argument",
13495c635efSGarrett D'Amore 	"duplicate display type",
13595c635efSGarrett D'Amore 	"duplicate list type",
13695c635efSGarrett D'Amore 	"unknown AT&T UNIX version",
13795c635efSGarrett D'Amore 	"bad Boolean value",
13895c635efSGarrett D'Amore 	"unknown font",
13995c635efSGarrett D'Amore 	"unknown standard specifier",
14095c635efSGarrett D'Amore 	"bad width argument",
14195c635efSGarrett D'Amore 
14295c635efSGarrett D'Amore 	/* related to plain text */
14395c635efSGarrett D'Amore 	"blank line in non-literal context",
14495c635efSGarrett D'Amore 	"tab in non-literal context",
14595c635efSGarrett D'Amore 	"end of line whitespace",
14695c635efSGarrett D'Amore 	"bad comment style",
14795c635efSGarrett D'Amore 	"bad escape sequence",
14895c635efSGarrett D'Amore 	"unterminated quoted string",
14995c635efSGarrett D'Amore 
15095c635efSGarrett D'Amore 	/* related to equations */
15195c635efSGarrett D'Amore 	"unexpected literal in equation",
15295c635efSGarrett D'Amore 
15395c635efSGarrett D'Amore 	"generic error",
15495c635efSGarrett D'Amore 
15595c635efSGarrett D'Amore 	/* related to equations */
15695c635efSGarrett D'Amore 	"unexpected equation scope closure",
15795c635efSGarrett D'Amore 	"equation scope open on exit",
15895c635efSGarrett D'Amore 	"overlapping equation scopes",
15995c635efSGarrett D'Amore 	"unexpected end of equation",
16095c635efSGarrett D'Amore 	"equation syntax error",
16195c635efSGarrett D'Amore 
16295c635efSGarrett D'Amore 	/* related to tables */
16395c635efSGarrett D'Amore 	"bad table syntax",
16495c635efSGarrett D'Amore 	"bad table option",
16595c635efSGarrett D'Amore 	"bad table layout",
16695c635efSGarrett D'Amore 	"no table layout cells specified",
16795c635efSGarrett D'Amore 	"no table data cells specified",
16895c635efSGarrett D'Amore 	"ignore data in cell",
16995c635efSGarrett D'Amore 	"data block still open",
17095c635efSGarrett D'Amore 	"ignoring extra data cells",
17195c635efSGarrett D'Amore 
17295c635efSGarrett D'Amore 	"input stack limit exceeded, infinite loop?",
17395c635efSGarrett D'Amore 	"skipping bad character",
17495c635efSGarrett D'Amore 	"escaped character not allowed in a name",
175*698f87a4SGarrett D'Amore 	"manual name not yet set",
17695c635efSGarrett D'Amore 	"skipping text before the first section header",
17795c635efSGarrett D'Amore 	"skipping unknown macro",
17895c635efSGarrett D'Amore 	"NOT IMPLEMENTED, please use groff: skipping request",
17995c635efSGarrett D'Amore 	"argument count wrong",
180*698f87a4SGarrett D'Amore 	"skipping column outside column list",
18195c635efSGarrett D'Amore 	"skipping end of block that is not open",
18295c635efSGarrett D'Amore 	"missing end of block",
18395c635efSGarrett D'Amore 	"scope open on exit",
18495c635efSGarrett D'Amore 	"uname(3) system call failed",
18595c635efSGarrett D'Amore 	"macro requires line argument(s)",
18695c635efSGarrett D'Amore 	"macro requires body argument(s)",
18795c635efSGarrett D'Amore 	"macro requires argument(s)",
188*698f87a4SGarrett D'Amore 	"request requires a numeric argument",
18995c635efSGarrett D'Amore 	"missing list type",
19095c635efSGarrett D'Amore 	"line argument(s) will be lost",
19195c635efSGarrett D'Amore 	"body argument(s) will be lost",
19295c635efSGarrett D'Amore 
19395c635efSGarrett D'Amore 	"generic fatal error",
19495c635efSGarrett D'Amore 
19595c635efSGarrett D'Amore 	"not a manual",
19695c635efSGarrett D'Amore 	"column syntax is inconsistent",
19795c635efSGarrett D'Amore 	"NOT IMPLEMENTED: .Bd -file",
19895c635efSGarrett D'Amore 	"argument count wrong, violates syntax",
19995c635efSGarrett D'Amore 	"child violates parent syntax",
20095c635efSGarrett D'Amore 	"argument count wrong, violates syntax",
20195c635efSGarrett D'Amore 	"NOT IMPLEMENTED: .so with absolute path or \"..\"",
20295c635efSGarrett D'Amore 	"no document body",
20395c635efSGarrett D'Amore 	"no document prologue",
20495c635efSGarrett D'Amore 	"static buffer exhausted",
20595c635efSGarrett D'Amore };
20695c635efSGarrett D'Amore 
20795c635efSGarrett D'Amore static	const char * const	mandoclevels[MANDOCLEVEL_MAX] = {
20895c635efSGarrett D'Amore 	"SUCCESS",
20995c635efSGarrett D'Amore 	"RESERVED",
21095c635efSGarrett D'Amore 	"WARNING",
21195c635efSGarrett D'Amore 	"ERROR",
21295c635efSGarrett D'Amore 	"FATAL",
21395c635efSGarrett D'Amore 	"BADARG",
21495c635efSGarrett D'Amore 	"SYSERR"
21595c635efSGarrett D'Amore };
21695c635efSGarrett D'Amore 
21795c635efSGarrett D'Amore static void
resize_buf(struct buf * buf,size_t initial)21895c635efSGarrett D'Amore resize_buf(struct buf *buf, size_t initial)
21995c635efSGarrett D'Amore {
22095c635efSGarrett D'Amore 
22195c635efSGarrett D'Amore 	buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
22295c635efSGarrett D'Amore 	buf->buf = mandoc_realloc(buf->buf, buf->sz);
22395c635efSGarrett D'Amore }
22495c635efSGarrett D'Amore 
22595c635efSGarrett D'Amore static void
pset(const char * buf,int pos,struct mparse * curp)22695c635efSGarrett D'Amore pset(const char *buf, int pos, struct mparse *curp)
22795c635efSGarrett D'Amore {
22895c635efSGarrett D'Amore 	int		 i;
22995c635efSGarrett D'Amore 
23095c635efSGarrett D'Amore 	/*
23195c635efSGarrett D'Amore 	 * Try to intuit which kind of manual parser should be used.  If
23295c635efSGarrett D'Amore 	 * passed in by command-line (-man, -mdoc), then use that
23395c635efSGarrett D'Amore 	 * explicitly.  If passed as -mandoc, then try to guess from the
23495c635efSGarrett D'Amore 	 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
23595c635efSGarrett D'Amore 	 * default to -man, which is more lenient.
23695c635efSGarrett D'Amore 	 *
23795c635efSGarrett D'Amore 	 * Separate out pmdoc/pman from mdoc/man: the first persists
23895c635efSGarrett D'Amore 	 * through all parsers, while the latter is used per-parse.
23995c635efSGarrett D'Amore 	 */
24095c635efSGarrett D'Amore 
24195c635efSGarrett D'Amore 	if ('.' == buf[0] || '\'' == buf[0]) {
24295c635efSGarrett D'Amore 		for (i = 1; buf[i]; i++)
24395c635efSGarrett D'Amore 			if (' ' != buf[i] && '\t' != buf[i])
24495c635efSGarrett D'Amore 				break;
24595c635efSGarrett D'Amore 		if ('\0' == buf[i])
24695c635efSGarrett D'Amore 			return;
24795c635efSGarrett D'Amore 	}
24895c635efSGarrett D'Amore 
24995c635efSGarrett D'Amore 	switch (curp->inttype) {
25095c635efSGarrett D'Amore 	case (MPARSE_MDOC):
25195c635efSGarrett D'Amore 		if (NULL == curp->pmdoc)
252*698f87a4SGarrett D'Amore 			curp->pmdoc = mdoc_alloc(curp->roff, curp,
253*698f87a4SGarrett D'Amore 					curp->defos);
25495c635efSGarrett D'Amore 		assert(curp->pmdoc);
25595c635efSGarrett D'Amore 		curp->mdoc = curp->pmdoc;
25695c635efSGarrett D'Amore 		return;
25795c635efSGarrett D'Amore 	case (MPARSE_MAN):
25895c635efSGarrett D'Amore 		if (NULL == curp->pman)
25995c635efSGarrett D'Amore 			curp->pman = man_alloc(curp->roff, curp);
26095c635efSGarrett D'Amore 		assert(curp->pman);
26195c635efSGarrett D'Amore 		curp->man = curp->pman;
26295c635efSGarrett D'Amore 		return;
26395c635efSGarrett D'Amore 	default:
26495c635efSGarrett D'Amore 		break;
26595c635efSGarrett D'Amore 	}
26695c635efSGarrett D'Amore 
26795c635efSGarrett D'Amore 	if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
26895c635efSGarrett D'Amore 		if (NULL == curp->pmdoc)
269*698f87a4SGarrett D'Amore 			curp->pmdoc = mdoc_alloc(curp->roff, curp,
270*698f87a4SGarrett D'Amore 					curp->defos);
27195c635efSGarrett D'Amore 		assert(curp->pmdoc);
27295c635efSGarrett D'Amore 		curp->mdoc = curp->pmdoc;
27395c635efSGarrett D'Amore 		return;
27495c635efSGarrett D'Amore 	}
27595c635efSGarrett D'Amore 
27695c635efSGarrett D'Amore 	if (NULL == curp->pman)
27795c635efSGarrett D'Amore 		curp->pman = man_alloc(curp->roff, curp);
27895c635efSGarrett D'Amore 	assert(curp->pman);
27995c635efSGarrett D'Amore 	curp->man = curp->pman;
28095c635efSGarrett D'Amore }
28195c635efSGarrett D'Amore 
28295c635efSGarrett D'Amore /*
28395c635efSGarrett D'Amore  * Main parse routine for an opened file.  This is called for each
28495c635efSGarrett D'Amore  * opened file and simply loops around the full input file, possibly
28595c635efSGarrett D'Amore  * nesting (i.e., with `so').
28695c635efSGarrett D'Amore  */
28795c635efSGarrett D'Amore static void
mparse_buf_r(struct mparse * curp,struct buf blk,int start)28895c635efSGarrett D'Amore mparse_buf_r(struct mparse *curp, struct buf blk, int start)
28995c635efSGarrett D'Amore {
29095c635efSGarrett D'Amore 	const struct tbl_span	*span;
29195c635efSGarrett D'Amore 	struct buf	 ln;
29295c635efSGarrett D'Amore 	enum rofferr	 rr;
29395c635efSGarrett D'Amore 	int		 i, of, rc;
29495c635efSGarrett D'Amore 	int		 pos; /* byte number in the ln buffer */
29595c635efSGarrett D'Amore 	int		 lnn; /* line number in the real file */
29695c635efSGarrett D'Amore 	unsigned char	 c;
29795c635efSGarrett D'Amore 
29895c635efSGarrett D'Amore 	memset(&ln, 0, sizeof(struct buf));
29995c635efSGarrett D'Amore 
30095c635efSGarrett D'Amore 	lnn = curp->line;
30195c635efSGarrett D'Amore 	pos = 0;
30295c635efSGarrett D'Amore 
30395c635efSGarrett D'Amore 	for (i = 0; i < (int)blk.sz; ) {
30495c635efSGarrett D'Amore 		if (0 == pos && '\0' == blk.buf[i])
30595c635efSGarrett D'Amore 			break;
30695c635efSGarrett D'Amore 
30795c635efSGarrett D'Amore 		if (start) {
30895c635efSGarrett D'Amore 			curp->line = lnn;
30995c635efSGarrett D'Amore 			curp->reparse_count = 0;
31095c635efSGarrett D'Amore 		}
31195c635efSGarrett D'Amore 
31295c635efSGarrett D'Amore 		while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
31395c635efSGarrett D'Amore 
31495c635efSGarrett D'Amore 			/*
31595c635efSGarrett D'Amore 			 * When finding an unescaped newline character,
31695c635efSGarrett D'Amore 			 * leave the character loop to process the line.
31795c635efSGarrett D'Amore 			 * Skip a preceding carriage return, if any.
31895c635efSGarrett D'Amore 			 */
31995c635efSGarrett D'Amore 
32095c635efSGarrett D'Amore 			if ('\r' == blk.buf[i] && i + 1 < (int)blk.sz &&
32195c635efSGarrett D'Amore 			    '\n' == blk.buf[i + 1])
32295c635efSGarrett D'Amore 				++i;
32395c635efSGarrett D'Amore 			if ('\n' == blk.buf[i]) {
32495c635efSGarrett D'Amore 				++i;
32595c635efSGarrett D'Amore 				++lnn;
32695c635efSGarrett D'Amore 				break;
32795c635efSGarrett D'Amore 			}
32895c635efSGarrett D'Amore 
32995c635efSGarrett D'Amore 			/*
330*698f87a4SGarrett D'Amore 			 * Make sure we have space for at least
331*698f87a4SGarrett D'Amore 			 * one backslash and one other character
332*698f87a4SGarrett D'Amore 			 * and the trailing NUL byte.
333*698f87a4SGarrett D'Amore 			 */
334*698f87a4SGarrett D'Amore 
335*698f87a4SGarrett D'Amore 			if (pos + 2 >= (int)ln.sz)
336*698f87a4SGarrett D'Amore 				resize_buf(&ln, 256);
337*698f87a4SGarrett D'Amore 
338*698f87a4SGarrett D'Amore 			/*
33995c635efSGarrett D'Amore 			 * Warn about bogus characters.  If you're using
34095c635efSGarrett D'Amore 			 * non-ASCII encoding, you're screwing your
34195c635efSGarrett D'Amore 			 * readers.  Since I'd rather this not happen,
34295c635efSGarrett D'Amore 			 * I'll be helpful and replace these characters
34395c635efSGarrett D'Amore 			 * with "?", so we don't display gibberish.
34495c635efSGarrett D'Amore 			 * Note to manual writers: use special characters.
34595c635efSGarrett D'Amore 			 */
34695c635efSGarrett D'Amore 
34795c635efSGarrett D'Amore 			c = (unsigned char) blk.buf[i];
34895c635efSGarrett D'Amore 
34995c635efSGarrett D'Amore 			if ( ! (isascii(c) &&
35095c635efSGarrett D'Amore 					(isgraph(c) || isblank(c)))) {
35195c635efSGarrett D'Amore 				mandoc_msg(MANDOCERR_BADCHAR, curp,
35295c635efSGarrett D'Amore 						curp->line, pos, NULL);
35395c635efSGarrett D'Amore 				i++;
35495c635efSGarrett D'Amore 				ln.buf[pos++] = '?';
35595c635efSGarrett D'Amore 				continue;
35695c635efSGarrett D'Amore 			}
35795c635efSGarrett D'Amore 
35895c635efSGarrett D'Amore 			/* Trailing backslash = a plain char. */
35995c635efSGarrett D'Amore 
36095c635efSGarrett D'Amore 			if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
36195c635efSGarrett D'Amore 				ln.buf[pos++] = blk.buf[i++];
36295c635efSGarrett D'Amore 				continue;
36395c635efSGarrett D'Amore 			}
36495c635efSGarrett D'Amore 
36595c635efSGarrett D'Amore 			/*
36695c635efSGarrett D'Amore 			 * Found escape and at least one other character.
36795c635efSGarrett D'Amore 			 * When it's a newline character, skip it.
36895c635efSGarrett D'Amore 			 * When there is a carriage return in between,
36995c635efSGarrett D'Amore 			 * skip that one as well.
37095c635efSGarrett D'Amore 			 */
37195c635efSGarrett D'Amore 
37295c635efSGarrett D'Amore 			if ('\r' == blk.buf[i + 1] && i + 2 < (int)blk.sz &&
37395c635efSGarrett D'Amore 			    '\n' == blk.buf[i + 2])
37495c635efSGarrett D'Amore 				++i;
37595c635efSGarrett D'Amore 			if ('\n' == blk.buf[i + 1]) {
37695c635efSGarrett D'Amore 				i += 2;
37795c635efSGarrett D'Amore 				++lnn;
37895c635efSGarrett D'Amore 				continue;
37995c635efSGarrett D'Amore 			}
38095c635efSGarrett D'Amore 
38195c635efSGarrett D'Amore 			if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) {
38295c635efSGarrett D'Amore 				i += 2;
38395c635efSGarrett D'Amore 				/* Comment, skip to end of line */
38495c635efSGarrett D'Amore 				for (; i < (int)blk.sz; ++i) {
38595c635efSGarrett D'Amore 					if ('\n' == blk.buf[i]) {
38695c635efSGarrett D'Amore 						++i;
38795c635efSGarrett D'Amore 						++lnn;
38895c635efSGarrett D'Amore 						break;
38995c635efSGarrett D'Amore 					}
39095c635efSGarrett D'Amore 				}
39195c635efSGarrett D'Amore 
39295c635efSGarrett D'Amore 				/* Backout trailing whitespaces */
39395c635efSGarrett D'Amore 				for (; pos > 0; --pos) {
39495c635efSGarrett D'Amore 					if (ln.buf[pos - 1] != ' ')
39595c635efSGarrett D'Amore 						break;
39695c635efSGarrett D'Amore 					if (pos > 2 && ln.buf[pos - 2] == '\\')
39795c635efSGarrett D'Amore 						break;
39895c635efSGarrett D'Amore 				}
39995c635efSGarrett D'Amore 				break;
40095c635efSGarrett D'Amore 			}
40195c635efSGarrett D'Amore 
402*698f87a4SGarrett D'Amore 			/* Catch escaped bogus characters. */
40395c635efSGarrett D'Amore 
404*698f87a4SGarrett D'Amore 			c = (unsigned char) blk.buf[i+1];
405*698f87a4SGarrett D'Amore 
406*698f87a4SGarrett D'Amore 			if ( ! (isascii(c) &&
407*698f87a4SGarrett D'Amore 					(isgraph(c) || isblank(c)))) {
408*698f87a4SGarrett D'Amore 				mandoc_msg(MANDOCERR_BADCHAR, curp,
409*698f87a4SGarrett D'Amore 						curp->line, pos, NULL);
410*698f87a4SGarrett D'Amore 				i += 2;
411*698f87a4SGarrett D'Amore 				ln.buf[pos++] = '?';
412*698f87a4SGarrett D'Amore 				continue;
413*698f87a4SGarrett D'Amore 			}
414*698f87a4SGarrett D'Amore 
415*698f87a4SGarrett D'Amore 			/* Some other escape sequence, copy & cont. */
41695c635efSGarrett D'Amore 
41795c635efSGarrett D'Amore 			ln.buf[pos++] = blk.buf[i++];
41895c635efSGarrett D'Amore 			ln.buf[pos++] = blk.buf[i++];
41995c635efSGarrett D'Amore 		}
42095c635efSGarrett D'Amore 
42195c635efSGarrett D'Amore  		if (pos >= (int)ln.sz)
42295c635efSGarrett D'Amore 			resize_buf(&ln, 256);
42395c635efSGarrett D'Amore 
42495c635efSGarrett D'Amore 		ln.buf[pos] = '\0';
42595c635efSGarrett D'Amore 
42695c635efSGarrett D'Amore 		/*
42795c635efSGarrett D'Amore 		 * A significant amount of complexity is contained by
42895c635efSGarrett D'Amore 		 * the roff preprocessor.  It's line-oriented but can be
42995c635efSGarrett D'Amore 		 * expressed on one line, so we need at times to
43095c635efSGarrett D'Amore 		 * readjust our starting point and re-run it.  The roff
43195c635efSGarrett D'Amore 		 * preprocessor can also readjust the buffers with new
43295c635efSGarrett D'Amore 		 * data, so we pass them in wholesale.
43395c635efSGarrett D'Amore 		 */
43495c635efSGarrett D'Amore 
43595c635efSGarrett D'Amore 		of = 0;
43695c635efSGarrett D'Amore 
43795c635efSGarrett D'Amore 		/*
43895c635efSGarrett D'Amore 		 * Maintain a lookaside buffer of all parsed lines.  We
43995c635efSGarrett D'Amore 		 * only do this if mparse_keep() has been invoked (the
44095c635efSGarrett D'Amore 		 * buffer may be accessed with mparse_getkeep()).
44195c635efSGarrett D'Amore 		 */
44295c635efSGarrett D'Amore 
44395c635efSGarrett D'Amore 		if (curp->secondary) {
44495c635efSGarrett D'Amore 			curp->secondary->buf =
44595c635efSGarrett D'Amore 				mandoc_realloc
44695c635efSGarrett D'Amore 				(curp->secondary->buf,
44795c635efSGarrett D'Amore 				 curp->secondary->sz + pos + 2);
44895c635efSGarrett D'Amore 			memcpy(curp->secondary->buf +
44995c635efSGarrett D'Amore 					curp->secondary->sz,
45095c635efSGarrett D'Amore 					ln.buf, pos);
45195c635efSGarrett D'Amore 			curp->secondary->sz += pos;
45295c635efSGarrett D'Amore 			curp->secondary->buf
45395c635efSGarrett D'Amore 				[curp->secondary->sz] = '\n';
45495c635efSGarrett D'Amore 			curp->secondary->sz++;
45595c635efSGarrett D'Amore 			curp->secondary->buf
45695c635efSGarrett D'Amore 				[curp->secondary->sz] = '\0';
45795c635efSGarrett D'Amore 		}
45895c635efSGarrett D'Amore rerun:
45995c635efSGarrett D'Amore 		rr = roff_parseln
46095c635efSGarrett D'Amore 			(curp->roff, curp->line,
46195c635efSGarrett D'Amore 			 &ln.buf, &ln.sz, of, &of);
46295c635efSGarrett D'Amore 
46395c635efSGarrett D'Amore 		switch (rr) {
46495c635efSGarrett D'Amore 		case (ROFF_REPARSE):
46595c635efSGarrett D'Amore 			if (REPARSE_LIMIT >= ++curp->reparse_count)
46695c635efSGarrett D'Amore 				mparse_buf_r(curp, ln, 0);
46795c635efSGarrett D'Amore 			else
46895c635efSGarrett D'Amore 				mandoc_msg(MANDOCERR_ROFFLOOP, curp,
46995c635efSGarrett D'Amore 					curp->line, pos, NULL);
47095c635efSGarrett D'Amore 			pos = 0;
47195c635efSGarrett D'Amore 			continue;
47295c635efSGarrett D'Amore 		case (ROFF_APPEND):
47395c635efSGarrett D'Amore 			pos = (int)strlen(ln.buf);
47495c635efSGarrett D'Amore 			continue;
47595c635efSGarrett D'Amore 		case (ROFF_RERUN):
47695c635efSGarrett D'Amore 			goto rerun;
47795c635efSGarrett D'Amore 		case (ROFF_IGN):
47895c635efSGarrett D'Amore 			pos = 0;
47995c635efSGarrett D'Amore 			continue;
48095c635efSGarrett D'Amore 		case (ROFF_ERR):
48195c635efSGarrett D'Amore 			assert(MANDOCLEVEL_FATAL <= curp->file_status);
48295c635efSGarrett D'Amore 			break;
48395c635efSGarrett D'Amore 		case (ROFF_SO):
48495c635efSGarrett D'Amore 			/*
48595c635efSGarrett D'Amore 			 * We remove `so' clauses from our lookaside
48695c635efSGarrett D'Amore 			 * buffer because we're going to descend into
48795c635efSGarrett D'Amore 			 * the file recursively.
48895c635efSGarrett D'Amore 			 */
48995c635efSGarrett D'Amore 			if (curp->secondary)
49095c635efSGarrett D'Amore 				curp->secondary->sz -= pos + 1;
491*698f87a4SGarrett D'Amore 			mparse_readfd(curp, -1, ln.buf + of);
49295c635efSGarrett D'Amore 			if (MANDOCLEVEL_FATAL <= curp->file_status)
49395c635efSGarrett D'Amore 				break;
49495c635efSGarrett D'Amore 			pos = 0;
49595c635efSGarrett D'Amore 			continue;
49695c635efSGarrett D'Amore 		default:
49795c635efSGarrett D'Amore 			break;
49895c635efSGarrett D'Amore 		}
49995c635efSGarrett D'Amore 
50095c635efSGarrett D'Amore 		/*
50195c635efSGarrett D'Amore 		 * If we encounter errors in the recursive parse, make
50295c635efSGarrett D'Amore 		 * sure we don't continue parsing.
50395c635efSGarrett D'Amore 		 */
50495c635efSGarrett D'Amore 
50595c635efSGarrett D'Amore 		if (MANDOCLEVEL_FATAL <= curp->file_status)
50695c635efSGarrett D'Amore 			break;
50795c635efSGarrett D'Amore 
50895c635efSGarrett D'Amore 		/*
50995c635efSGarrett D'Amore 		 * If input parsers have not been allocated, do so now.
51095c635efSGarrett D'Amore 		 * We keep these instanced between parsers, but set them
51195c635efSGarrett D'Amore 		 * locally per parse routine since we can use different
51295c635efSGarrett D'Amore 		 * parsers with each one.
51395c635efSGarrett D'Amore 		 */
51495c635efSGarrett D'Amore 
51595c635efSGarrett D'Amore 		if ( ! (curp->man || curp->mdoc))
51695c635efSGarrett D'Amore 			pset(ln.buf + of, pos - of, curp);
51795c635efSGarrett D'Amore 
51895c635efSGarrett D'Amore 		/*
51995c635efSGarrett D'Amore 		 * Lastly, push down into the parsers themselves.  One
52095c635efSGarrett D'Amore 		 * of these will have already been set in the pset()
52195c635efSGarrett D'Amore 		 * routine.
52295c635efSGarrett D'Amore 		 * If libroff returns ROFF_TBL, then add it to the
52395c635efSGarrett D'Amore 		 * currently open parse.  Since we only get here if
52495c635efSGarrett D'Amore 		 * there does exist data (see tbl_data.c), we're
52595c635efSGarrett D'Amore 		 * guaranteed that something's been allocated.
52695c635efSGarrett D'Amore 		 * Do the same for ROFF_EQN.
52795c635efSGarrett D'Amore 		 */
52895c635efSGarrett D'Amore 
52995c635efSGarrett D'Amore 		rc = -1;
53095c635efSGarrett D'Amore 
53195c635efSGarrett D'Amore 		if (ROFF_TBL == rr)
53295c635efSGarrett D'Amore 			while (NULL != (span = roff_span(curp->roff))) {
53395c635efSGarrett D'Amore 				rc = curp->man ?
53495c635efSGarrett D'Amore 					man_addspan(curp->man, span) :
53595c635efSGarrett D'Amore 					mdoc_addspan(curp->mdoc, span);
53695c635efSGarrett D'Amore 				if (0 == rc)
53795c635efSGarrett D'Amore 					break;
53895c635efSGarrett D'Amore 			}
53995c635efSGarrett D'Amore 		else if (ROFF_EQN == rr)
54095c635efSGarrett D'Amore 			rc = curp->mdoc ?
54195c635efSGarrett D'Amore 				mdoc_addeqn(curp->mdoc,
54295c635efSGarrett D'Amore 					roff_eqn(curp->roff)) :
54395c635efSGarrett D'Amore 				man_addeqn(curp->man,
54495c635efSGarrett D'Amore 					roff_eqn(curp->roff));
54595c635efSGarrett D'Amore 		else if (curp->man || curp->mdoc)
54695c635efSGarrett D'Amore 			rc = curp->man ?
54795c635efSGarrett D'Amore 				man_parseln(curp->man,
54895c635efSGarrett D'Amore 					curp->line, ln.buf, of) :
54995c635efSGarrett D'Amore 				mdoc_parseln(curp->mdoc,
55095c635efSGarrett D'Amore 					curp->line, ln.buf, of);
55195c635efSGarrett D'Amore 
55295c635efSGarrett D'Amore 		if (0 == rc) {
55395c635efSGarrett D'Amore 			assert(MANDOCLEVEL_FATAL <= curp->file_status);
55495c635efSGarrett D'Amore 			break;
55595c635efSGarrett D'Amore 		}
55695c635efSGarrett D'Amore 
55795c635efSGarrett D'Amore 		/* Temporary buffers typically are not full. */
55895c635efSGarrett D'Amore 
55995c635efSGarrett D'Amore 		if (0 == start && '\0' == blk.buf[i])
56095c635efSGarrett D'Amore 			break;
56195c635efSGarrett D'Amore 
56295c635efSGarrett D'Amore 		/* Start the next input line. */
56395c635efSGarrett D'Amore 
56495c635efSGarrett D'Amore 		pos = 0;
56595c635efSGarrett D'Amore 	}
56695c635efSGarrett D'Amore 
56795c635efSGarrett D'Amore 	free(ln.buf);
56895c635efSGarrett D'Amore }
56995c635efSGarrett D'Amore 
57095c635efSGarrett D'Amore static int
read_whole_file(const char * file,int fd,struct buf * fb,int * with_mmap)57195c635efSGarrett D'Amore read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
57295c635efSGarrett D'Amore {
57395c635efSGarrett D'Amore 	size_t		 off;
57495c635efSGarrett D'Amore 	ssize_t		 ssz;
57595c635efSGarrett D'Amore 
57695c635efSGarrett D'Amore #ifdef	HAVE_MMAP
57795c635efSGarrett D'Amore 	struct stat	 st;
57895c635efSGarrett D'Amore 	if (-1 == fstat(fd, &st)) {
57995c635efSGarrett D'Amore 		perror(file);
58095c635efSGarrett D'Amore 		return(0);
58195c635efSGarrett D'Amore 	}
58295c635efSGarrett D'Amore 
58395c635efSGarrett D'Amore 	/*
58495c635efSGarrett D'Amore 	 * If we're a regular file, try just reading in the whole entry
58595c635efSGarrett D'Amore 	 * via mmap().  This is faster than reading it into blocks, and
58695c635efSGarrett D'Amore 	 * since each file is only a few bytes to begin with, I'm not
58795c635efSGarrett D'Amore 	 * concerned that this is going to tank any machines.
58895c635efSGarrett D'Amore 	 */
58995c635efSGarrett D'Amore 
59095c635efSGarrett D'Amore 	if (S_ISREG(st.st_mode)) {
59195c635efSGarrett D'Amore 		if (st.st_size >= (1U << 31)) {
59295c635efSGarrett D'Amore 			fprintf(stderr, "%s: input too large\n", file);
59395c635efSGarrett D'Amore 			return(0);
59495c635efSGarrett D'Amore 		}
59595c635efSGarrett D'Amore 		*with_mmap = 1;
59695c635efSGarrett D'Amore 		fb->sz = (size_t)st.st_size;
597*698f87a4SGarrett D'Amore 		fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0);
59895c635efSGarrett D'Amore 		if (fb->buf != MAP_FAILED)
59995c635efSGarrett D'Amore 			return(1);
60095c635efSGarrett D'Amore 	}
60195c635efSGarrett D'Amore #endif
60295c635efSGarrett D'Amore 
60395c635efSGarrett D'Amore 	/*
60495c635efSGarrett D'Amore 	 * If this isn't a regular file (like, say, stdin), then we must
60595c635efSGarrett D'Amore 	 * go the old way and just read things in bit by bit.
60695c635efSGarrett D'Amore 	 */
60795c635efSGarrett D'Amore 
60895c635efSGarrett D'Amore 	*with_mmap = 0;
60995c635efSGarrett D'Amore 	off = 0;
61095c635efSGarrett D'Amore 	fb->sz = 0;
61195c635efSGarrett D'Amore 	fb->buf = NULL;
61295c635efSGarrett D'Amore 	for (;;) {
61395c635efSGarrett D'Amore 		if (off == fb->sz) {
61495c635efSGarrett D'Amore 			if (fb->sz == (1U << 31)) {
61595c635efSGarrett D'Amore 				fprintf(stderr, "%s: input too large\n", file);
61695c635efSGarrett D'Amore 				break;
61795c635efSGarrett D'Amore 			}
61895c635efSGarrett D'Amore 			resize_buf(fb, 65536);
61995c635efSGarrett D'Amore 		}
62095c635efSGarrett D'Amore 		ssz = read(fd, fb->buf + (int)off, fb->sz - off);
62195c635efSGarrett D'Amore 		if (ssz == 0) {
62295c635efSGarrett D'Amore 			fb->sz = off;
62395c635efSGarrett D'Amore 			return(1);
62495c635efSGarrett D'Amore 		}
62595c635efSGarrett D'Amore 		if (ssz == -1) {
62695c635efSGarrett D'Amore 			perror(file);
62795c635efSGarrett D'Amore 			break;
62895c635efSGarrett D'Amore 		}
62995c635efSGarrett D'Amore 		off += (size_t)ssz;
63095c635efSGarrett D'Amore 	}
63195c635efSGarrett D'Amore 
63295c635efSGarrett D'Amore 	free(fb->buf);
63395c635efSGarrett D'Amore 	fb->buf = NULL;
63495c635efSGarrett D'Amore 	return(0);
63595c635efSGarrett D'Amore }
63695c635efSGarrett D'Amore 
63795c635efSGarrett D'Amore static void
mparse_end(struct mparse * curp)63895c635efSGarrett D'Amore mparse_end(struct mparse *curp)
63995c635efSGarrett D'Amore {
64095c635efSGarrett D'Amore 
64195c635efSGarrett D'Amore 	if (MANDOCLEVEL_FATAL <= curp->file_status)
64295c635efSGarrett D'Amore 		return;
64395c635efSGarrett D'Amore 
64495c635efSGarrett D'Amore 	if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
64595c635efSGarrett D'Amore 		assert(MANDOCLEVEL_FATAL <= curp->file_status);
64695c635efSGarrett D'Amore 		return;
64795c635efSGarrett D'Amore 	}
64895c635efSGarrett D'Amore 
64995c635efSGarrett D'Amore 	if (curp->man && ! man_endparse(curp->man)) {
65095c635efSGarrett D'Amore 		assert(MANDOCLEVEL_FATAL <= curp->file_status);
65195c635efSGarrett D'Amore 		return;
65295c635efSGarrett D'Amore 	}
65395c635efSGarrett D'Amore 
65495c635efSGarrett D'Amore 	if ( ! (curp->man || curp->mdoc)) {
65595c635efSGarrett D'Amore 		mandoc_msg(MANDOCERR_NOTMANUAL, curp, 1, 0, NULL);
65695c635efSGarrett D'Amore 		curp->file_status = MANDOCLEVEL_FATAL;
65795c635efSGarrett D'Amore 		return;
65895c635efSGarrett D'Amore 	}
65995c635efSGarrett D'Amore 
66095c635efSGarrett D'Amore 	roff_endparse(curp->roff);
66195c635efSGarrett D'Amore }
66295c635efSGarrett D'Amore 
66395c635efSGarrett D'Amore static void
mparse_parse_buffer(struct mparse * curp,struct buf blk,const char * file)664*698f87a4SGarrett D'Amore mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file)
66595c635efSGarrett D'Amore {
66695c635efSGarrett D'Amore 	const char	*svfile;
667*698f87a4SGarrett D'Amore 	static int	 recursion_depth;
668*698f87a4SGarrett D'Amore 
669*698f87a4SGarrett D'Amore 	if (64 < recursion_depth) {
670*698f87a4SGarrett D'Amore 		mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL);
671*698f87a4SGarrett D'Amore 		return;
672*698f87a4SGarrett D'Amore 	}
67395c635efSGarrett D'Amore 
67495c635efSGarrett D'Amore 	/* Line number is per-file. */
67595c635efSGarrett D'Amore 	svfile = curp->file;
67695c635efSGarrett D'Amore 	curp->file = file;
67795c635efSGarrett D'Amore 	curp->line = 1;
678*698f87a4SGarrett D'Amore 	recursion_depth++;
67995c635efSGarrett D'Amore 
68095c635efSGarrett D'Amore 	mparse_buf_r(curp, blk, 1);
68195c635efSGarrett D'Amore 
682*698f87a4SGarrett D'Amore 	if (0 == --recursion_depth && MANDOCLEVEL_FATAL > curp->file_status)
68395c635efSGarrett D'Amore 		mparse_end(curp);
68495c635efSGarrett D'Amore 
68595c635efSGarrett D'Amore 	curp->file = svfile;
68695c635efSGarrett D'Amore }
68795c635efSGarrett D'Amore 
68895c635efSGarrett D'Amore enum mandoclevel
mparse_readmem(struct mparse * curp,const void * buf,size_t len,const char * file)68995c635efSGarrett D'Amore mparse_readmem(struct mparse *curp, const void *buf, size_t len,
69095c635efSGarrett D'Amore 		const char *file)
69195c635efSGarrett D'Amore {
69295c635efSGarrett D'Amore 	struct buf blk;
69395c635efSGarrett D'Amore 
69495c635efSGarrett D'Amore 	blk.buf = UNCONST(buf);
69595c635efSGarrett D'Amore 	blk.sz = len;
69695c635efSGarrett D'Amore 
697*698f87a4SGarrett D'Amore 	mparse_parse_buffer(curp, blk, file);
69895c635efSGarrett D'Amore 	return(curp->file_status);
69995c635efSGarrett D'Amore }
70095c635efSGarrett D'Amore 
701*698f87a4SGarrett D'Amore enum mandoclevel
mparse_readfd(struct mparse * curp,int fd,const char * file)702*698f87a4SGarrett D'Amore mparse_readfd(struct mparse *curp, int fd, const char *file)
70395c635efSGarrett D'Amore {
70495c635efSGarrett D'Amore 	struct buf	 blk;
70595c635efSGarrett D'Amore 	int		 with_mmap;
70695c635efSGarrett D'Amore 
70795c635efSGarrett D'Amore 	if (-1 == fd)
70895c635efSGarrett D'Amore 		if (-1 == (fd = open(file, O_RDONLY, 0))) {
70995c635efSGarrett D'Amore 			perror(file);
71095c635efSGarrett D'Amore 			curp->file_status = MANDOCLEVEL_SYSERR;
711*698f87a4SGarrett D'Amore 			goto out;
71295c635efSGarrett D'Amore 		}
71395c635efSGarrett D'Amore 	/*
71495c635efSGarrett D'Amore 	 * Run for each opened file; may be called more than once for
71595c635efSGarrett D'Amore 	 * each full parse sequence if the opened file is nested (i.e.,
71695c635efSGarrett D'Amore 	 * from `so').  Simply sucks in the whole file and moves into
71795c635efSGarrett D'Amore 	 * the parse phase for the file.
71895c635efSGarrett D'Amore 	 */
71995c635efSGarrett D'Amore 
72095c635efSGarrett D'Amore 	if ( ! read_whole_file(file, fd, &blk, &with_mmap)) {
72195c635efSGarrett D'Amore 		curp->file_status = MANDOCLEVEL_SYSERR;
722*698f87a4SGarrett D'Amore 		goto out;
72395c635efSGarrett D'Amore 	}
72495c635efSGarrett D'Amore 
725*698f87a4SGarrett D'Amore 	mparse_parse_buffer(curp, blk, file);
72695c635efSGarrett D'Amore 
72795c635efSGarrett D'Amore #ifdef	HAVE_MMAP
72895c635efSGarrett D'Amore 	if (with_mmap)
72995c635efSGarrett D'Amore 		munmap(blk.buf, blk.sz);
73095c635efSGarrett D'Amore 	else
73195c635efSGarrett D'Amore #endif
73295c635efSGarrett D'Amore 		free(blk.buf);
73395c635efSGarrett D'Amore 
73495c635efSGarrett D'Amore 	if (STDIN_FILENO != fd && -1 == close(fd))
73595c635efSGarrett D'Amore 		perror(file);
736*698f87a4SGarrett D'Amore out:
73795c635efSGarrett D'Amore 	return(curp->file_status);
73895c635efSGarrett D'Amore }
73995c635efSGarrett D'Amore 
74095c635efSGarrett D'Amore struct mparse *
mparse_alloc(enum mparset inttype,enum mandoclevel wlevel,mandocmsg mmsg,void * arg,char * defos)741*698f87a4SGarrett D'Amore mparse_alloc(enum mparset inttype, enum mandoclevel wlevel,
742*698f87a4SGarrett D'Amore 		mandocmsg mmsg, void *arg, char *defos)
74395c635efSGarrett D'Amore {
74495c635efSGarrett D'Amore 	struct mparse	*curp;
74595c635efSGarrett D'Amore 
74695c635efSGarrett D'Amore 	assert(wlevel <= MANDOCLEVEL_FATAL);
74795c635efSGarrett D'Amore 
74895c635efSGarrett D'Amore 	curp = mandoc_calloc(1, sizeof(struct mparse));
74995c635efSGarrett D'Amore 
75095c635efSGarrett D'Amore 	curp->wlevel = wlevel;
75195c635efSGarrett D'Amore 	curp->mmsg = mmsg;
75295c635efSGarrett D'Amore 	curp->arg = arg;
75395c635efSGarrett D'Amore 	curp->inttype = inttype;
754*698f87a4SGarrett D'Amore 	curp->defos = defos;
75595c635efSGarrett D'Amore 
756*698f87a4SGarrett D'Amore 	curp->roff = roff_alloc(inttype, curp);
75795c635efSGarrett D'Amore 	return(curp);
75895c635efSGarrett D'Amore }
75995c635efSGarrett D'Amore 
76095c635efSGarrett D'Amore void
mparse_reset(struct mparse * curp)76195c635efSGarrett D'Amore mparse_reset(struct mparse *curp)
76295c635efSGarrett D'Amore {
76395c635efSGarrett D'Amore 
76495c635efSGarrett D'Amore 	roff_reset(curp->roff);
76595c635efSGarrett D'Amore 
76695c635efSGarrett D'Amore 	if (curp->mdoc)
76795c635efSGarrett D'Amore 		mdoc_reset(curp->mdoc);
76895c635efSGarrett D'Amore 	if (curp->man)
76995c635efSGarrett D'Amore 		man_reset(curp->man);
77095c635efSGarrett D'Amore 	if (curp->secondary)
77195c635efSGarrett D'Amore 		curp->secondary->sz = 0;
77295c635efSGarrett D'Amore 
77395c635efSGarrett D'Amore 	curp->file_status = MANDOCLEVEL_OK;
77495c635efSGarrett D'Amore 	curp->mdoc = NULL;
77595c635efSGarrett D'Amore 	curp->man = NULL;
77695c635efSGarrett D'Amore }
77795c635efSGarrett D'Amore 
77895c635efSGarrett D'Amore void
mparse_free(struct mparse * curp)77995c635efSGarrett D'Amore mparse_free(struct mparse *curp)
78095c635efSGarrett D'Amore {
78195c635efSGarrett D'Amore 
78295c635efSGarrett D'Amore 	if (curp->pmdoc)
78395c635efSGarrett D'Amore 		mdoc_free(curp->pmdoc);
78495c635efSGarrett D'Amore 	if (curp->pman)
78595c635efSGarrett D'Amore 		man_free(curp->pman);
78695c635efSGarrett D'Amore 	if (curp->roff)
78795c635efSGarrett D'Amore 		roff_free(curp->roff);
78895c635efSGarrett D'Amore 	if (curp->secondary)
78995c635efSGarrett D'Amore 		free(curp->secondary->buf);
79095c635efSGarrett D'Amore 
79195c635efSGarrett D'Amore 	free(curp->secondary);
79295c635efSGarrett D'Amore 	free(curp);
79395c635efSGarrett D'Amore }
79495c635efSGarrett D'Amore 
79595c635efSGarrett D'Amore void
mparse_result(struct mparse * curp,struct mdoc ** mdoc,struct man ** man)79695c635efSGarrett D'Amore mparse_result(struct mparse *curp, struct mdoc **mdoc, struct man **man)
79795c635efSGarrett D'Amore {
79895c635efSGarrett D'Amore 
79995c635efSGarrett D'Amore 	if (mdoc)
80095c635efSGarrett D'Amore 		*mdoc = curp->mdoc;
80195c635efSGarrett D'Amore 	if (man)
80295c635efSGarrett D'Amore 		*man = curp->man;
80395c635efSGarrett D'Amore }
80495c635efSGarrett D'Amore 
80595c635efSGarrett D'Amore void
mandoc_vmsg(enum mandocerr t,struct mparse * m,int ln,int pos,const char * fmt,...)80695c635efSGarrett D'Amore mandoc_vmsg(enum mandocerr t, struct mparse *m,
80795c635efSGarrett D'Amore 		int ln, int pos, const char *fmt, ...)
80895c635efSGarrett D'Amore {
80995c635efSGarrett D'Amore 	char		 buf[256];
81095c635efSGarrett D'Amore 	va_list		 ap;
81195c635efSGarrett D'Amore 
81295c635efSGarrett D'Amore 	va_start(ap, fmt);
81395c635efSGarrett D'Amore 	vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
81495c635efSGarrett D'Amore 	va_end(ap);
81595c635efSGarrett D'Amore 
81695c635efSGarrett D'Amore 	mandoc_msg(t, m, ln, pos, buf);
81795c635efSGarrett D'Amore }
81895c635efSGarrett D'Amore 
81995c635efSGarrett D'Amore void
mandoc_msg(enum mandocerr er,struct mparse * m,int ln,int col,const char * msg)82095c635efSGarrett D'Amore mandoc_msg(enum mandocerr er, struct mparse *m,
82195c635efSGarrett D'Amore 		int ln, int col, const char *msg)
82295c635efSGarrett D'Amore {
82395c635efSGarrett D'Amore 	enum mandoclevel level;
82495c635efSGarrett D'Amore 
82595c635efSGarrett D'Amore 	level = MANDOCLEVEL_FATAL;
82695c635efSGarrett D'Amore 	while (er < mandoclimits[level])
82795c635efSGarrett D'Amore 		level--;
82895c635efSGarrett D'Amore 
82995c635efSGarrett D'Amore 	if (level < m->wlevel)
83095c635efSGarrett D'Amore 		return;
83195c635efSGarrett D'Amore 
83295c635efSGarrett D'Amore 	if (m->mmsg)
83395c635efSGarrett D'Amore 		(*m->mmsg)(er, level, m->file, ln, col, msg);
83495c635efSGarrett D'Amore 
83595c635efSGarrett D'Amore 	if (m->file_status < level)
83695c635efSGarrett D'Amore 		m->file_status = level;
83795c635efSGarrett D'Amore }
83895c635efSGarrett D'Amore 
83995c635efSGarrett D'Amore const char *
mparse_strerror(enum mandocerr er)84095c635efSGarrett D'Amore mparse_strerror(enum mandocerr er)
84195c635efSGarrett D'Amore {
84295c635efSGarrett D'Amore 
84395c635efSGarrett D'Amore 	return(mandocerrs[er]);
84495c635efSGarrett D'Amore }
84595c635efSGarrett D'Amore 
84695c635efSGarrett D'Amore const char *
mparse_strlevel(enum mandoclevel lvl)84795c635efSGarrett D'Amore mparse_strlevel(enum mandoclevel lvl)
84895c635efSGarrett D'Amore {
84995c635efSGarrett D'Amore 	return(mandoclevels[lvl]);
85095c635efSGarrett D'Amore }
85195c635efSGarrett D'Amore 
85295c635efSGarrett D'Amore void
mparse_keep(struct mparse * p)85395c635efSGarrett D'Amore mparse_keep(struct mparse *p)
85495c635efSGarrett D'Amore {
85595c635efSGarrett D'Amore 
85695c635efSGarrett D'Amore 	assert(NULL == p->secondary);
85795c635efSGarrett D'Amore 	p->secondary = mandoc_calloc(1, sizeof(struct buf));
85895c635efSGarrett D'Amore }
85995c635efSGarrett D'Amore 
86095c635efSGarrett D'Amore const char *
mparse_getkeep(const struct mparse * p)86195c635efSGarrett D'Amore mparse_getkeep(const struct mparse *p)
86295c635efSGarrett D'Amore {
86395c635efSGarrett D'Amore 
86495c635efSGarrett D'Amore 	assert(p->secondary);
86595c635efSGarrett D'Amore 	return(p->secondary->sz ? p->secondary->buf : NULL);
86695c635efSGarrett D'Amore }
867