xref: /freebsd/contrib/mandoc/read.c (revision 45a5aec3f156d8a01fe1ea4ec87c1b1d489f13ac)
1*45a5aec3SBaptiste Daroussin /*	$Id: read.c,v 1.214 2019/07/10 19:39:01 schwarze Exp $ */
261d06d6bSBaptiste Daroussin /*
361d06d6bSBaptiste Daroussin  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
47295610fSBaptiste Daroussin  * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org>
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.
1861d06d6bSBaptiste Daroussin  */
1961d06d6bSBaptiste Daroussin #include "config.h"
2061d06d6bSBaptiste Daroussin 
2161d06d6bSBaptiste Daroussin #include <sys/types.h>
2261d06d6bSBaptiste Daroussin #include <sys/mman.h>
2361d06d6bSBaptiste Daroussin #include <sys/stat.h>
2461d06d6bSBaptiste Daroussin 
2561d06d6bSBaptiste Daroussin #include <assert.h>
2661d06d6bSBaptiste Daroussin #include <ctype.h>
2761d06d6bSBaptiste Daroussin #include <errno.h>
2861d06d6bSBaptiste Daroussin #include <fcntl.h>
2961d06d6bSBaptiste Daroussin #include <stdarg.h>
3061d06d6bSBaptiste Daroussin #include <stdio.h>
3161d06d6bSBaptiste Daroussin #include <stdlib.h>
3261d06d6bSBaptiste Daroussin #include <string.h>
3361d06d6bSBaptiste Daroussin #include <unistd.h>
3461d06d6bSBaptiste Daroussin #include <zlib.h>
3561d06d6bSBaptiste Daroussin 
3661d06d6bSBaptiste Daroussin #include "mandoc_aux.h"
3761d06d6bSBaptiste Daroussin #include "mandoc.h"
3861d06d6bSBaptiste Daroussin #include "roff.h"
3961d06d6bSBaptiste Daroussin #include "mdoc.h"
4061d06d6bSBaptiste Daroussin #include "man.h"
417295610fSBaptiste Daroussin #include "mandoc_parse.h"
4261d06d6bSBaptiste Daroussin #include "libmandoc.h"
437295610fSBaptiste Daroussin #include "roff_int.h"
4461d06d6bSBaptiste Daroussin 
4561d06d6bSBaptiste Daroussin #define	REPARSE_LIMIT	1000
4661d06d6bSBaptiste Daroussin 
4761d06d6bSBaptiste Daroussin struct	mparse {
4861d06d6bSBaptiste Daroussin 	struct roff	 *roff; /* roff parser (!NULL) */
4961d06d6bSBaptiste Daroussin 	struct roff_man	 *man; /* man parser */
5061d06d6bSBaptiste Daroussin 	struct buf	 *primary; /* buffer currently being parsed */
517295610fSBaptiste Daroussin 	struct buf	 *secondary; /* copy of top level input */
527295610fSBaptiste Daroussin 	struct buf	 *loop; /* open .while request line */
5361d06d6bSBaptiste Daroussin 	const char	 *os_s; /* default operating system */
5461d06d6bSBaptiste Daroussin 	int		  options; /* parser options */
5561d06d6bSBaptiste Daroussin 	int		  gzip; /* current input file is gzipped */
5661d06d6bSBaptiste Daroussin 	int		  filenc; /* encoding of the current file */
5761d06d6bSBaptiste Daroussin 	int		  reparse_count; /* finite interp. stack */
5861d06d6bSBaptiste Daroussin 	int		  line; /* line number in the file */
5961d06d6bSBaptiste Daroussin };
6061d06d6bSBaptiste Daroussin 
6161d06d6bSBaptiste Daroussin static	void	  choose_parser(struct mparse *);
627295610fSBaptiste Daroussin static	void	  free_buf_list(struct buf *);
6361d06d6bSBaptiste Daroussin static	void	  resize_buf(struct buf *, size_t);
6461d06d6bSBaptiste Daroussin static	int	  mparse_buf_r(struct mparse *, struct buf, size_t, int);
657295610fSBaptiste Daroussin static	int	  read_whole_file(struct mparse *, int, struct buf *, int *);
6661d06d6bSBaptiste Daroussin static	void	  mparse_end(struct mparse *);
6761d06d6bSBaptiste Daroussin 
6861d06d6bSBaptiste Daroussin 
6961d06d6bSBaptiste Daroussin static void
7061d06d6bSBaptiste Daroussin resize_buf(struct buf *buf, size_t initial)
7161d06d6bSBaptiste Daroussin {
7261d06d6bSBaptiste Daroussin 
7361d06d6bSBaptiste Daroussin 	buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
7461d06d6bSBaptiste Daroussin 	buf->buf = mandoc_realloc(buf->buf, buf->sz);
7561d06d6bSBaptiste Daroussin }
7661d06d6bSBaptiste Daroussin 
7761d06d6bSBaptiste Daroussin static void
787295610fSBaptiste Daroussin free_buf_list(struct buf *buf)
797295610fSBaptiste Daroussin {
807295610fSBaptiste Daroussin 	struct buf *tmp;
817295610fSBaptiste Daroussin 
827295610fSBaptiste Daroussin 	while (buf != NULL) {
837295610fSBaptiste Daroussin 		tmp = buf;
847295610fSBaptiste Daroussin 		buf = tmp->next;
857295610fSBaptiste Daroussin 		free(tmp->buf);
867295610fSBaptiste Daroussin 		free(tmp);
877295610fSBaptiste Daroussin 	}
887295610fSBaptiste Daroussin }
897295610fSBaptiste Daroussin 
907295610fSBaptiste Daroussin static void
9161d06d6bSBaptiste Daroussin choose_parser(struct mparse *curp)
9261d06d6bSBaptiste Daroussin {
9361d06d6bSBaptiste Daroussin 	char		*cp, *ep;
9461d06d6bSBaptiste Daroussin 	int		 format;
9561d06d6bSBaptiste Daroussin 
9661d06d6bSBaptiste Daroussin 	/*
9761d06d6bSBaptiste Daroussin 	 * If neither command line arguments -mdoc or -man select
9861d06d6bSBaptiste Daroussin 	 * a parser nor the roff parser found a .Dd or .TH macro
9961d06d6bSBaptiste Daroussin 	 * yet, look ahead in the main input buffer.
10061d06d6bSBaptiste Daroussin 	 */
10161d06d6bSBaptiste Daroussin 
10261d06d6bSBaptiste Daroussin 	if ((format = roff_getformat(curp->roff)) == 0) {
10361d06d6bSBaptiste Daroussin 		cp = curp->primary->buf;
10461d06d6bSBaptiste Daroussin 		ep = cp + curp->primary->sz;
10561d06d6bSBaptiste Daroussin 		while (cp < ep) {
10661d06d6bSBaptiste Daroussin 			if (*cp == '.' || *cp == '\'') {
10761d06d6bSBaptiste Daroussin 				cp++;
10861d06d6bSBaptiste Daroussin 				if (cp[0] == 'D' && cp[1] == 'd') {
10961d06d6bSBaptiste Daroussin 					format = MPARSE_MDOC;
11061d06d6bSBaptiste Daroussin 					break;
11161d06d6bSBaptiste Daroussin 				}
11261d06d6bSBaptiste Daroussin 				if (cp[0] == 'T' && cp[1] == 'H') {
11361d06d6bSBaptiste Daroussin 					format = MPARSE_MAN;
11461d06d6bSBaptiste Daroussin 					break;
11561d06d6bSBaptiste Daroussin 				}
11661d06d6bSBaptiste Daroussin 			}
11761d06d6bSBaptiste Daroussin 			cp = memchr(cp, '\n', ep - cp);
11861d06d6bSBaptiste Daroussin 			if (cp == NULL)
11961d06d6bSBaptiste Daroussin 				break;
12061d06d6bSBaptiste Daroussin 			cp++;
12161d06d6bSBaptiste Daroussin 		}
12261d06d6bSBaptiste Daroussin 	}
12361d06d6bSBaptiste Daroussin 
12461d06d6bSBaptiste Daroussin 	if (format == MPARSE_MDOC) {
1257295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MDOC;
12661d06d6bSBaptiste Daroussin 		if (curp->man->mdocmac == NULL)
12761d06d6bSBaptiste Daroussin 			curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX);
12861d06d6bSBaptiste Daroussin 	} else {
1297295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MAN;
13061d06d6bSBaptiste Daroussin 		if (curp->man->manmac == NULL)
13161d06d6bSBaptiste Daroussin 			curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX);
13261d06d6bSBaptiste Daroussin 	}
1337295610fSBaptiste Daroussin 	curp->man->meta.first->tok = TOKEN_NONE;
13461d06d6bSBaptiste Daroussin }
13561d06d6bSBaptiste Daroussin 
13661d06d6bSBaptiste Daroussin /*
13761d06d6bSBaptiste Daroussin  * Main parse routine for a buffer.
13861d06d6bSBaptiste Daroussin  * It assumes encoding and line numbering are already set up.
13961d06d6bSBaptiste Daroussin  * It can recurse directly (for invocations of user-defined
14061d06d6bSBaptiste Daroussin  * macros, inline equations, and input line traps)
14161d06d6bSBaptiste Daroussin  * and indirectly (for .so file inclusion).
14261d06d6bSBaptiste Daroussin  */
14361d06d6bSBaptiste Daroussin static int
14461d06d6bSBaptiste Daroussin mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start)
14561d06d6bSBaptiste Daroussin {
14661d06d6bSBaptiste Daroussin 	struct buf	 ln;
1477295610fSBaptiste Daroussin 	struct buf	*firstln, *lastln, *thisln, *loop;
14861d06d6bSBaptiste Daroussin 	char		*cp;
14961d06d6bSBaptiste Daroussin 	size_t		 pos; /* byte number in the ln buffer */
1507295610fSBaptiste Daroussin 	int		 line_result, result;
15161d06d6bSBaptiste Daroussin 	int		 of;
15261d06d6bSBaptiste Daroussin 	int		 lnn; /* line number in the real file */
15361d06d6bSBaptiste Daroussin 	int		 fd;
1547295610fSBaptiste Daroussin 	int		 inloop; /* Saw .while on this level. */
15561d06d6bSBaptiste Daroussin 	unsigned char	 c;
15661d06d6bSBaptiste Daroussin 
1577295610fSBaptiste Daroussin 	ln.sz = 256;
1587295610fSBaptiste Daroussin 	ln.buf = mandoc_malloc(ln.sz);
1597295610fSBaptiste Daroussin 	ln.next = NULL;
160*45a5aec3SBaptiste Daroussin 	firstln = lastln = loop = NULL;
16161d06d6bSBaptiste Daroussin 	lnn = curp->line;
16261d06d6bSBaptiste Daroussin 	pos = 0;
1637295610fSBaptiste Daroussin 	inloop = 0;
1647295610fSBaptiste Daroussin 	result = ROFF_CONT;
16561d06d6bSBaptiste Daroussin 
1667295610fSBaptiste Daroussin 	while (i < blk.sz && (blk.buf[i] != '\0' || pos != 0)) {
16761d06d6bSBaptiste Daroussin 		if (start) {
16861d06d6bSBaptiste Daroussin 			curp->line = lnn;
16961d06d6bSBaptiste Daroussin 			curp->reparse_count = 0;
17061d06d6bSBaptiste Daroussin 
17161d06d6bSBaptiste Daroussin 			if (lnn < 3 &&
17261d06d6bSBaptiste Daroussin 			    curp->filenc & MPARSE_UTF8 &&
17361d06d6bSBaptiste Daroussin 			    curp->filenc & MPARSE_LATIN1)
17461d06d6bSBaptiste Daroussin 				curp->filenc = preconv_cue(&blk, i);
17561d06d6bSBaptiste Daroussin 		}
17661d06d6bSBaptiste Daroussin 
17761d06d6bSBaptiste Daroussin 		while (i < blk.sz && (start || blk.buf[i] != '\0')) {
17861d06d6bSBaptiste Daroussin 
17961d06d6bSBaptiste Daroussin 			/*
18061d06d6bSBaptiste Daroussin 			 * When finding an unescaped newline character,
18161d06d6bSBaptiste Daroussin 			 * leave the character loop to process the line.
18261d06d6bSBaptiste Daroussin 			 * Skip a preceding carriage return, if any.
18361d06d6bSBaptiste Daroussin 			 */
18461d06d6bSBaptiste Daroussin 
18561d06d6bSBaptiste Daroussin 			if ('\r' == blk.buf[i] && i + 1 < blk.sz &&
18661d06d6bSBaptiste Daroussin 			    '\n' == blk.buf[i + 1])
18761d06d6bSBaptiste Daroussin 				++i;
18861d06d6bSBaptiste Daroussin 			if ('\n' == blk.buf[i]) {
18961d06d6bSBaptiste Daroussin 				++i;
19061d06d6bSBaptiste Daroussin 				++lnn;
19161d06d6bSBaptiste Daroussin 				break;
19261d06d6bSBaptiste Daroussin 			}
19361d06d6bSBaptiste Daroussin 
19461d06d6bSBaptiste Daroussin 			/*
19561d06d6bSBaptiste Daroussin 			 * Make sure we have space for the worst
1967295610fSBaptiste Daroussin 			 * case of 12 bytes: "\\[u10ffff]\n\0"
19761d06d6bSBaptiste Daroussin 			 */
19861d06d6bSBaptiste Daroussin 
1997295610fSBaptiste Daroussin 			if (pos + 12 > ln.sz)
20061d06d6bSBaptiste Daroussin 				resize_buf(&ln, 256);
20161d06d6bSBaptiste Daroussin 
20261d06d6bSBaptiste Daroussin 			/*
20361d06d6bSBaptiste Daroussin 			 * Encode 8-bit input.
20461d06d6bSBaptiste Daroussin 			 */
20561d06d6bSBaptiste Daroussin 
20661d06d6bSBaptiste Daroussin 			c = blk.buf[i];
20761d06d6bSBaptiste Daroussin 			if (c & 0x80) {
20861d06d6bSBaptiste Daroussin 				if ( ! (curp->filenc && preconv_encode(
20961d06d6bSBaptiste Daroussin 				    &blk, &i, &ln, &pos, &curp->filenc))) {
2107295610fSBaptiste Daroussin 					mandoc_msg(MANDOCERR_CHAR_BAD,
21161d06d6bSBaptiste Daroussin 					    curp->line, pos, "0x%x", c);
21261d06d6bSBaptiste Daroussin 					ln.buf[pos++] = '?';
21361d06d6bSBaptiste Daroussin 					i++;
21461d06d6bSBaptiste Daroussin 				}
21561d06d6bSBaptiste Daroussin 				continue;
21661d06d6bSBaptiste Daroussin 			}
21761d06d6bSBaptiste Daroussin 
21861d06d6bSBaptiste Daroussin 			/*
21961d06d6bSBaptiste Daroussin 			 * Exclude control characters.
22061d06d6bSBaptiste Daroussin 			 */
22161d06d6bSBaptiste Daroussin 
22261d06d6bSBaptiste Daroussin 			if (c == 0x7f || (c < 0x20 && c != 0x09)) {
2237295610fSBaptiste Daroussin 				mandoc_msg(c == 0x00 || c == 0x04 ||
22461d06d6bSBaptiste Daroussin 				    c > 0x0a ? MANDOCERR_CHAR_BAD :
22561d06d6bSBaptiste Daroussin 				    MANDOCERR_CHAR_UNSUPP,
2267295610fSBaptiste Daroussin 				    curp->line, pos, "0x%x", c);
22761d06d6bSBaptiste Daroussin 				i++;
22861d06d6bSBaptiste Daroussin 				if (c != '\r')
22961d06d6bSBaptiste Daroussin 					ln.buf[pos++] = '?';
23061d06d6bSBaptiste Daroussin 				continue;
23161d06d6bSBaptiste Daroussin 			}
23261d06d6bSBaptiste Daroussin 
23361d06d6bSBaptiste Daroussin 			ln.buf[pos++] = blk.buf[i++];
23461d06d6bSBaptiste Daroussin 		}
2357295610fSBaptiste Daroussin 		ln.buf[pos] = '\0';
23661d06d6bSBaptiste Daroussin 
2377295610fSBaptiste Daroussin 		/*
2387295610fSBaptiste Daroussin 		 * Maintain a lookaside buffer of all lines.
2397295610fSBaptiste Daroussin 		 * parsed from this input source.
2407295610fSBaptiste Daroussin 		 */
24161d06d6bSBaptiste Daroussin 
2427295610fSBaptiste Daroussin 		thisln = mandoc_malloc(sizeof(*thisln));
2437295610fSBaptiste Daroussin 		thisln->buf = mandoc_strdup(ln.buf);
2447295610fSBaptiste Daroussin 		thisln->sz = strlen(ln.buf) + 1;
2457295610fSBaptiste Daroussin 		thisln->next = NULL;
2467295610fSBaptiste Daroussin 		if (firstln == NULL) {
2477295610fSBaptiste Daroussin 			firstln = lastln = thisln;
2487295610fSBaptiste Daroussin 			if (curp->secondary == NULL)
2497295610fSBaptiste Daroussin 				curp->secondary = firstln;
2507295610fSBaptiste Daroussin 		} else {
2517295610fSBaptiste Daroussin 			lastln->next = thisln;
2527295610fSBaptiste Daroussin 			lastln = thisln;
2537295610fSBaptiste Daroussin 		}
2547295610fSBaptiste Daroussin 
2557295610fSBaptiste Daroussin 		/* XXX Ugly hack to mark the end of the input. */
2567295610fSBaptiste Daroussin 
2577295610fSBaptiste Daroussin 		if (i == blk.sz || blk.buf[i] == '\0') {
258*45a5aec3SBaptiste Daroussin 			if (pos + 2 > ln.sz)
259*45a5aec3SBaptiste Daroussin 				resize_buf(&ln, 256);
26061d06d6bSBaptiste Daroussin 			ln.buf[pos++] = '\n';
26161d06d6bSBaptiste Daroussin 			ln.buf[pos] = '\0';
2627295610fSBaptiste Daroussin 		}
26361d06d6bSBaptiste Daroussin 
26461d06d6bSBaptiste Daroussin 		/*
26561d06d6bSBaptiste Daroussin 		 * A significant amount of complexity is contained by
26661d06d6bSBaptiste Daroussin 		 * the roff preprocessor.  It's line-oriented but can be
26761d06d6bSBaptiste Daroussin 		 * expressed on one line, so we need at times to
26861d06d6bSBaptiste Daroussin 		 * readjust our starting point and re-run it.  The roff
26961d06d6bSBaptiste Daroussin 		 * preprocessor can also readjust the buffers with new
27061d06d6bSBaptiste Daroussin 		 * data, so we pass them in wholesale.
27161d06d6bSBaptiste Daroussin 		 */
27261d06d6bSBaptiste Daroussin 
27361d06d6bSBaptiste Daroussin 		of = 0;
27461d06d6bSBaptiste Daroussin rerun:
2757295610fSBaptiste Daroussin 		line_result = roff_parseln(curp->roff, curp->line, &ln, &of);
27661d06d6bSBaptiste Daroussin 
2777295610fSBaptiste Daroussin 		/* Process options. */
2787295610fSBaptiste Daroussin 
2797295610fSBaptiste Daroussin 		if (line_result & ROFF_APPEND)
2807295610fSBaptiste Daroussin 			assert(line_result == (ROFF_IGN | ROFF_APPEND));
2817295610fSBaptiste Daroussin 
2827295610fSBaptiste Daroussin 		if (line_result & ROFF_USERCALL)
2837295610fSBaptiste Daroussin 			assert((line_result & ROFF_MASK) == ROFF_REPARSE);
2847295610fSBaptiste Daroussin 
2857295610fSBaptiste Daroussin 		if (line_result & ROFF_USERRET) {
2867295610fSBaptiste Daroussin 			assert(line_result == (ROFF_IGN | ROFF_USERRET));
2877295610fSBaptiste Daroussin 			if (start == 0) {
2887295610fSBaptiste Daroussin 				/* Return from the current macro. */
2897295610fSBaptiste Daroussin 				result = ROFF_USERRET;
2907295610fSBaptiste Daroussin 				goto out;
29161d06d6bSBaptiste Daroussin 			}
2927295610fSBaptiste Daroussin 		}
2937295610fSBaptiste Daroussin 
2947295610fSBaptiste Daroussin 		switch (line_result & ROFF_LOOPMASK) {
2957295610fSBaptiste Daroussin 		case ROFF_IGN:
2967295610fSBaptiste Daroussin 			break;
2977295610fSBaptiste Daroussin 		case ROFF_WHILE:
2987295610fSBaptiste Daroussin 			if (curp->loop != NULL) {
2997295610fSBaptiste Daroussin 				if (loop == curp->loop)
3007295610fSBaptiste Daroussin 					break;
3017295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_WHILE_NEST,
3027295610fSBaptiste Daroussin 				    curp->line, pos, NULL);
3037295610fSBaptiste Daroussin 			}
3047295610fSBaptiste Daroussin 			curp->loop = thisln;
3057295610fSBaptiste Daroussin 			loop = NULL;
3067295610fSBaptiste Daroussin 			inloop = 1;
3077295610fSBaptiste Daroussin 			break;
3087295610fSBaptiste Daroussin 		case ROFF_LOOPCONT:
3097295610fSBaptiste Daroussin 		case ROFF_LOOPEXIT:
3107295610fSBaptiste Daroussin 			if (curp->loop == NULL) {
3117295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_WHILE_FAIL,
3127295610fSBaptiste Daroussin 				    curp->line, pos, NULL);
3137295610fSBaptiste Daroussin 				break;
3147295610fSBaptiste Daroussin 			}
3157295610fSBaptiste Daroussin 			if (inloop == 0) {
3167295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_WHILE_INTO,
3177295610fSBaptiste Daroussin 				    curp->line, pos, NULL);
3187295610fSBaptiste Daroussin 				curp->loop = loop = NULL;
3197295610fSBaptiste Daroussin 				break;
3207295610fSBaptiste Daroussin 			}
3217295610fSBaptiste Daroussin 			if (line_result & ROFF_LOOPCONT)
3227295610fSBaptiste Daroussin 				loop = curp->loop;
3237295610fSBaptiste Daroussin 			else {
3247295610fSBaptiste Daroussin 				curp->loop = loop = NULL;
3257295610fSBaptiste Daroussin 				inloop = 0;
3267295610fSBaptiste Daroussin 			}
3277295610fSBaptiste Daroussin 			break;
3287295610fSBaptiste Daroussin 		default:
3297295610fSBaptiste Daroussin 			abort();
3307295610fSBaptiste Daroussin 		}
3317295610fSBaptiste Daroussin 
3327295610fSBaptiste Daroussin 		/* Process the main instruction from the roff parser. */
3337295610fSBaptiste Daroussin 
3347295610fSBaptiste Daroussin 		switch (line_result & ROFF_MASK) {
3357295610fSBaptiste Daroussin 		case ROFF_IGN:
3367295610fSBaptiste Daroussin 			break;
3377295610fSBaptiste Daroussin 		case ROFF_CONT:
3387295610fSBaptiste Daroussin 			if (curp->man->meta.macroset == MACROSET_NONE)
3397295610fSBaptiste Daroussin 				choose_parser(curp);
3407295610fSBaptiste Daroussin 			if ((curp->man->meta.macroset == MACROSET_MDOC ?
3417295610fSBaptiste Daroussin 			     mdoc_parseln(curp->man, curp->line, ln.buf, of) :
3427295610fSBaptiste Daroussin 			     man_parseln(curp->man, curp->line, ln.buf, of)
3437295610fSBaptiste Daroussin 			    ) == 2)
3447295610fSBaptiste Daroussin 				goto out;
3457295610fSBaptiste Daroussin 			break;
34661d06d6bSBaptiste Daroussin 		case ROFF_RERUN:
34761d06d6bSBaptiste Daroussin 			goto rerun;
3487295610fSBaptiste Daroussin 		case ROFF_REPARSE:
3497295610fSBaptiste Daroussin 			if (++curp->reparse_count > REPARSE_LIMIT) {
3507295610fSBaptiste Daroussin 				/* Abort and return to the top level. */
3517295610fSBaptiste Daroussin 				result = ROFF_IGN;
3527295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_ROFFLOOP,
3537295610fSBaptiste Daroussin 				    curp->line, pos, NULL);
3547295610fSBaptiste Daroussin 				goto out;
3557295610fSBaptiste Daroussin 			}
3567295610fSBaptiste Daroussin 			result = mparse_buf_r(curp, ln, of, 0);
3577295610fSBaptiste Daroussin 			if (line_result & ROFF_USERCALL) {
3587295610fSBaptiste Daroussin 				roff_userret(curp->roff);
3597295610fSBaptiste Daroussin 				/* Continue normally. */
3607295610fSBaptiste Daroussin 				if (result & ROFF_USERRET)
3617295610fSBaptiste Daroussin 					result = ROFF_CONT;
3627295610fSBaptiste Daroussin 			}
3637295610fSBaptiste Daroussin 			if (start == 0 && result != ROFF_CONT)
3647295610fSBaptiste Daroussin 				goto out;
3657295610fSBaptiste Daroussin 			break;
36661d06d6bSBaptiste Daroussin 		case ROFF_SO:
36761d06d6bSBaptiste Daroussin 			if ( ! (curp->options & MPARSE_SO) &&
36861d06d6bSBaptiste Daroussin 			    (i >= blk.sz || blk.buf[i] == '\0')) {
3697295610fSBaptiste Daroussin 				curp->man->meta.sodest =
3707295610fSBaptiste Daroussin 				    mandoc_strdup(ln.buf + of);
3717295610fSBaptiste Daroussin 				goto out;
37261d06d6bSBaptiste Daroussin 			}
37361d06d6bSBaptiste Daroussin 			if ((fd = mparse_open(curp, ln.buf + of)) != -1) {
37461d06d6bSBaptiste Daroussin 				mparse_readfd(curp, fd, ln.buf + of);
37561d06d6bSBaptiste Daroussin 				close(fd);
37661d06d6bSBaptiste Daroussin 			} else {
3777295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_SO_FAIL,
3787295610fSBaptiste Daroussin 				    curp->line, of, ".so %s: %s",
3797295610fSBaptiste Daroussin 				    ln.buf + of, strerror(errno));
38061d06d6bSBaptiste Daroussin 				ln.sz = mandoc_asprintf(&cp,
38161d06d6bSBaptiste Daroussin 				    ".sp\nSee the file %s.\n.sp",
38261d06d6bSBaptiste Daroussin 				    ln.buf + of);
38361d06d6bSBaptiste Daroussin 				free(ln.buf);
38461d06d6bSBaptiste Daroussin 				ln.buf = cp;
38561d06d6bSBaptiste Daroussin 				of = 0;
38661d06d6bSBaptiste Daroussin 				mparse_buf_r(curp, ln, of, 0);
38761d06d6bSBaptiste Daroussin 			}
3887295610fSBaptiste Daroussin 			break;
38961d06d6bSBaptiste Daroussin 		default:
3907295610fSBaptiste Daroussin 			abort();
39161d06d6bSBaptiste Daroussin 		}
39261d06d6bSBaptiste Daroussin 
39361d06d6bSBaptiste Daroussin 		/* Start the next input line. */
39461d06d6bSBaptiste Daroussin 
3957295610fSBaptiste Daroussin 		if (loop != NULL &&
3967295610fSBaptiste Daroussin 		    (line_result & ROFF_LOOPMASK) == ROFF_IGN)
3977295610fSBaptiste Daroussin 			loop = loop->next;
3987295610fSBaptiste Daroussin 
3997295610fSBaptiste Daroussin 		if (loop != NULL) {
4007295610fSBaptiste Daroussin 			if ((line_result & ROFF_APPEND) == 0)
4017295610fSBaptiste Daroussin 				*ln.buf = '\0';
4027295610fSBaptiste Daroussin 			if (ln.sz < loop->sz)
4037295610fSBaptiste Daroussin 				resize_buf(&ln, loop->sz);
4047295610fSBaptiste Daroussin 			(void)strlcat(ln.buf, loop->buf, ln.sz);
4057295610fSBaptiste Daroussin 			of = 0;
4067295610fSBaptiste Daroussin 			goto rerun;
40761d06d6bSBaptiste Daroussin 		}
40861d06d6bSBaptiste Daroussin 
4097295610fSBaptiste Daroussin 		pos = (line_result & ROFF_APPEND) ? strlen(ln.buf) : 0;
4107295610fSBaptiste Daroussin 	}
4117295610fSBaptiste Daroussin out:
4127295610fSBaptiste Daroussin 	if (inloop) {
4137295610fSBaptiste Daroussin 		if (result != ROFF_USERRET)
4147295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_WHILE_OUTOF,
4157295610fSBaptiste Daroussin 			    curp->line, pos, NULL);
4167295610fSBaptiste Daroussin 		curp->loop = NULL;
4177295610fSBaptiste Daroussin 	}
41861d06d6bSBaptiste Daroussin 	free(ln.buf);
4197295610fSBaptiste Daroussin 	if (firstln != curp->secondary)
4207295610fSBaptiste Daroussin 		free_buf_list(firstln);
4217295610fSBaptiste Daroussin 	return result;
42261d06d6bSBaptiste Daroussin }
42361d06d6bSBaptiste Daroussin 
42461d06d6bSBaptiste Daroussin static int
4257295610fSBaptiste Daroussin read_whole_file(struct mparse *curp, int fd, struct buf *fb, int *with_mmap)
42661d06d6bSBaptiste Daroussin {
42761d06d6bSBaptiste Daroussin 	struct stat	 st;
42861d06d6bSBaptiste Daroussin 	gzFile		 gz;
42961d06d6bSBaptiste Daroussin 	size_t		 off;
43061d06d6bSBaptiste Daroussin 	ssize_t		 ssz;
43161d06d6bSBaptiste Daroussin 	int		 gzerrnum, retval;
43261d06d6bSBaptiste Daroussin 
43361d06d6bSBaptiste Daroussin 	if (fstat(fd, &st) == -1) {
434*45a5aec3SBaptiste Daroussin 		mandoc_msg(MANDOCERR_FSTAT, 0, 0, "%s", strerror(errno));
435*45a5aec3SBaptiste Daroussin 		return -1;
43661d06d6bSBaptiste Daroussin 	}
43761d06d6bSBaptiste Daroussin 
43861d06d6bSBaptiste Daroussin 	/*
43961d06d6bSBaptiste Daroussin 	 * If we're a regular file, try just reading in the whole entry
44061d06d6bSBaptiste Daroussin 	 * via mmap().  This is faster than reading it into blocks, and
44161d06d6bSBaptiste Daroussin 	 * since each file is only a few bytes to begin with, I'm not
44261d06d6bSBaptiste Daroussin 	 * concerned that this is going to tank any machines.
44361d06d6bSBaptiste Daroussin 	 */
44461d06d6bSBaptiste Daroussin 
44561d06d6bSBaptiste Daroussin 	if (curp->gzip == 0 && S_ISREG(st.st_mode)) {
44661d06d6bSBaptiste Daroussin 		if (st.st_size > 0x7fffffff) {
4477295610fSBaptiste Daroussin 			mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL);
448*45a5aec3SBaptiste Daroussin 			return -1;
44961d06d6bSBaptiste Daroussin 		}
45061d06d6bSBaptiste Daroussin 		*with_mmap = 1;
45161d06d6bSBaptiste Daroussin 		fb->sz = (size_t)st.st_size;
45261d06d6bSBaptiste Daroussin 		fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0);
45361d06d6bSBaptiste Daroussin 		if (fb->buf != MAP_FAILED)
454*45a5aec3SBaptiste Daroussin 			return 0;
45561d06d6bSBaptiste Daroussin 	}
45661d06d6bSBaptiste Daroussin 
45761d06d6bSBaptiste Daroussin 	if (curp->gzip) {
45861d06d6bSBaptiste Daroussin 		/*
45961d06d6bSBaptiste Daroussin 		 * Duplicating the file descriptor is required
46061d06d6bSBaptiste Daroussin 		 * because we will have to call gzclose(3)
46161d06d6bSBaptiste Daroussin 		 * to free memory used internally by zlib,
46261d06d6bSBaptiste Daroussin 		 * but that will also close the file descriptor,
46361d06d6bSBaptiste Daroussin 		 * which this function must not do.
46461d06d6bSBaptiste Daroussin 		 */
46561d06d6bSBaptiste Daroussin 		if ((fd = dup(fd)) == -1) {
466*45a5aec3SBaptiste Daroussin 			mandoc_msg(MANDOCERR_DUP, 0, 0,
467*45a5aec3SBaptiste Daroussin 			    "%s", strerror(errno));
468*45a5aec3SBaptiste Daroussin 			return -1;
46961d06d6bSBaptiste Daroussin 		}
47061d06d6bSBaptiste Daroussin 		if ((gz = gzdopen(fd, "rb")) == NULL) {
471*45a5aec3SBaptiste Daroussin 			mandoc_msg(MANDOCERR_GZDOPEN, 0, 0,
472*45a5aec3SBaptiste Daroussin 			    "%s", strerror(errno));
47361d06d6bSBaptiste Daroussin 			close(fd);
474*45a5aec3SBaptiste Daroussin 			return -1;
47561d06d6bSBaptiste Daroussin 		}
47661d06d6bSBaptiste Daroussin 	} else
47761d06d6bSBaptiste Daroussin 		gz = NULL;
47861d06d6bSBaptiste Daroussin 
47961d06d6bSBaptiste Daroussin 	/*
48061d06d6bSBaptiste Daroussin 	 * If this isn't a regular file (like, say, stdin), then we must
48161d06d6bSBaptiste Daroussin 	 * go the old way and just read things in bit by bit.
48261d06d6bSBaptiste Daroussin 	 */
48361d06d6bSBaptiste Daroussin 
48461d06d6bSBaptiste Daroussin 	*with_mmap = 0;
48561d06d6bSBaptiste Daroussin 	off = 0;
486*45a5aec3SBaptiste Daroussin 	retval = -1;
48761d06d6bSBaptiste Daroussin 	fb->sz = 0;
48861d06d6bSBaptiste Daroussin 	fb->buf = NULL;
48961d06d6bSBaptiste Daroussin 	for (;;) {
49061d06d6bSBaptiste Daroussin 		if (off == fb->sz) {
49161d06d6bSBaptiste Daroussin 			if (fb->sz == (1U << 31)) {
4927295610fSBaptiste Daroussin 				mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL);
49361d06d6bSBaptiste Daroussin 				break;
49461d06d6bSBaptiste Daroussin 			}
49561d06d6bSBaptiste Daroussin 			resize_buf(fb, 65536);
49661d06d6bSBaptiste Daroussin 		}
49761d06d6bSBaptiste Daroussin 		ssz = curp->gzip ?
49861d06d6bSBaptiste Daroussin 		    gzread(gz, fb->buf + (int)off, fb->sz - off) :
49961d06d6bSBaptiste Daroussin 		    read(fd, fb->buf + (int)off, fb->sz - off);
50061d06d6bSBaptiste Daroussin 		if (ssz == 0) {
50161d06d6bSBaptiste Daroussin 			fb->sz = off;
502*45a5aec3SBaptiste Daroussin 			retval = 0;
50361d06d6bSBaptiste Daroussin 			break;
50461d06d6bSBaptiste Daroussin 		}
50561d06d6bSBaptiste Daroussin 		if (ssz == -1) {
50661d06d6bSBaptiste Daroussin 			if (curp->gzip)
50761d06d6bSBaptiste Daroussin 				(void)gzerror(gz, &gzerrnum);
508*45a5aec3SBaptiste Daroussin 			mandoc_msg(MANDOCERR_READ, 0, 0, "%s",
50961d06d6bSBaptiste Daroussin 			    curp->gzip && gzerrnum != Z_ERRNO ?
51061d06d6bSBaptiste Daroussin 			    zError(gzerrnum) : strerror(errno));
51161d06d6bSBaptiste Daroussin 			break;
51261d06d6bSBaptiste Daroussin 		}
51361d06d6bSBaptiste Daroussin 		off += (size_t)ssz;
51461d06d6bSBaptiste Daroussin 	}
51561d06d6bSBaptiste Daroussin 
51661d06d6bSBaptiste Daroussin 	if (curp->gzip && (gzerrnum = gzclose(gz)) != Z_OK)
517*45a5aec3SBaptiste Daroussin 		mandoc_msg(MANDOCERR_GZCLOSE, 0, 0, "%s",
51861d06d6bSBaptiste Daroussin 		    gzerrnum == Z_ERRNO ? strerror(errno) :
51961d06d6bSBaptiste Daroussin 		    zError(gzerrnum));
520*45a5aec3SBaptiste Daroussin 	if (retval == -1) {
52161d06d6bSBaptiste Daroussin 		free(fb->buf);
52261d06d6bSBaptiste Daroussin 		fb->buf = NULL;
52361d06d6bSBaptiste Daroussin 	}
52461d06d6bSBaptiste Daroussin 	return retval;
52561d06d6bSBaptiste Daroussin }
52661d06d6bSBaptiste Daroussin 
52761d06d6bSBaptiste Daroussin static void
52861d06d6bSBaptiste Daroussin mparse_end(struct mparse *curp)
52961d06d6bSBaptiste Daroussin {
5307295610fSBaptiste Daroussin 	if (curp->man->meta.macroset == MACROSET_NONE)
5317295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MAN;
5327295610fSBaptiste Daroussin 	if (curp->man->meta.macroset == MACROSET_MDOC)
53361d06d6bSBaptiste Daroussin 		mdoc_endparse(curp->man);
53461d06d6bSBaptiste Daroussin 	else
53561d06d6bSBaptiste Daroussin 		man_endparse(curp->man);
53661d06d6bSBaptiste Daroussin 	roff_endparse(curp->roff);
53761d06d6bSBaptiste Daroussin }
53861d06d6bSBaptiste Daroussin 
5397295610fSBaptiste Daroussin /*
5407295610fSBaptiste Daroussin  * Read the whole file into memory and call the parsers.
5417295610fSBaptiste Daroussin  * Called recursively when an .so request is encountered.
5427295610fSBaptiste Daroussin  */
5437295610fSBaptiste Daroussin void
5447295610fSBaptiste Daroussin mparse_readfd(struct mparse *curp, int fd, const char *filename)
54561d06d6bSBaptiste Daroussin {
54661d06d6bSBaptiste Daroussin 	static int	 recursion_depth;
54761d06d6bSBaptiste Daroussin 
5487295610fSBaptiste Daroussin 	struct buf	 blk;
5497295610fSBaptiste Daroussin 	struct buf	*save_primary;
5507295610fSBaptiste Daroussin 	const char	*save_filename;
5517295610fSBaptiste Daroussin 	size_t		 offset;
5527295610fSBaptiste Daroussin 	int		 save_filenc, save_lineno;
5537295610fSBaptiste Daroussin 	int		 with_mmap;
5547295610fSBaptiste Daroussin 
5557295610fSBaptiste Daroussin 	if (recursion_depth > 64) {
5567295610fSBaptiste Daroussin 		mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL);
55761d06d6bSBaptiste Daroussin 		return;
55861d06d6bSBaptiste Daroussin 	}
559*45a5aec3SBaptiste Daroussin 	if (read_whole_file(curp, fd, &blk, &with_mmap) == -1)
5607295610fSBaptiste Daroussin 		return;
56161d06d6bSBaptiste Daroussin 
5627295610fSBaptiste Daroussin 	/*
5637295610fSBaptiste Daroussin 	 * Save some properties of the parent file.
5647295610fSBaptiste Daroussin 	 */
5657295610fSBaptiste Daroussin 
5667295610fSBaptiste Daroussin 	save_primary = curp->primary;
5677295610fSBaptiste Daroussin 	save_filenc = curp->filenc;
5687295610fSBaptiste Daroussin 	save_lineno = curp->line;
5697295610fSBaptiste Daroussin 	save_filename = mandoc_msg_getinfilename();
5707295610fSBaptiste Daroussin 
57161d06d6bSBaptiste Daroussin 	curp->primary = &blk;
5727295610fSBaptiste Daroussin 	curp->filenc = curp->options & (MPARSE_UTF8 | MPARSE_LATIN1);
57361d06d6bSBaptiste Daroussin 	curp->line = 1;
5747295610fSBaptiste Daroussin 	mandoc_msg_setinfilename(filename);
57561d06d6bSBaptiste Daroussin 
57661d06d6bSBaptiste Daroussin 	/* Skip an UTF-8 byte order mark. */
57761d06d6bSBaptiste Daroussin 	if (curp->filenc & MPARSE_UTF8 && blk.sz > 2 &&
57861d06d6bSBaptiste Daroussin 	    (unsigned char)blk.buf[0] == 0xef &&
57961d06d6bSBaptiste Daroussin 	    (unsigned char)blk.buf[1] == 0xbb &&
58061d06d6bSBaptiste Daroussin 	    (unsigned char)blk.buf[2] == 0xbf) {
58161d06d6bSBaptiste Daroussin 		offset = 3;
58261d06d6bSBaptiste Daroussin 		curp->filenc &= ~MPARSE_LATIN1;
58361d06d6bSBaptiste Daroussin 	} else
58461d06d6bSBaptiste Daroussin 		offset = 0;
58561d06d6bSBaptiste Daroussin 
5867295610fSBaptiste Daroussin 	recursion_depth++;
58761d06d6bSBaptiste Daroussin 	mparse_buf_r(curp, blk, offset, 1);
58861d06d6bSBaptiste Daroussin 	if (--recursion_depth == 0)
58961d06d6bSBaptiste Daroussin 		mparse_end(curp);
59061d06d6bSBaptiste Daroussin 
59161d06d6bSBaptiste Daroussin 	/*
5927295610fSBaptiste Daroussin 	 * Clean up and restore saved parent properties.
59361d06d6bSBaptiste Daroussin 	 */
59461d06d6bSBaptiste Daroussin 
59561d06d6bSBaptiste Daroussin 	if (with_mmap)
59661d06d6bSBaptiste Daroussin 		munmap(blk.buf, blk.sz);
59761d06d6bSBaptiste Daroussin 	else
59861d06d6bSBaptiste Daroussin 		free(blk.buf);
5997295610fSBaptiste Daroussin 
6007295610fSBaptiste Daroussin 	curp->primary = save_primary;
6017295610fSBaptiste Daroussin 	curp->filenc = save_filenc;
6027295610fSBaptiste Daroussin 	curp->line = save_lineno;
6037295610fSBaptiste Daroussin 	if (save_filename != NULL)
6047295610fSBaptiste Daroussin 		mandoc_msg_setinfilename(save_filename);
60561d06d6bSBaptiste Daroussin }
60661d06d6bSBaptiste Daroussin 
60761d06d6bSBaptiste Daroussin int
60861d06d6bSBaptiste Daroussin mparse_open(struct mparse *curp, const char *file)
60961d06d6bSBaptiste Daroussin {
61061d06d6bSBaptiste Daroussin 	char		 *cp;
6117295610fSBaptiste Daroussin 	int		  fd, save_errno;
61261d06d6bSBaptiste Daroussin 
61361d06d6bSBaptiste Daroussin 	cp = strrchr(file, '.');
61461d06d6bSBaptiste Daroussin 	curp->gzip = (cp != NULL && ! strcmp(cp + 1, "gz"));
61561d06d6bSBaptiste Daroussin 
61661d06d6bSBaptiste Daroussin 	/* First try to use the filename as it is. */
61761d06d6bSBaptiste Daroussin 
61861d06d6bSBaptiste Daroussin 	if ((fd = open(file, O_RDONLY)) != -1)
61961d06d6bSBaptiste Daroussin 		return fd;
62061d06d6bSBaptiste Daroussin 
62161d06d6bSBaptiste Daroussin 	/*
62261d06d6bSBaptiste Daroussin 	 * If that doesn't work and the filename doesn't
62361d06d6bSBaptiste Daroussin 	 * already  end in .gz, try appending .gz.
62461d06d6bSBaptiste Daroussin 	 */
62561d06d6bSBaptiste Daroussin 
62661d06d6bSBaptiste Daroussin 	if ( ! curp->gzip) {
6277295610fSBaptiste Daroussin 		save_errno = errno;
62861d06d6bSBaptiste Daroussin 		mandoc_asprintf(&cp, "%s.gz", file);
62961d06d6bSBaptiste Daroussin 		fd = open(cp, O_RDONLY);
63061d06d6bSBaptiste Daroussin 		free(cp);
6317295610fSBaptiste Daroussin 		errno = save_errno;
63261d06d6bSBaptiste Daroussin 		if (fd != -1) {
63361d06d6bSBaptiste Daroussin 			curp->gzip = 1;
63461d06d6bSBaptiste Daroussin 			return fd;
63561d06d6bSBaptiste Daroussin 		}
63661d06d6bSBaptiste Daroussin 	}
63761d06d6bSBaptiste Daroussin 
63861d06d6bSBaptiste Daroussin 	/* Neither worked, give up. */
63961d06d6bSBaptiste Daroussin 
64061d06d6bSBaptiste Daroussin 	return -1;
64161d06d6bSBaptiste Daroussin }
64261d06d6bSBaptiste Daroussin 
64361d06d6bSBaptiste Daroussin struct mparse *
6447295610fSBaptiste Daroussin mparse_alloc(int options, enum mandoc_os os_e, const char *os_s)
64561d06d6bSBaptiste Daroussin {
64661d06d6bSBaptiste Daroussin 	struct mparse	*curp;
64761d06d6bSBaptiste Daroussin 
64861d06d6bSBaptiste Daroussin 	curp = mandoc_calloc(1, sizeof(struct mparse));
64961d06d6bSBaptiste Daroussin 
65061d06d6bSBaptiste Daroussin 	curp->options = options;
65161d06d6bSBaptiste Daroussin 	curp->os_s = os_s;
65261d06d6bSBaptiste Daroussin 
6537295610fSBaptiste Daroussin 	curp->roff = roff_alloc(options);
6547295610fSBaptiste Daroussin 	curp->man = roff_man_alloc(curp->roff, curp->os_s,
65561d06d6bSBaptiste Daroussin 		curp->options & MPARSE_QUICK ? 1 : 0);
65661d06d6bSBaptiste Daroussin 	if (curp->options & MPARSE_MDOC) {
6577295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MDOC;
65861d06d6bSBaptiste Daroussin 		if (curp->man->mdocmac == NULL)
65961d06d6bSBaptiste Daroussin 			curp->man->mdocmac = roffhash_alloc(MDOC_Dd, MDOC_MAX);
66061d06d6bSBaptiste Daroussin 	} else if (curp->options & MPARSE_MAN) {
6617295610fSBaptiste Daroussin 		curp->man->meta.macroset = MACROSET_MAN;
66261d06d6bSBaptiste Daroussin 		if (curp->man->manmac == NULL)
66361d06d6bSBaptiste Daroussin 			curp->man->manmac = roffhash_alloc(MAN_TH, MAN_MAX);
66461d06d6bSBaptiste Daroussin 	}
6657295610fSBaptiste Daroussin 	curp->man->meta.first->tok = TOKEN_NONE;
66661d06d6bSBaptiste Daroussin 	curp->man->meta.os_e = os_e;
66761d06d6bSBaptiste Daroussin 	return curp;
66861d06d6bSBaptiste Daroussin }
66961d06d6bSBaptiste Daroussin 
67061d06d6bSBaptiste Daroussin void
67161d06d6bSBaptiste Daroussin mparse_reset(struct mparse *curp)
67261d06d6bSBaptiste Daroussin {
67361d06d6bSBaptiste Daroussin 	roff_reset(curp->roff);
67461d06d6bSBaptiste Daroussin 	roff_man_reset(curp->man);
6757295610fSBaptiste Daroussin 	free_buf_list(curp->secondary);
6767295610fSBaptiste Daroussin 	curp->secondary = NULL;
67761d06d6bSBaptiste Daroussin 	curp->gzip = 0;
67861d06d6bSBaptiste Daroussin }
67961d06d6bSBaptiste Daroussin 
68061d06d6bSBaptiste Daroussin void
68161d06d6bSBaptiste Daroussin mparse_free(struct mparse *curp)
68261d06d6bSBaptiste Daroussin {
68361d06d6bSBaptiste Daroussin 	roffhash_free(curp->man->mdocmac);
68461d06d6bSBaptiste Daroussin 	roffhash_free(curp->man->manmac);
68561d06d6bSBaptiste Daroussin 	roff_man_free(curp->man);
68661d06d6bSBaptiste Daroussin 	roff_free(curp->roff);
6877295610fSBaptiste Daroussin 	free_buf_list(curp->secondary);
68861d06d6bSBaptiste Daroussin 	free(curp);
68961d06d6bSBaptiste Daroussin }
69061d06d6bSBaptiste Daroussin 
6917295610fSBaptiste Daroussin struct roff_meta *
6927295610fSBaptiste Daroussin mparse_result(struct mparse *curp)
69361d06d6bSBaptiste Daroussin {
6947295610fSBaptiste Daroussin 	roff_state_reset(curp->man);
6957295610fSBaptiste Daroussin 	if (curp->options & MPARSE_VALIDATE) {
6967295610fSBaptiste Daroussin 		if (curp->man->meta.macroset == MACROSET_MDOC)
6977295610fSBaptiste Daroussin 			mdoc_validate(curp->man);
6987295610fSBaptiste Daroussin 		else
6997295610fSBaptiste Daroussin 			man_validate(curp->man);
70061d06d6bSBaptiste Daroussin 	}
7017295610fSBaptiste Daroussin 	return &curp->man->meta;
70261d06d6bSBaptiste Daroussin }
70361d06d6bSBaptiste Daroussin 
70461d06d6bSBaptiste Daroussin void
7057295610fSBaptiste Daroussin mparse_copy(const struct mparse *p)
70661d06d6bSBaptiste Daroussin {
7077295610fSBaptiste Daroussin 	struct buf	*buf;
70861d06d6bSBaptiste Daroussin 
7097295610fSBaptiste Daroussin 	for (buf = p->secondary; buf != NULL; buf = buf->next)
7107295610fSBaptiste Daroussin 		puts(buf->buf);
71161d06d6bSBaptiste Daroussin }
712