xref: /freebsd/contrib/mandoc/read.c (revision 6d38604fc532a3fc060788e3ce40464b46047eaf)
1*6d38604fSBaptiste Daroussin /* $Id: read.c,v 1.220 2021/06/27 17:57:54 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
3*6d38604fSBaptiste Daroussin  * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org>
461d06d6bSBaptiste Daroussin  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
561d06d6bSBaptiste Daroussin  * Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org>
661d06d6bSBaptiste Daroussin  *
761d06d6bSBaptiste Daroussin  * Permission to use, copy, modify, and distribute this software for any
861d06d6bSBaptiste Daroussin  * purpose with or without fee is hereby granted, provided that the above
961d06d6bSBaptiste Daroussin  * copyright notice and this permission notice appear in all copies.
1061d06d6bSBaptiste Daroussin  *
1161d06d6bSBaptiste Daroussin  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
1261d06d6bSBaptiste Daroussin  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1361d06d6bSBaptiste Daroussin  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
1461d06d6bSBaptiste Daroussin  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1561d06d6bSBaptiste Daroussin  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1661d06d6bSBaptiste Daroussin  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1761d06d6bSBaptiste Daroussin  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*6d38604fSBaptiste Daroussin  *
19*6d38604fSBaptiste Daroussin  * Top-level functions of the mandoc(3) parser:
20*6d38604fSBaptiste Daroussin  * Parser and input encoding selection, decompression,
21*6d38604fSBaptiste Daroussin  * handling of input bytes, characters, lines, and files,
22*6d38604fSBaptiste Daroussin  * handling of roff(7) loops and file inclusion,
23*6d38604fSBaptiste Daroussin  * and steering of the various parsers.
2461d06d6bSBaptiste Daroussin  */
2561d06d6bSBaptiste Daroussin #include "config.h"
2661d06d6bSBaptiste Daroussin 
2761d06d6bSBaptiste Daroussin #include <sys/types.h>
2861d06d6bSBaptiste Daroussin #include <sys/mman.h>
2961d06d6bSBaptiste Daroussin #include <sys/stat.h>
3061d06d6bSBaptiste Daroussin 
3161d06d6bSBaptiste Daroussin #include <assert.h>
3261d06d6bSBaptiste Daroussin #include <ctype.h>
3361d06d6bSBaptiste Daroussin #include <errno.h>
3461d06d6bSBaptiste Daroussin #include <fcntl.h>
3561d06d6bSBaptiste Daroussin #include <stdarg.h>
3661d06d6bSBaptiste Daroussin #include <stdio.h>
3761d06d6bSBaptiste Daroussin #include <stdlib.h>
3861d06d6bSBaptiste Daroussin #include <string.h>
3961d06d6bSBaptiste Daroussin #include <unistd.h>
4061d06d6bSBaptiste Daroussin #include <zlib.h>
4161d06d6bSBaptiste Daroussin 
4261d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
4361d06d6bSBaptiste Daroussin #include "mandoc.h"
4461d06d6bSBaptiste Daroussin #include "roff.h"
4561d06d6bSBaptiste Daroussin #include "mdoc.h"
4661d06d6bSBaptiste Daroussin #include "man.h"
477295610fSBaptiste Daroussin #include "mandoc_parse.h"
4861d06d6bSBaptiste Daroussin #include "libmandoc.h"
497295610fSBaptiste Daroussin #include "roff_int.h"
50*6d38604fSBaptiste Daroussin #include "tag.h"
5161d06d6bSBaptiste Daroussin 
5261d06d6bSBaptiste Daroussin #define	REPARSE_LIMIT	1000
5361d06d6bSBaptiste Daroussin 
5461d06d6bSBaptiste Daroussin struct	mparse {
5561d06d6bSBaptiste Daroussin 	struct roff	 *roff; /* roff parser (!NULL) */
5661d06d6bSBaptiste Daroussin 	struct roff_man	 *man; /* man parser */
5761d06d6bSBaptiste Daroussin 	struct buf	 *primary; /* buffer currently being parsed */
587295610fSBaptiste Daroussin 	struct buf	 *secondary; /* copy of top level input */
597295610fSBaptiste Daroussin 	struct buf	 *loop; /* open .while request line */
6061d06d6bSBaptiste Daroussin 	const char	 *os_s; /* default operating system */
6161d06d6bSBaptiste Daroussin 	int		  options; /* parser options */
6261d06d6bSBaptiste Daroussin 	int		  gzip; /* current input file is gzipped */
6361d06d6bSBaptiste Daroussin 	int		  filenc; /* encoding of the current file */
6461d06d6bSBaptiste Daroussin 	int		  reparse_count; /* finite interp. stack */
6561d06d6bSBaptiste Daroussin 	int		  line; /* line number in the file */
6661d06d6bSBaptiste Daroussin };
6761d06d6bSBaptiste Daroussin 
6861d06d6bSBaptiste Daroussin static	void	  choose_parser(struct mparse *);
697295610fSBaptiste Daroussin static	void	  free_buf_list(struct buf *);
7061d06d6bSBaptiste Daroussin static	void	  resize_buf(struct buf *, size_t);
7161d06d6bSBaptiste Daroussin static	int	  mparse_buf_r(struct mparse *, struct buf, size_t, int);
727295610fSBaptiste Daroussin static	int	  read_whole_file(struct mparse *, int, struct buf *, int *);
7361d06d6bSBaptiste Daroussin static	void	  mparse_end(struct mparse *);
7461d06d6bSBaptiste Daroussin 
7561d06d6bSBaptiste Daroussin 
7661d06d6bSBaptiste Daroussin static void
resize_buf(struct buf * buf,size_t initial)7761d06d6bSBaptiste Daroussin resize_buf(struct buf *buf, size_t initial)
7861d06d6bSBaptiste Daroussin {
7961d06d6bSBaptiste Daroussin 
8061d06d6bSBaptiste Daroussin 	buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
8161d06d6bSBaptiste Daroussin 	buf->buf = mandoc_realloc(buf->buf, buf->sz);
8261d06d6bSBaptiste Daroussin }
8361d06d6bSBaptiste Daroussin 
8461d06d6bSBaptiste Daroussin static void
free_buf_list(struct buf * buf)857295610fSBaptiste Daroussin free_buf_list(struct buf *buf)
867295610fSBaptiste Daroussin {
877295610fSBaptiste Daroussin 	struct buf *tmp;
887295610fSBaptiste Daroussin 
897295610fSBaptiste Daroussin 	while (buf != NULL) {
907295610fSBaptiste Daroussin 		tmp = buf;
917295610fSBaptiste Daroussin 		buf = tmp->next;
927295610fSBaptiste Daroussin 		free(tmp->buf);
937295610fSBaptiste Daroussin 		free(tmp);
947295610fSBaptiste Daroussin 	}
957295610fSBaptiste Daroussin }
967295610fSBaptiste Daroussin 
977295610fSBaptiste Daroussin static void
choose_parser(struct mparse * curp)9861d06d6bSBaptiste Daroussin choose_parser(struct mparse *curp)
9961d06d6bSBaptiste Daroussin {
10061d06d6bSBaptiste Daroussin 	char		*cp, *ep;
10161d06d6bSBaptiste Daroussin 	int		 format;
10261d06d6bSBaptiste Daroussin 
10361d06d6bSBaptiste Daroussin 	/*
10461d06d6bSBaptiste Daroussin 	 * If neither command line arguments -mdoc or -man select
10561d06d6bSBaptiste Daroussin 	 * a parser nor the roff parser found a .Dd or .TH macro
10661d06d6bSBaptiste Daroussin 	 * yet, look ahead in the main input buffer.
10761d06d6bSBaptiste Daroussin 	 */
10861d06d6bSBaptiste Daroussin 
10961d06d6bSBaptiste Daroussin 	if ((format = roff_getformat(curp->roff)) == 0) {
11061d06d6bSBaptiste Daroussin 		cp = curp->primary->buf;
11161d06d6bSBaptiste Daroussin 		ep = cp + curp->primary->sz;
11261d06d6bSBaptiste Daroussin 		while (cp < ep) {
11361d06d6bSBaptiste Daroussin 			if (*cp == '.' || *cp == '\'') {
11461d06d6bSBaptiste Daroussin 				cp++;
11561d06d6bSBaptiste Daroussin 				if (cp[0] == 'D' && cp[1] == 'd') {
11661d06d6bSBaptiste Daroussin 					format = MPARSE_MDOC;
11761d06d6bSBaptiste Daroussin 					break;
11861d06d6bSBaptiste Daroussin 				}
11961d06d6bSBaptiste Daroussin 				if (cp[0] == 'T' && cp[1] == 'H') {
12061d06d6bSBaptiste Daroussin 					format = MPARSE_MAN;
12161d06d6bSBaptiste Daroussin 					break;
12261d06d6bSBaptiste Daroussin 				}
12361d06d6bSBaptiste Daroussin 			}
12461d06d6bSBaptiste Daroussin 			cp = memchr(cp, '\n', ep - cp);
12561d06d6bSBaptiste Daroussin 			if (cp == NULL)
12661d06d6bSBaptiste Daroussin 				break;
12761d06d6bSBaptiste Daroussin 			cp++;
12861d06d6bSBaptiste Daroussin 		}
12961d06d6bSBaptiste Daroussin 	}
13061d06d6bSBaptiste Daroussin 
13161d06d6bSBaptiste Daroussin 	if (format == MPARSE_MDOC) {
1327295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MDOC;
13361d06d6bSBaptiste Daroussin 		if (curp->man->mdocmac == NULL)
13461d06d6bSBaptiste Daroussin 			curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX);
13561d06d6bSBaptiste Daroussin 	} else {
1367295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MAN;
13761d06d6bSBaptiste Daroussin 		if (curp->man->manmac == NULL)
13861d06d6bSBaptiste Daroussin 			curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX);
13961d06d6bSBaptiste Daroussin 	}
1407295610fSBaptiste Daroussin 	curp->man->meta.first->tok = TOKEN_NONE;
14161d06d6bSBaptiste Daroussin }
14261d06d6bSBaptiste Daroussin 
14361d06d6bSBaptiste Daroussin /*
14461d06d6bSBaptiste Daroussin  * Main parse routine for a buffer.
14561d06d6bSBaptiste Daroussin  * It assumes encoding and line numbering are already set up.
14661d06d6bSBaptiste Daroussin  * It can recurse directly (for invocations of user-defined
14761d06d6bSBaptiste Daroussin  * macros, inline equations, and input line traps)
14861d06d6bSBaptiste Daroussin  * and indirectly (for .so file inclusion).
14961d06d6bSBaptiste Daroussin  */
15061d06d6bSBaptiste Daroussin static int
mparse_buf_r(struct mparse * curp,struct buf blk,size_t i,int start)15161d06d6bSBaptiste Daroussin mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start)
15261d06d6bSBaptiste Daroussin {
15361d06d6bSBaptiste Daroussin 	struct buf	 ln;
1547295610fSBaptiste Daroussin 	struct buf	*firstln, *lastln, *thisln, *loop;
15561d06d6bSBaptiste Daroussin 	char		*cp;
15661d06d6bSBaptiste Daroussin 	size_t		 pos; /* byte number in the ln buffer */
157*6d38604fSBaptiste Daroussin 	size_t		 spos; /* at the start of the current line parse */
1587295610fSBaptiste Daroussin 	int		 line_result, result;
15961d06d6bSBaptiste Daroussin 	int		 of;
16061d06d6bSBaptiste Daroussin 	int		 lnn; /* line number in the real file */
16161d06d6bSBaptiste Daroussin 	int		 fd;
1627295610fSBaptiste Daroussin 	int		 inloop; /* Saw .while on this level. */
16361d06d6bSBaptiste Daroussin 	unsigned char	 c;
16461d06d6bSBaptiste Daroussin 
1657295610fSBaptiste Daroussin 	ln.sz = 256;
1667295610fSBaptiste Daroussin 	ln.buf = mandoc_malloc(ln.sz);
1677295610fSBaptiste Daroussin 	ln.next = NULL;
16845a5aec3SBaptiste Daroussin 	firstln = lastln = loop = NULL;
16961d06d6bSBaptiste Daroussin 	lnn = curp->line;
17061d06d6bSBaptiste Daroussin 	pos = 0;
1717295610fSBaptiste Daroussin 	inloop = 0;
1727295610fSBaptiste Daroussin 	result = ROFF_CONT;
17361d06d6bSBaptiste Daroussin 
1747295610fSBaptiste Daroussin 	while (i < blk.sz && (blk.buf[i] != '\0' || pos != 0)) {
17561d06d6bSBaptiste Daroussin 		if (start) {
17661d06d6bSBaptiste Daroussin 			curp->line = lnn;
17761d06d6bSBaptiste Daroussin 			curp->reparse_count = 0;
17861d06d6bSBaptiste Daroussin 
17961d06d6bSBaptiste Daroussin 			if (lnn < 3 &&
18061d06d6bSBaptiste Daroussin 			    curp->filenc & MPARSE_UTF8 &&
18161d06d6bSBaptiste Daroussin 			    curp->filenc & MPARSE_LATIN1)
18261d06d6bSBaptiste Daroussin 				curp->filenc = preconv_cue(&blk, i);
18361d06d6bSBaptiste Daroussin 		}
184*6d38604fSBaptiste Daroussin 		spos = pos;
18561d06d6bSBaptiste Daroussin 
18661d06d6bSBaptiste Daroussin 		while (i < blk.sz && (start || blk.buf[i] != '\0')) {
18761d06d6bSBaptiste Daroussin 
18861d06d6bSBaptiste Daroussin 			/*
18961d06d6bSBaptiste Daroussin 			 * When finding an unescaped newline character,
19061d06d6bSBaptiste Daroussin 			 * leave the character loop to process the line.
19161d06d6bSBaptiste Daroussin 			 * Skip a preceding carriage return, if any.
19261d06d6bSBaptiste Daroussin 			 */
19361d06d6bSBaptiste Daroussin 
19461d06d6bSBaptiste Daroussin 			if ('\r' == blk.buf[i] && i + 1 < blk.sz &&
19561d06d6bSBaptiste Daroussin 			    '\n' == blk.buf[i + 1])
19661d06d6bSBaptiste Daroussin 				++i;
19761d06d6bSBaptiste Daroussin 			if ('\n' == blk.buf[i]) {
19861d06d6bSBaptiste Daroussin 				++i;
19961d06d6bSBaptiste Daroussin 				++lnn;
20061d06d6bSBaptiste Daroussin 				break;
20161d06d6bSBaptiste Daroussin 			}
20261d06d6bSBaptiste Daroussin 
20361d06d6bSBaptiste Daroussin 			/*
20461d06d6bSBaptiste Daroussin 			 * Make sure we have space for the worst
2057295610fSBaptiste Daroussin 			 * case of 12 bytes: "\\[u10ffff]\n\0"
20661d06d6bSBaptiste Daroussin 			 */
20761d06d6bSBaptiste Daroussin 
2087295610fSBaptiste Daroussin 			if (pos + 12 > ln.sz)
20961d06d6bSBaptiste Daroussin 				resize_buf(&ln, 256);
21061d06d6bSBaptiste Daroussin 
21161d06d6bSBaptiste Daroussin 			/*
21261d06d6bSBaptiste Daroussin 			 * Encode 8-bit input.
21361d06d6bSBaptiste Daroussin 			 */
21461d06d6bSBaptiste Daroussin 
21561d06d6bSBaptiste Daroussin 			c = blk.buf[i];
21661d06d6bSBaptiste Daroussin 			if (c & 0x80) {
21761d06d6bSBaptiste Daroussin 				if ( ! (curp->filenc && preconv_encode(
21861d06d6bSBaptiste Daroussin 				    &blk, &i, &ln, &pos, &curp->filenc))) {
2197295610fSBaptiste Daroussin 					mandoc_msg(MANDOCERR_CHAR_BAD,
22061d06d6bSBaptiste Daroussin 					    curp->line, pos, "0x%x", c);
22161d06d6bSBaptiste Daroussin 					ln.buf[pos++] = '?';
22261d06d6bSBaptiste Daroussin 					i++;
22361d06d6bSBaptiste Daroussin 				}
22461d06d6bSBaptiste Daroussin 				continue;
22561d06d6bSBaptiste Daroussin 			}
22661d06d6bSBaptiste Daroussin 
22761d06d6bSBaptiste Daroussin 			/*
22861d06d6bSBaptiste Daroussin 			 * Exclude control characters.
22961d06d6bSBaptiste Daroussin 			 */
23061d06d6bSBaptiste Daroussin 
23161d06d6bSBaptiste Daroussin 			if (c == 0x7f || (c < 0x20 && c != 0x09)) {
2327295610fSBaptiste Daroussin 				mandoc_msg(c == 0x00 || c == 0x04 ||
23361d06d6bSBaptiste Daroussin 				    c > 0x0a ? MANDOCERR_CHAR_BAD :
23461d06d6bSBaptiste Daroussin 				    MANDOCERR_CHAR_UNSUPP,
2357295610fSBaptiste Daroussin 				    curp->line, pos, "0x%x", c);
23661d06d6bSBaptiste Daroussin 				i++;
23761d06d6bSBaptiste Daroussin 				if (c != '\r')
23861d06d6bSBaptiste Daroussin 					ln.buf[pos++] = '?';
23961d06d6bSBaptiste Daroussin 				continue;
24061d06d6bSBaptiste Daroussin 			}
24161d06d6bSBaptiste Daroussin 
24261d06d6bSBaptiste Daroussin 			ln.buf[pos++] = blk.buf[i++];
24361d06d6bSBaptiste Daroussin 		}
2447295610fSBaptiste Daroussin 		ln.buf[pos] = '\0';
24561d06d6bSBaptiste Daroussin 
2467295610fSBaptiste Daroussin 		/*
2477295610fSBaptiste Daroussin 		 * Maintain a lookaside buffer of all lines.
2487295610fSBaptiste Daroussin 		 * parsed from this input source.
2497295610fSBaptiste Daroussin 		 */
25061d06d6bSBaptiste Daroussin 
2517295610fSBaptiste Daroussin 		thisln = mandoc_malloc(sizeof(*thisln));
2527295610fSBaptiste Daroussin 		thisln->buf = mandoc_strdup(ln.buf);
2537295610fSBaptiste Daroussin 		thisln->sz = strlen(ln.buf) + 1;
2547295610fSBaptiste Daroussin 		thisln->next = NULL;
2557295610fSBaptiste Daroussin 		if (firstln == NULL) {
2567295610fSBaptiste Daroussin 			firstln = lastln = thisln;
2577295610fSBaptiste Daroussin 			if (curp->secondary == NULL)
2587295610fSBaptiste Daroussin 				curp->secondary = firstln;
2597295610fSBaptiste Daroussin 		} else {
2607295610fSBaptiste Daroussin 			lastln->next = thisln;
2617295610fSBaptiste Daroussin 			lastln = thisln;
2627295610fSBaptiste Daroussin 		}
2637295610fSBaptiste Daroussin 
2647295610fSBaptiste Daroussin 		/* XXX Ugly hack to mark the end of the input. */
2657295610fSBaptiste Daroussin 
2667295610fSBaptiste Daroussin 		if (i == blk.sz || blk.buf[i] == '\0') {
26745a5aec3SBaptiste Daroussin 			if (pos + 2 > ln.sz)
26845a5aec3SBaptiste Daroussin 				resize_buf(&ln, 256);
26961d06d6bSBaptiste Daroussin 			ln.buf[pos++] = '\n';
27061d06d6bSBaptiste Daroussin 			ln.buf[pos] = '\0';
2717295610fSBaptiste Daroussin 		}
27261d06d6bSBaptiste Daroussin 
27361d06d6bSBaptiste Daroussin 		/*
27461d06d6bSBaptiste Daroussin 		 * A significant amount of complexity is contained by
27561d06d6bSBaptiste Daroussin 		 * the roff preprocessor.  It's line-oriented but can be
27661d06d6bSBaptiste Daroussin 		 * expressed on one line, so we need at times to
27761d06d6bSBaptiste Daroussin 		 * readjust our starting point and re-run it.  The roff
27861d06d6bSBaptiste Daroussin 		 * preprocessor can also readjust the buffers with new
27961d06d6bSBaptiste Daroussin 		 * data, so we pass them in wholesale.
28061d06d6bSBaptiste Daroussin 		 */
28161d06d6bSBaptiste Daroussin 
28261d06d6bSBaptiste Daroussin 		of = 0;
28361d06d6bSBaptiste Daroussin rerun:
284*6d38604fSBaptiste Daroussin 		line_result = roff_parseln(curp->roff, curp->line,
285*6d38604fSBaptiste Daroussin 		    &ln, &of, start && spos == 0 ? pos : 0);
28661d06d6bSBaptiste Daroussin 
2877295610fSBaptiste Daroussin 		/* Process options. */
2887295610fSBaptiste Daroussin 
2897295610fSBaptiste Daroussin 		if (line_result & ROFF_APPEND)
2907295610fSBaptiste Daroussin 			assert(line_result == (ROFF_IGN | ROFF_APPEND));
2917295610fSBaptiste Daroussin 
2927295610fSBaptiste Daroussin 		if (line_result & ROFF_USERCALL)
2937295610fSBaptiste Daroussin 			assert((line_result & ROFF_MASK) == ROFF_REPARSE);
2947295610fSBaptiste Daroussin 
2957295610fSBaptiste Daroussin 		if (line_result & ROFF_USERRET) {
2967295610fSBaptiste Daroussin 			assert(line_result == (ROFF_IGN | ROFF_USERRET));
2977295610fSBaptiste Daroussin 			if (start == 0) {
2987295610fSBaptiste Daroussin 				/* Return from the current macro. */
2997295610fSBaptiste Daroussin 				result = ROFF_USERRET;
3007295610fSBaptiste Daroussin 				goto out;
30161d06d6bSBaptiste Daroussin 			}
3027295610fSBaptiste Daroussin 		}
3037295610fSBaptiste Daroussin 
3047295610fSBaptiste Daroussin 		switch (line_result & ROFF_LOOPMASK) {
3057295610fSBaptiste Daroussin 		case ROFF_IGN:
3067295610fSBaptiste Daroussin 			break;
3077295610fSBaptiste Daroussin 		case ROFF_WHILE:
3087295610fSBaptiste Daroussin 			if (curp->loop != NULL) {
3097295610fSBaptiste Daroussin 				if (loop == curp->loop)
3107295610fSBaptiste Daroussin 					break;
3117295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_WHILE_NEST,
3127295610fSBaptiste Daroussin 				    curp->line, pos, NULL);
3137295610fSBaptiste Daroussin 			}
3147295610fSBaptiste Daroussin 			curp->loop = thisln;
3157295610fSBaptiste Daroussin 			loop = NULL;
3167295610fSBaptiste Daroussin 			inloop = 1;
3177295610fSBaptiste Daroussin 			break;
3187295610fSBaptiste Daroussin 		case ROFF_LOOPCONT:
3197295610fSBaptiste Daroussin 		case ROFF_LOOPEXIT:
3207295610fSBaptiste Daroussin 			if (curp->loop == NULL) {
3217295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_WHILE_FAIL,
3227295610fSBaptiste Daroussin 				    curp->line, pos, NULL);
3237295610fSBaptiste Daroussin 				break;
3247295610fSBaptiste Daroussin 			}
3257295610fSBaptiste Daroussin 			if (inloop == 0) {
3267295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_WHILE_INTO,
3277295610fSBaptiste Daroussin 				    curp->line, pos, NULL);
3287295610fSBaptiste Daroussin 				curp->loop = loop = NULL;
3297295610fSBaptiste Daroussin 				break;
3307295610fSBaptiste Daroussin 			}
3317295610fSBaptiste Daroussin 			if (line_result & ROFF_LOOPCONT)
3327295610fSBaptiste Daroussin 				loop = curp->loop;
3337295610fSBaptiste Daroussin 			else {
3347295610fSBaptiste Daroussin 				curp->loop = loop = NULL;
3357295610fSBaptiste Daroussin 				inloop = 0;
3367295610fSBaptiste Daroussin 			}
3377295610fSBaptiste Daroussin 			break;
3387295610fSBaptiste Daroussin 		default:
3397295610fSBaptiste Daroussin 			abort();
3407295610fSBaptiste Daroussin 		}
3417295610fSBaptiste Daroussin 
3427295610fSBaptiste Daroussin 		/* Process the main instruction from the roff parser. */
3437295610fSBaptiste Daroussin 
3447295610fSBaptiste Daroussin 		switch (line_result & ROFF_MASK) {
3457295610fSBaptiste Daroussin 		case ROFF_IGN:
3467295610fSBaptiste Daroussin 			break;
3477295610fSBaptiste Daroussin 		case ROFF_CONT:
3487295610fSBaptiste Daroussin 			if (curp->man->meta.macroset == MACROSET_NONE)
3497295610fSBaptiste Daroussin 				choose_parser(curp);
3507295610fSBaptiste Daroussin 			if ((curp->man->meta.macroset == MACROSET_MDOC ?
3517295610fSBaptiste Daroussin 			     mdoc_parseln(curp->man, curp->line, ln.buf, of) :
3527295610fSBaptiste Daroussin 			     man_parseln(curp->man, curp->line, ln.buf, of)
3537295610fSBaptiste Daroussin 			    ) == 2)
3547295610fSBaptiste Daroussin 				goto out;
3557295610fSBaptiste Daroussin 			break;
35661d06d6bSBaptiste Daroussin 		case ROFF_RERUN:
35761d06d6bSBaptiste Daroussin 			goto rerun;
3587295610fSBaptiste Daroussin 		case ROFF_REPARSE:
3597295610fSBaptiste Daroussin 			if (++curp->reparse_count > REPARSE_LIMIT) {
3607295610fSBaptiste Daroussin 				/* Abort and return to the top level. */
3617295610fSBaptiste Daroussin 				result = ROFF_IGN;
3627295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ROFFLOOP,
3637295610fSBaptiste Daroussin 				    curp->line, pos, NULL);
3647295610fSBaptiste Daroussin 				goto out;
3657295610fSBaptiste Daroussin 			}
3667295610fSBaptiste Daroussin 			result = mparse_buf_r(curp, ln, of, 0);
3677295610fSBaptiste Daroussin 			if (line_result & ROFF_USERCALL) {
3687295610fSBaptiste Daroussin 				roff_userret(curp->roff);
3697295610fSBaptiste Daroussin 				/* Continue normally. */
3707295610fSBaptiste Daroussin 				if (result & ROFF_USERRET)
3717295610fSBaptiste Daroussin 					result = ROFF_CONT;
3727295610fSBaptiste Daroussin 			}
3737295610fSBaptiste Daroussin 			if (start == 0 && result != ROFF_CONT)
3747295610fSBaptiste Daroussin 				goto out;
3757295610fSBaptiste Daroussin 			break;
37661d06d6bSBaptiste Daroussin 		case ROFF_SO:
37761d06d6bSBaptiste Daroussin 			if ( ! (curp->options & MPARSE_SO) &&
37861d06d6bSBaptiste Daroussin 			    (i >= blk.sz || blk.buf[i] == '\0')) {
3797295610fSBaptiste Daroussin 				curp->man->meta.sodest =
3807295610fSBaptiste Daroussin 				    mandoc_strdup(ln.buf + of);
3817295610fSBaptiste Daroussin 				goto out;
38261d06d6bSBaptiste Daroussin 			}
38361d06d6bSBaptiste Daroussin 			if ((fd = mparse_open(curp, ln.buf + of)) != -1) {
38461d06d6bSBaptiste Daroussin 				mparse_readfd(curp, fd, ln.buf + of);
38561d06d6bSBaptiste Daroussin 				close(fd);
38661d06d6bSBaptiste Daroussin 			} else {
3877295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_SO_FAIL,
3887295610fSBaptiste Daroussin 				    curp->line, of, ".so %s: %s",
3897295610fSBaptiste Daroussin 				    ln.buf + of, strerror(errno));
39061d06d6bSBaptiste Daroussin 				ln.sz = mandoc_asprintf(&cp,
39161d06d6bSBaptiste Daroussin 				    ".sp\nSee the file %s.\n.sp",
39261d06d6bSBaptiste Daroussin 				    ln.buf + of);
39361d06d6bSBaptiste Daroussin 				free(ln.buf);
39461d06d6bSBaptiste Daroussin 				ln.buf = cp;
39561d06d6bSBaptiste Daroussin 				of = 0;
39661d06d6bSBaptiste Daroussin 				mparse_buf_r(curp, ln, of, 0);
39761d06d6bSBaptiste Daroussin 			}
3987295610fSBaptiste Daroussin 			break;
39961d06d6bSBaptiste Daroussin 		default:
4007295610fSBaptiste Daroussin 			abort();
40161d06d6bSBaptiste Daroussin 		}
40261d06d6bSBaptiste Daroussin 
40361d06d6bSBaptiste Daroussin 		/* Start the next input line. */
40461d06d6bSBaptiste Daroussin 
4057295610fSBaptiste Daroussin 		if (loop != NULL &&
4067295610fSBaptiste Daroussin 		    (line_result & ROFF_LOOPMASK) == ROFF_IGN)
4077295610fSBaptiste Daroussin 			loop = loop->next;
4087295610fSBaptiste Daroussin 
4097295610fSBaptiste Daroussin 		if (loop != NULL) {
4107295610fSBaptiste Daroussin 			if ((line_result & ROFF_APPEND) == 0)
4117295610fSBaptiste Daroussin 				*ln.buf = '\0';
4127295610fSBaptiste Daroussin 			if (ln.sz < loop->sz)
4137295610fSBaptiste Daroussin 				resize_buf(&ln, loop->sz);
4147295610fSBaptiste Daroussin 			(void)strlcat(ln.buf, loop->buf, ln.sz);
4157295610fSBaptiste Daroussin 			of = 0;
4167295610fSBaptiste Daroussin 			goto rerun;
41761d06d6bSBaptiste Daroussin 		}
41861d06d6bSBaptiste Daroussin 
4197295610fSBaptiste Daroussin 		pos = (line_result & ROFF_APPEND) ? strlen(ln.buf) : 0;
4207295610fSBaptiste Daroussin 	}
4217295610fSBaptiste Daroussin out:
4227295610fSBaptiste Daroussin 	if (inloop) {
4237295610fSBaptiste Daroussin 		if (result != ROFF_USERRET)
4247295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_WHILE_OUTOF,
4257295610fSBaptiste Daroussin 			    curp->line, pos, NULL);
4267295610fSBaptiste Daroussin 		curp->loop = NULL;
4277295610fSBaptiste Daroussin 	}
42861d06d6bSBaptiste Daroussin 	free(ln.buf);
4297295610fSBaptiste Daroussin 	if (firstln != curp->secondary)
4307295610fSBaptiste Daroussin 		free_buf_list(firstln);
4317295610fSBaptiste Daroussin 	return result;
43261d06d6bSBaptiste Daroussin }
43361d06d6bSBaptiste Daroussin 
43461d06d6bSBaptiste Daroussin static int
read_whole_file(struct mparse * curp,int fd,struct buf * fb,int * with_mmap)4357295610fSBaptiste Daroussin read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap)
43661d06d6bSBaptiste Daroussin {
43761d06d6bSBaptiste Daroussin 	struct stat	 st;
43861d06d6bSBaptiste Daroussin 	gzFile		 gz;
43961d06d6bSBaptiste Daroussin 	size_t		 off;
44061d06d6bSBaptiste Daroussin 	ssize_t		 ssz;
44161d06d6bSBaptiste Daroussin 	int		 gzerrnum, retval;
44261d06d6bSBaptiste Daroussin 
44361d06d6bSBaptiste Daroussin 	if (fstat(fd, &st) == -1) {
44445a5aec3SBaptiste Daroussin 		mandoc_msg(MANDOCERR_FSTAT, 0, 0, "%s", strerror(errno));
44545a5aec3SBaptiste Daroussin 		return -1;
44661d06d6bSBaptiste Daroussin 	}
44761d06d6bSBaptiste Daroussin 
44861d06d6bSBaptiste Daroussin 	/*
44961d06d6bSBaptiste Daroussin 	 * If we're a regular file, try just reading in the whole entry
45061d06d6bSBaptiste Daroussin 	 * via mmap().  This is faster than reading it into blocks, and
45161d06d6bSBaptiste Daroussin 	 * since each file is only a few bytes to begin with, I'm not
45261d06d6bSBaptiste Daroussin 	 * concerned that this is going to tank any machines.
45361d06d6bSBaptiste Daroussin 	 */
45461d06d6bSBaptiste Daroussin 
45561d06d6bSBaptiste Daroussin 	if (curp->gzip == 0 && S_ISREG(st.st_mode)) {
45661d06d6bSBaptiste Daroussin 		if (st.st_size > 0x7fffffff) {
4577295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL);
45845a5aec3SBaptiste Daroussin 			return -1;
45961d06d6bSBaptiste Daroussin 		}
46061d06d6bSBaptiste Daroussin 		*with_mmap = 1;
46161d06d6bSBaptiste Daroussin 		fb->sz = (size_t)st.st_size;
46261d06d6bSBaptiste Daroussin 		fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0);
46361d06d6bSBaptiste Daroussin 		if (fb->buf != MAP_FAILED)
46445a5aec3SBaptiste Daroussin 			return 0;
46561d06d6bSBaptiste Daroussin 	}
46661d06d6bSBaptiste Daroussin 
46761d06d6bSBaptiste Daroussin 	if (curp->gzip) {
46861d06d6bSBaptiste Daroussin 		/*
46961d06d6bSBaptiste Daroussin 		 * Duplicating the file descriptor is required
47061d06d6bSBaptiste Daroussin 		 * because we will have to call gzclose(3)
47161d06d6bSBaptiste Daroussin 		 * to free memory used internally by zlib,
47261d06d6bSBaptiste Daroussin 		 * but that will also close the file descriptor,
47361d06d6bSBaptiste Daroussin 		 * which this function must not do.
47461d06d6bSBaptiste Daroussin 		 */
47561d06d6bSBaptiste Daroussin 		if ((fd = dup(fd)) == -1) {
47645a5aec3SBaptiste Daroussin 			mandoc_msg(MANDOCERR_DUP, 0, 0,
47745a5aec3SBaptiste Daroussin 			    "%s", strerror(errno));
47845a5aec3SBaptiste Daroussin 			return -1;
47961d06d6bSBaptiste Daroussin 		}
48061d06d6bSBaptiste Daroussin 		if ((gz = gzdopen(fd, "rb")) == NULL) {
48145a5aec3SBaptiste Daroussin 			mandoc_msg(MANDOCERR_GZDOPEN, 0, 0,
48245a5aec3SBaptiste Daroussin 			    "%s", strerror(errno));
48361d06d6bSBaptiste Daroussin 			close(fd);
48445a5aec3SBaptiste Daroussin 			return -1;
48561d06d6bSBaptiste Daroussin 		}
48661d06d6bSBaptiste Daroussin 	} else
48761d06d6bSBaptiste Daroussin 		gz = NULL;
48861d06d6bSBaptiste Daroussin 
48961d06d6bSBaptiste Daroussin 	/*
49061d06d6bSBaptiste Daroussin 	 * If this isn't a regular file (like, say, stdin), then we must
49161d06d6bSBaptiste Daroussin 	 * go the old way and just read things in bit by bit.
49261d06d6bSBaptiste Daroussin 	 */
49361d06d6bSBaptiste Daroussin 
49461d06d6bSBaptiste Daroussin 	*with_mmap = 0;
49561d06d6bSBaptiste Daroussin 	off = 0;
49645a5aec3SBaptiste Daroussin 	retval = -1;
49761d06d6bSBaptiste Daroussin 	fb->sz = 0;
49861d06d6bSBaptiste Daroussin 	fb->buf = NULL;
49961d06d6bSBaptiste Daroussin 	for (;;) {
50061d06d6bSBaptiste Daroussin 		if (off == fb->sz) {
50161d06d6bSBaptiste Daroussin 			if (fb->sz == (1U << 31)) {
5027295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL);
50361d06d6bSBaptiste Daroussin 				break;
50461d06d6bSBaptiste Daroussin 			}
50561d06d6bSBaptiste Daroussin 			resize_buf(fb, 65536);
50661d06d6bSBaptiste Daroussin 		}
50761d06d6bSBaptiste Daroussin 		ssz = curp->gzip ?
50861d06d6bSBaptiste Daroussin 		    gzread(gz, fb->buf + (int)off, fb->sz - off) :
50961d06d6bSBaptiste Daroussin 		    read(fd, fb->buf + (int)off, fb->sz - off);
51061d06d6bSBaptiste Daroussin 		if (ssz == 0) {
51161d06d6bSBaptiste Daroussin 			fb->sz = off;
51245a5aec3SBaptiste Daroussin 			retval = 0;
51361d06d6bSBaptiste Daroussin 			break;
51461d06d6bSBaptiste Daroussin 		}
51561d06d6bSBaptiste Daroussin 		if (ssz == -1) {
51661d06d6bSBaptiste Daroussin 			if (curp->gzip)
51761d06d6bSBaptiste Daroussin 				(void)gzerror(gz, &gzerrnum);
51845a5aec3SBaptiste Daroussin 			mandoc_msg(MANDOCERR_READ, 0, 0, "%s",
51961d06d6bSBaptiste Daroussin 			    curp->gzip && gzerrnum != Z_ERRNO ?
52061d06d6bSBaptiste Daroussin 			    zError(gzerrnum) : strerror(errno));
52161d06d6bSBaptiste Daroussin 			break;
52261d06d6bSBaptiste Daroussin 		}
52361d06d6bSBaptiste Daroussin 		off += (size_t)ssz;
52461d06d6bSBaptiste Daroussin 	}
52561d06d6bSBaptiste Daroussin 
52661d06d6bSBaptiste Daroussin 	if (curp->gzip && (gzerrnum = gzclose(gz)) != Z_OK)
52745a5aec3SBaptiste Daroussin 		mandoc_msg(MANDOCERR_GZCLOSE, 0, 0, "%s",
52861d06d6bSBaptiste Daroussin 		    gzerrnum == Z_ERRNO ? strerror(errno) :
52961d06d6bSBaptiste Daroussin 		    zError(gzerrnum));
53045a5aec3SBaptiste Daroussin 	if (retval == -1) {
53161d06d6bSBaptiste Daroussin 		free(fb->buf);
53261d06d6bSBaptiste Daroussin 		fb->buf = NULL;
53361d06d6bSBaptiste Daroussin 	}
53461d06d6bSBaptiste Daroussin 	return retval;
53561d06d6bSBaptiste Daroussin }
53661d06d6bSBaptiste Daroussin 
53761d06d6bSBaptiste Daroussin static void
mparse_end(struct mparse * curp)53861d06d6bSBaptiste Daroussin mparse_end(struct mparse *curp)
53961d06d6bSBaptiste Daroussin {
5407295610fSBaptiste Daroussin 	if (curp->man->meta.macroset == MACROSET_NONE)
5417295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MAN;
5427295610fSBaptiste Daroussin 	if (curp->man->meta.macroset == MACROSET_MDOC)
54361d06d6bSBaptiste Daroussin 		mdoc_endparse(curp->man);
54461d06d6bSBaptiste Daroussin 	else
54561d06d6bSBaptiste Daroussin 		man_endparse(curp->man);
54661d06d6bSBaptiste Daroussin 	roff_endparse(curp->roff);
54761d06d6bSBaptiste Daroussin }
54861d06d6bSBaptiste Daroussin 
5497295610fSBaptiste Daroussin /*
5507295610fSBaptiste Daroussin  * Read the whole file into memory and call the parsers.
5517295610fSBaptiste Daroussin  * Called recursively when an .so request is encountered.
5527295610fSBaptiste Daroussin  */
5537295610fSBaptiste Daroussin void
mparse_readfd(struct mparse * curp,int fd,const char * filename)5547295610fSBaptiste Daroussin mparse_readfd(struct mparse *curp, int fd, const char *filename)
55561d06d6bSBaptiste Daroussin {
55661d06d6bSBaptiste Daroussin 	static int	 recursion_depth;
55761d06d6bSBaptiste Daroussin 
5587295610fSBaptiste Daroussin 	struct buf	 blk;
5597295610fSBaptiste Daroussin 	struct buf	*save_primary;
560*6d38604fSBaptiste Daroussin 	const char	*save_filename, *cp;
5617295610fSBaptiste Daroussin 	size_t		 offset;
5627295610fSBaptiste Daroussin 	int		 save_filenc, save_lineno;
5637295610fSBaptiste Daroussin 	int		 with_mmap;
5647295610fSBaptiste Daroussin 
5657295610fSBaptiste Daroussin 	if (recursion_depth > 64) {
5667295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL);
56761d06d6bSBaptiste Daroussin 		return;
568*6d38604fSBaptiste Daroussin 	} else if (recursion_depth == 0 &&
569*6d38604fSBaptiste Daroussin 	    (cp = strrchr(filename, '.')) != NULL &&
570*6d38604fSBaptiste Daroussin             cp[1] >= '1' && cp[1] <= '9')
571*6d38604fSBaptiste Daroussin                 curp->man->filesec = cp[1];
572*6d38604fSBaptiste Daroussin         else
573*6d38604fSBaptiste Daroussin                 curp->man->filesec = '\0';
574*6d38604fSBaptiste Daroussin 
57545a5aec3SBaptiste Daroussin 	if (read_whole_file(curp, fd, &blk, &with_mmap) == -1)
5767295610fSBaptiste Daroussin 		return;
57761d06d6bSBaptiste Daroussin 
5787295610fSBaptiste Daroussin 	/*
5797295610fSBaptiste Daroussin 	 * Save some properties of the parent file.
5807295610fSBaptiste Daroussin 	 */
5817295610fSBaptiste Daroussin 
5827295610fSBaptiste Daroussin 	save_primary = curp->primary;
5837295610fSBaptiste Daroussin 	save_filenc = curp->filenc;
5847295610fSBaptiste Daroussin 	save_lineno = curp->line;
5857295610fSBaptiste Daroussin 	save_filename = mandoc_msg_getinfilename();
5867295610fSBaptiste Daroussin 
58761d06d6bSBaptiste Daroussin 	curp->primary = &blk;
5887295610fSBaptiste Daroussin 	curp->filenc = curp->options & (MPARSE_UTF8 | MPARSE_LATIN1);
58961d06d6bSBaptiste Daroussin 	curp->line = 1;
5907295610fSBaptiste Daroussin 	mandoc_msg_setinfilename(filename);
59161d06d6bSBaptiste Daroussin 
59261d06d6bSBaptiste Daroussin 	/* Skip an UTF-8 byte order mark. */
59361d06d6bSBaptiste Daroussin 	if (curp->filenc & MPARSE_UTF8 && blk.sz > 2 &&
59461d06d6bSBaptiste Daroussin 	    (unsigned char)blk.buf[0] == 0xef &&
59561d06d6bSBaptiste Daroussin 	    (unsigned char)blk.buf[1] == 0xbb &&
59661d06d6bSBaptiste Daroussin 	    (unsigned char)blk.buf[2] == 0xbf) {
59761d06d6bSBaptiste Daroussin 		offset = 3;
59861d06d6bSBaptiste Daroussin 		curp->filenc &= ~MPARSE_LATIN1;
59961d06d6bSBaptiste Daroussin 	} else
60061d06d6bSBaptiste Daroussin 		offset = 0;
60161d06d6bSBaptiste Daroussin 
6027295610fSBaptiste Daroussin 	recursion_depth++;
60361d06d6bSBaptiste Daroussin 	mparse_buf_r(curp, blk, offset, 1);
60461d06d6bSBaptiste Daroussin 	if (--recursion_depth == 0)
60561d06d6bSBaptiste Daroussin 		mparse_end(curp);
60661d06d6bSBaptiste Daroussin 
60761d06d6bSBaptiste Daroussin 	/*
6087295610fSBaptiste Daroussin 	 * Clean up and restore saved parent properties.
60961d06d6bSBaptiste Daroussin 	 */
61061d06d6bSBaptiste Daroussin 
61161d06d6bSBaptiste Daroussin 	if (with_mmap)
61261d06d6bSBaptiste Daroussin 		munmap(blk.buf, blk.sz);
61361d06d6bSBaptiste Daroussin 	else
61461d06d6bSBaptiste Daroussin 		free(blk.buf);
6157295610fSBaptiste Daroussin 
6167295610fSBaptiste Daroussin 	curp->primary = save_primary;
6177295610fSBaptiste Daroussin 	curp->filenc = save_filenc;
6187295610fSBaptiste Daroussin 	curp->line = save_lineno;
6197295610fSBaptiste Daroussin 	if (save_filename != NULL)
6207295610fSBaptiste Daroussin 		mandoc_msg_setinfilename(save_filename);
62161d06d6bSBaptiste Daroussin }
62261d06d6bSBaptiste Daroussin 
62361d06d6bSBaptiste Daroussin int
mparse_open(struct mparse * curp,const char * file)62461d06d6bSBaptiste Daroussin mparse_open(struct mparse *curp, const char *file)
62561d06d6bSBaptiste Daroussin {
62661d06d6bSBaptiste Daroussin 	char		 *cp;
6277295610fSBaptiste Daroussin 	int		  fd, save_errno;
62861d06d6bSBaptiste Daroussin 
62961d06d6bSBaptiste Daroussin 	cp = strrchr(file, '.');
63061d06d6bSBaptiste Daroussin 	curp->gzip = (cp != NULL && ! strcmp(cp + 1, "gz"));
63161d06d6bSBaptiste Daroussin 
63261d06d6bSBaptiste Daroussin 	/* First try to use the filename as it is. */
63361d06d6bSBaptiste Daroussin 
63461d06d6bSBaptiste Daroussin 	if ((fd = open(file, O_RDONLY)) != -1)
63561d06d6bSBaptiste Daroussin 		return fd;
63661d06d6bSBaptiste Daroussin 
63761d06d6bSBaptiste Daroussin 	/*
63861d06d6bSBaptiste Daroussin 	 * If that doesn't work and the filename doesn't
63961d06d6bSBaptiste Daroussin 	 * already  end in .gz, try appending .gz.
64061d06d6bSBaptiste Daroussin 	 */
64161d06d6bSBaptiste Daroussin 
64261d06d6bSBaptiste Daroussin 	if ( ! curp->gzip) {
6437295610fSBaptiste Daroussin 		save_errno = errno;
64461d06d6bSBaptiste Daroussin 		mandoc_asprintf(&cp, "%s.gz", file);
64561d06d6bSBaptiste Daroussin 		fd = open(cp, O_RDONLY);
64661d06d6bSBaptiste Daroussin 		free(cp);
6477295610fSBaptiste Daroussin 		errno = save_errno;
64861d06d6bSBaptiste Daroussin 		if (fd != -1) {
64961d06d6bSBaptiste Daroussin 			curp->gzip = 1;
65061d06d6bSBaptiste Daroussin 			return fd;
65161d06d6bSBaptiste Daroussin 		}
65261d06d6bSBaptiste Daroussin 	}
65361d06d6bSBaptiste Daroussin 
65461d06d6bSBaptiste Daroussin 	/* Neither worked, give up. */
65561d06d6bSBaptiste Daroussin 
65661d06d6bSBaptiste Daroussin 	return -1;
65761d06d6bSBaptiste Daroussin }
65861d06d6bSBaptiste Daroussin 
65961d06d6bSBaptiste Daroussin struct mparse *
mparse_alloc(int options,enum mandoc_os os_e,const char * os_s)6607295610fSBaptiste Daroussin mparse_alloc(int options, enum mandoc_os os_e, const char *os_s)
66161d06d6bSBaptiste Daroussin {
66261d06d6bSBaptiste Daroussin 	struct mparse	*curp;
66361d06d6bSBaptiste Daroussin 
66461d06d6bSBaptiste Daroussin 	curp = mandoc_calloc(1, sizeof(struct mparse));
66561d06d6bSBaptiste Daroussin 
66661d06d6bSBaptiste Daroussin 	curp->options = options;
66761d06d6bSBaptiste Daroussin 	curp->os_s = os_s;
66861d06d6bSBaptiste Daroussin 
6697295610fSBaptiste Daroussin 	curp->roff = roff_alloc(options);
6707295610fSBaptiste Daroussin 	curp->man = roff_man_alloc(curp->roff, curp->os_s,
67161d06d6bSBaptiste Daroussin 		curp->options & MPARSE_QUICK ? 1 : 0);
67261d06d6bSBaptiste Daroussin 	if (curp->options & MPARSE_MDOC) {
6737295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MDOC;
67461d06d6bSBaptiste Daroussin 		if (curp->man->mdocmac == NULL)
67561d06d6bSBaptiste Daroussin 			curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX);
67661d06d6bSBaptiste Daroussin 	} else if (curp->options & MPARSE_MAN) {
6777295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MAN;
67861d06d6bSBaptiste Daroussin 		if (curp->man->manmac == NULL)
67961d06d6bSBaptiste Daroussin 			curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX);
68061d06d6bSBaptiste Daroussin 	}
6817295610fSBaptiste Daroussin 	curp->man->meta.first->tok = TOKEN_NONE;
68261d06d6bSBaptiste Daroussin 	curp->man->meta.os_e = os_e;
683*6d38604fSBaptiste Daroussin 	tag_alloc();
68461d06d6bSBaptiste Daroussin 	return curp;
68561d06d6bSBaptiste Daroussin }
68661d06d6bSBaptiste Daroussin 
68761d06d6bSBaptiste Daroussin void
mparse_reset(struct mparse * curp)68861d06d6bSBaptiste Daroussin mparse_reset(struct mparse *curp)
68961d06d6bSBaptiste Daroussin {
690*6d38604fSBaptiste Daroussin 	tag_free();
69161d06d6bSBaptiste Daroussin 	roff_reset(curp->roff);
69261d06d6bSBaptiste Daroussin 	roff_man_reset(curp->man);
6937295610fSBaptiste Daroussin 	free_buf_list(curp->secondary);
6947295610fSBaptiste Daroussin 	curp->secondary = NULL;
69561d06d6bSBaptiste Daroussin 	curp->gzip = 0;
696*6d38604fSBaptiste Daroussin 	tag_alloc();
69761d06d6bSBaptiste Daroussin }
69861d06d6bSBaptiste Daroussin 
69961d06d6bSBaptiste Daroussin void
mparse_free(struct mparse * curp)70061d06d6bSBaptiste Daroussin mparse_free(struct mparse *curp)
70161d06d6bSBaptiste Daroussin {
702*6d38604fSBaptiste Daroussin 	tag_free();
70361d06d6bSBaptiste Daroussin 	roffhash_free(curp->man->mdocmac);
70461d06d6bSBaptiste Daroussin 	roffhash_free(curp->man->manmac);
70561d06d6bSBaptiste Daroussin 	roff_man_free(curp->man);
70661d06d6bSBaptiste Daroussin 	roff_free(curp->roff);
7077295610fSBaptiste Daroussin 	free_buf_list(curp->secondary);
70861d06d6bSBaptiste Daroussin 	free(curp);
70961d06d6bSBaptiste Daroussin }
71061d06d6bSBaptiste Daroussin 
7117295610fSBaptiste Daroussin struct roff_meta *
mparse_result(struct mparse * curp)7127295610fSBaptiste Daroussin mparse_result(struct mparse *curp)
71361d06d6bSBaptiste Daroussin {
7147295610fSBaptiste Daroussin 	roff_state_reset(curp->man);
7157295610fSBaptiste Daroussin 	if (curp->options & MPARSE_VALIDATE) {
7167295610fSBaptiste Daroussin 		if (curp->man->meta.macroset == MACROSET_MDOC)
7177295610fSBaptiste Daroussin 			mdoc_validate(curp->man);
7187295610fSBaptiste Daroussin 		else
7197295610fSBaptiste Daroussin 			man_validate(curp->man);
720*6d38604fSBaptiste Daroussin 		tag_postprocess(curp->man, curp->man->meta.first);
72161d06d6bSBaptiste Daroussin 	}
7227295610fSBaptiste Daroussin 	return &curp->man->meta;
72361d06d6bSBaptiste Daroussin }
72461d06d6bSBaptiste Daroussin 
72561d06d6bSBaptiste Daroussin void
mparse_copy(const struct mparse * p)7267295610fSBaptiste Daroussin mparse_copy(const struct mparse *p)
72761d06d6bSBaptiste Daroussin {
7287295610fSBaptiste Daroussin 	struct buf	*buf;
72961d06d6bSBaptiste Daroussin 
7307295610fSBaptiste Daroussin 	for (buf = p->secondary; buf != NULL; buf = buf->next)
7317295610fSBaptiste Daroussin 		puts(buf->buf);
73261d06d6bSBaptiste Daroussin }
733