- Fix for possible unset uid/gid in toproto
 - Fix for default mtree style
 - Update libelf
 - Importing libexecinfo
 - Resynchronize GCC, mpc, gmp, mpfr
 - build.sh: Replace params with show-params.
     This has been done as the make target has been renamed in the same
     way, while a new target named params has been added. This new
     target generates a file containing all the parameters, instead of
     printing it on the console.
 - Update test48 with new etc/services (Fix by Ben Gras <ben@minix3.org)
     get getservbyport() out of the inner loop
Change-Id: Ie6ad5226fa2621ff9f0dee8782ea48f9443d2091
		
	
			
		
			
				
	
	
		
			852 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			852 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*	$Vendor-Id: read.c,v 1.28 2012/02/16 20:51:31 joerg Exp $ */
 | 
						|
/*
 | 
						|
 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
 | 
						|
 * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
 | 
						|
 *
 | 
						|
 * Permission to use, copy, modify, and distribute this software for any
 | 
						|
 * purpose with or without fee is hereby granted, provided that the above
 | 
						|
 * copyright notice and this permission notice appear in all copies.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 | 
						|
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 | 
						|
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 | 
						|
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 | 
						|
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 | 
						|
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 | 
						|
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 | 
						|
 */
 | 
						|
#ifdef HAVE_CONFIG_H
 | 
						|
#include "config.h"
 | 
						|
#endif
 | 
						|
 | 
						|
#ifdef HAVE_MMAP
 | 
						|
# include <sys/stat.h>
 | 
						|
# include <sys/mman.h>
 | 
						|
#endif
 | 
						|
 | 
						|
#include <assert.h>
 | 
						|
#include <ctype.h>
 | 
						|
#include <fcntl.h>
 | 
						|
#include <stdarg.h>
 | 
						|
#include <stdint.h>
 | 
						|
#include <stdio.h>
 | 
						|
#include <stdlib.h>
 | 
						|
#include <string.h>
 | 
						|
#include <unistd.h>
 | 
						|
 | 
						|
#include "mandoc.h"
 | 
						|
#include "libmandoc.h"
 | 
						|
#include "mdoc.h"
 | 
						|
#include "man.h"
 | 
						|
#include "main.h"
 | 
						|
 | 
						|
#ifndef MAP_FILE
 | 
						|
#define	MAP_FILE	0
 | 
						|
#endif
 | 
						|
 | 
						|
#define	REPARSE_LIMIT	1000
 | 
						|
 | 
						|
struct	buf {
 | 
						|
	char	 	 *buf; /* binary input buffer */
 | 
						|
	size_t		  sz; /* size of binary buffer */
 | 
						|
};
 | 
						|
 | 
						|
struct	mparse {
 | 
						|
	enum mandoclevel  file_status; /* status of current parse */
 | 
						|
	enum mandoclevel  wlevel; /* ignore messages below this */
 | 
						|
	int		  line; /* line number in the file */
 | 
						|
	enum mparset	  inttype; /* which parser to use */
 | 
						|
	struct man	 *pman; /* persistent man parser */
 | 
						|
	struct mdoc	 *pmdoc; /* persistent mdoc parser */
 | 
						|
	struct man	 *man; /* man parser */
 | 
						|
	struct mdoc	 *mdoc; /* mdoc parser */
 | 
						|
	struct roff	 *roff; /* roff parser (!NULL) */
 | 
						|
	int		  reparse_count; /* finite interp. stack */
 | 
						|
	mandocmsg	  mmsg; /* warning/error message handler */
 | 
						|
	void		 *arg; /* argument to mmsg */
 | 
						|
	const char	 *file; 
 | 
						|
	struct buf	 *secondary;
 | 
						|
};
 | 
						|
 | 
						|
static	void	  resize_buf(struct buf *, size_t);
 | 
						|
static	void	  mparse_buf_r(struct mparse *, struct buf, int);
 | 
						|
static	void	  mparse_readfd_r(struct mparse *, int, const char *, int);
 | 
						|
static	void	  pset(const char *, int, struct mparse *);
 | 
						|
static	int	  read_whole_file(const char *, int, struct buf *, int *);
 | 
						|
static	void	  mparse_end(struct mparse *);
 | 
						|
 | 
						|
static	const enum mandocerr	mandoclimits[MANDOCLEVEL_MAX] = {
 | 
						|
	MANDOCERR_OK,
 | 
						|
	MANDOCERR_WARNING,
 | 
						|
	MANDOCERR_WARNING,
 | 
						|
	MANDOCERR_ERROR,
 | 
						|
	MANDOCERR_FATAL,
 | 
						|
	MANDOCERR_MAX,
 | 
						|
	MANDOCERR_MAX
 | 
						|
};
 | 
						|
 | 
						|
static	const char * const	mandocerrs[MANDOCERR_MAX] = {
 | 
						|
	"ok",
 | 
						|
 | 
						|
	"generic warning",
 | 
						|
 | 
						|
	/* related to the prologue */
 | 
						|
	"no title in document",
 | 
						|
	"document title should be all caps",
 | 
						|
	"unknown manual section",
 | 
						|
	"date missing, using today's date",
 | 
						|
	"cannot parse date, using it verbatim",
 | 
						|
	"prologue macros out of order",
 | 
						|
	"duplicate prologue macro",
 | 
						|
	"macro not allowed in prologue",
 | 
						|
	"macro not allowed in body",
 | 
						|
 | 
						|
	/* related to document structure */
 | 
						|
	".so is fragile, better use ln(1)",
 | 
						|
	"NAME section must come first",
 | 
						|
	"bad NAME section contents",
 | 
						|
	"manual name not yet set",
 | 
						|
	"sections out of conventional order",
 | 
						|
	"duplicate section name",
 | 
						|
	"section not in conventional manual section",
 | 
						|
 | 
						|
	/* related to macros and nesting */
 | 
						|
	"skipping obsolete macro",
 | 
						|
	"skipping paragraph macro",
 | 
						|
	"skipping no-space macro",
 | 
						|
	"blocks badly nested",
 | 
						|
	"child violates parent syntax",
 | 
						|
	"nested displays are not portable",
 | 
						|
	"already in literal mode",
 | 
						|
	"line scope broken",
 | 
						|
 | 
						|
	/* related to missing macro arguments */
 | 
						|
	"skipping empty macro",
 | 
						|
	"argument count wrong",
 | 
						|
	"missing display type",
 | 
						|
	"list type must come first",
 | 
						|
	"tag lists require a width argument",
 | 
						|
	"missing font type",
 | 
						|
	"skipping end of block that is not open",
 | 
						|
 | 
						|
	/* related to bad macro arguments */
 | 
						|
	"skipping argument",
 | 
						|
	"duplicate argument",
 | 
						|
	"duplicate display type",
 | 
						|
	"duplicate list type",
 | 
						|
	"unknown AT&T UNIX version",
 | 
						|
	"bad Boolean value",
 | 
						|
	"unknown font",
 | 
						|
	"unknown standard specifier",
 | 
						|
	"bad width argument",
 | 
						|
 | 
						|
	/* related to plain text */
 | 
						|
	"blank line in non-literal context",
 | 
						|
	"tab in non-literal context",
 | 
						|
	"end of line whitespace",
 | 
						|
	"bad comment style",
 | 
						|
	"bad escape sequence",
 | 
						|
	"unterminated quoted string",
 | 
						|
 | 
						|
	/* related to equations */
 | 
						|
	"unexpected literal in equation",
 | 
						|
	
 | 
						|
	"generic error",
 | 
						|
 | 
						|
	/* related to equations */
 | 
						|
	"unexpected equation scope closure",
 | 
						|
	"equation scope open on exit",
 | 
						|
	"overlapping equation scopes",
 | 
						|
	"unexpected end of equation",
 | 
						|
	"equation syntax error",
 | 
						|
 | 
						|
	/* related to tables */
 | 
						|
	"bad table syntax",
 | 
						|
	"bad table option",
 | 
						|
	"bad table layout",
 | 
						|
	"no table layout cells specified",
 | 
						|
	"no table data cells specified",
 | 
						|
	"ignore data in cell",
 | 
						|
	"data block still open",
 | 
						|
	"ignoring extra data cells",
 | 
						|
 | 
						|
	"input stack limit exceeded, infinite loop?",
 | 
						|
	"skipping bad character",
 | 
						|
	"escaped character not allowed in a name",
 | 
						|
	"skipping text before the first section header",
 | 
						|
	"skipping unknown macro",
 | 
						|
	"NOT IMPLEMENTED, please use groff: skipping request",
 | 
						|
	"argument count wrong",
 | 
						|
	"skipping end of block that is not open",
 | 
						|
	"missing end of block",
 | 
						|
	"scope open on exit",
 | 
						|
	"uname(3) system call failed",
 | 
						|
	"macro requires line argument(s)",
 | 
						|
	"macro requires body argument(s)",
 | 
						|
	"macro requires argument(s)",
 | 
						|
	"missing list type",
 | 
						|
	"line argument(s) will be lost",
 | 
						|
	"body argument(s) will be lost",
 | 
						|
 | 
						|
	"generic fatal error",
 | 
						|
 | 
						|
	"not a manual",
 | 
						|
	"column syntax is inconsistent",
 | 
						|
	"NOT IMPLEMENTED: .Bd -file",
 | 
						|
	"argument count wrong, violates syntax",
 | 
						|
	"child violates parent syntax",
 | 
						|
	"argument count wrong, violates syntax",
 | 
						|
	"NOT IMPLEMENTED: .so with absolute path or \"..\"",
 | 
						|
	"no document body",
 | 
						|
	"no document prologue",
 | 
						|
	"static buffer exhausted",
 | 
						|
};
 | 
						|
 | 
						|
static	const char * const	mandoclevels[MANDOCLEVEL_MAX] = {
 | 
						|
	"SUCCESS",
 | 
						|
	"RESERVED",
 | 
						|
	"WARNING",
 | 
						|
	"ERROR",
 | 
						|
	"FATAL",
 | 
						|
	"BADARG",
 | 
						|
	"SYSERR"
 | 
						|
};
 | 
						|
 | 
						|
static void
 | 
						|
resize_buf(struct buf *buf, size_t initial)
 | 
						|
{
 | 
						|
 | 
						|
	buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
 | 
						|
	buf->buf = mandoc_realloc(buf->buf, buf->sz);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
pset(const char *buf, int pos, struct mparse *curp)
 | 
						|
{
 | 
						|
	int		 i;
 | 
						|
 | 
						|
	/*
 | 
						|
	 * Try to intuit which kind of manual parser should be used.  If
 | 
						|
	 * passed in by command-line (-man, -mdoc), then use that
 | 
						|
	 * explicitly.  If passed as -mandoc, then try to guess from the
 | 
						|
	 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
 | 
						|
	 * default to -man, which is more lenient.
 | 
						|
	 *
 | 
						|
	 * Separate out pmdoc/pman from mdoc/man: the first persists
 | 
						|
	 * through all parsers, while the latter is used per-parse.
 | 
						|
	 */
 | 
						|
 | 
						|
	if ('.' == buf[0] || '\'' == buf[0]) {
 | 
						|
		for (i = 1; buf[i]; i++)
 | 
						|
			if (' ' != buf[i] && '\t' != buf[i])
 | 
						|
				break;
 | 
						|
		if ('\0' == buf[i])
 | 
						|
			return;
 | 
						|
	}
 | 
						|
 | 
						|
	switch (curp->inttype) {
 | 
						|
	case (MPARSE_MDOC):
 | 
						|
		if (NULL == curp->pmdoc) 
 | 
						|
			curp->pmdoc = mdoc_alloc(curp->roff, curp);
 | 
						|
		assert(curp->pmdoc);
 | 
						|
		curp->mdoc = curp->pmdoc;
 | 
						|
		return;
 | 
						|
	case (MPARSE_MAN):
 | 
						|
		if (NULL == curp->pman) 
 | 
						|
			curp->pman = man_alloc(curp->roff, curp);
 | 
						|
		assert(curp->pman);
 | 
						|
		curp->man = curp->pman;
 | 
						|
		return;
 | 
						|
	default:
 | 
						|
		break;
 | 
						|
	}
 | 
						|
 | 
						|
	if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
 | 
						|
		if (NULL == curp->pmdoc) 
 | 
						|
			curp->pmdoc = mdoc_alloc(curp->roff, curp);
 | 
						|
		assert(curp->pmdoc);
 | 
						|
		curp->mdoc = curp->pmdoc;
 | 
						|
		return;
 | 
						|
	} 
 | 
						|
 | 
						|
	if (NULL == curp->pman) 
 | 
						|
		curp->pman = man_alloc(curp->roff, curp);
 | 
						|
	assert(curp->pman);
 | 
						|
	curp->man = curp->pman;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
 * Main parse routine for an opened file.  This is called for each
 | 
						|
 * opened file and simply loops around the full input file, possibly
 | 
						|
 * nesting (i.e., with `so').
 | 
						|
 */
 | 
						|
static void
 | 
						|
mparse_buf_r(struct mparse *curp, struct buf blk, int start)
 | 
						|
{
 | 
						|
	const struct tbl_span	*span;
 | 
						|
	struct buf	 ln;
 | 
						|
	enum rofferr	 rr;
 | 
						|
	int		 i, of, rc;
 | 
						|
	int		 pos; /* byte number in the ln buffer */
 | 
						|
	int		 lnn; /* line number in the real file */
 | 
						|
	unsigned char	 c;
 | 
						|
 | 
						|
	memset(&ln, 0, sizeof(struct buf));
 | 
						|
 | 
						|
	lnn = curp->line; 
 | 
						|
	pos = 0; 
 | 
						|
 | 
						|
	for (i = 0; i < (int)blk.sz; ) {
 | 
						|
		if (0 == pos && '\0' == blk.buf[i])
 | 
						|
			break;
 | 
						|
 | 
						|
		if (start) {
 | 
						|
			curp->line = lnn;
 | 
						|
			curp->reparse_count = 0;
 | 
						|
		}
 | 
						|
 | 
						|
		while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
 | 
						|
 | 
						|
			/*
 | 
						|
			 * When finding an unescaped newline character,
 | 
						|
			 * leave the character loop to process the line.
 | 
						|
			 * Skip a preceding carriage return, if any.
 | 
						|
			 */
 | 
						|
 | 
						|
			if ('\r' == blk.buf[i] && i + 1 < (int)blk.sz &&
 | 
						|
			    '\n' == blk.buf[i + 1])
 | 
						|
				++i;
 | 
						|
			if ('\n' == blk.buf[i]) {
 | 
						|
				++i;
 | 
						|
				++lnn;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			/* 
 | 
						|
			 * Warn about bogus characters.  If you're using
 | 
						|
			 * non-ASCII encoding, you're screwing your
 | 
						|
			 * readers.  Since I'd rather this not happen,
 | 
						|
			 * I'll be helpful and replace these characters
 | 
						|
			 * with "?", so we don't display gibberish.
 | 
						|
			 * Note to manual writers: use special characters.
 | 
						|
			 */
 | 
						|
 | 
						|
			c = (unsigned char) blk.buf[i];
 | 
						|
 | 
						|
			if ( ! (isascii(c) && 
 | 
						|
					(isgraph(c) || isblank(c)))) {
 | 
						|
				mandoc_msg(MANDOCERR_BADCHAR, curp,
 | 
						|
						curp->line, pos, NULL);
 | 
						|
				i++;
 | 
						|
				if (pos >= (int)ln.sz)
 | 
						|
					resize_buf(&ln, 256);
 | 
						|
				ln.buf[pos++] = '?';
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			/* Expand registers inline */
 | 
						|
			if ('\\' == blk.buf[i] && 'n' == blk.buf[i + 1]) {
 | 
						|
				roff_expand_nr(curp->roff,
 | 
						|
				    blk.buf, &i, blk.sz, &ln.buf, &pos, &ln.sz);
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			/* Trailing backslash = a plain char. */
 | 
						|
			if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
 | 
						|
				if (pos >= (int)ln.sz)
 | 
						|
					resize_buf(&ln, 256);
 | 
						|
				ln.buf[pos++] = blk.buf[i++];
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
			/*
 | 
						|
			 * Found escape and at least one other character.
 | 
						|
			 * When it's a newline character, skip it.
 | 
						|
			 * When there is a carriage return in between,
 | 
						|
			 * skip that one as well.
 | 
						|
			 */
 | 
						|
 | 
						|
			if ('\r' == blk.buf[i + 1] && i + 2 < (int)blk.sz &&
 | 
						|
			    '\n' == blk.buf[i + 2])
 | 
						|
				++i;
 | 
						|
			if ('\n' == blk.buf[i + 1]) {
 | 
						|
				i += 2;
 | 
						|
				++lnn;
 | 
						|
				continue;
 | 
						|
			}
 | 
						|
 | 
						|
			if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) {
 | 
						|
				i += 2;
 | 
						|
				/* Comment, skip to end of line */
 | 
						|
				for (; i < (int)blk.sz; ++i) {
 | 
						|
					if ('\n' == blk.buf[i]) {
 | 
						|
						++i;
 | 
						|
						++lnn;
 | 
						|
						break;
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				/* Backout trailing whitespaces */
 | 
						|
				for (; pos > 0; --pos) {
 | 
						|
					if (ln.buf[pos - 1] != ' ')
 | 
						|
						break;
 | 
						|
					if (pos > 2 && ln.buf[pos - 2] == '\\')
 | 
						|
						break;
 | 
						|
				}
 | 
						|
				break;
 | 
						|
			}
 | 
						|
 | 
						|
			/* Some other escape sequence, copy & cont. */
 | 
						|
 | 
						|
			if (pos + 1 >= (int)ln.sz)
 | 
						|
				resize_buf(&ln, 256);
 | 
						|
 | 
						|
			ln.buf[pos++] = blk.buf[i++];
 | 
						|
			ln.buf[pos++] = blk.buf[i++];
 | 
						|
		}
 | 
						|
 | 
						|
 		if (pos >= (int)ln.sz)
 | 
						|
			resize_buf(&ln, 256);
 | 
						|
 | 
						|
		ln.buf[pos] = '\0';
 | 
						|
 | 
						|
		/*
 | 
						|
		 * A significant amount of complexity is contained by
 | 
						|
		 * the roff preprocessor.  It's line-oriented but can be
 | 
						|
		 * expressed on one line, so we need at times to
 | 
						|
		 * readjust our starting point and re-run it.  The roff
 | 
						|
		 * preprocessor can also readjust the buffers with new
 | 
						|
		 * data, so we pass them in wholesale.
 | 
						|
		 */
 | 
						|
 | 
						|
		of = 0;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * Maintain a lookaside buffer of all parsed lines.  We
 | 
						|
		 * only do this if mparse_keep() has been invoked (the
 | 
						|
		 * buffer may be accessed with mparse_getkeep()).
 | 
						|
		 */
 | 
						|
 | 
						|
		if (curp->secondary) {
 | 
						|
			curp->secondary->buf = 
 | 
						|
				mandoc_realloc
 | 
						|
				(curp->secondary->buf, 
 | 
						|
				 curp->secondary->sz + pos + 2);
 | 
						|
			memcpy(curp->secondary->buf + 
 | 
						|
					curp->secondary->sz, 
 | 
						|
					ln.buf, pos);
 | 
						|
			curp->secondary->sz += pos;
 | 
						|
			curp->secondary->buf
 | 
						|
				[curp->secondary->sz] = '\n';
 | 
						|
			curp->secondary->sz++;
 | 
						|
			curp->secondary->buf
 | 
						|
				[curp->secondary->sz] = '\0';
 | 
						|
		}
 | 
						|
rerun:
 | 
						|
		rr = roff_parseln
 | 
						|
			(curp->roff, curp->line, 
 | 
						|
			 &ln.buf, &ln.sz, of, &of);
 | 
						|
 | 
						|
		switch (rr) {
 | 
						|
		case (ROFF_REPARSE):
 | 
						|
			if (REPARSE_LIMIT >= ++curp->reparse_count)
 | 
						|
				mparse_buf_r(curp, ln, 0);
 | 
						|
			else
 | 
						|
				mandoc_msg(MANDOCERR_ROFFLOOP, curp,
 | 
						|
					curp->line, pos, NULL);
 | 
						|
			pos = 0;
 | 
						|
			continue;
 | 
						|
		case (ROFF_APPEND):
 | 
						|
			pos = (int)strlen(ln.buf);
 | 
						|
			continue;
 | 
						|
		case (ROFF_RERUN):
 | 
						|
			goto rerun;
 | 
						|
		case (ROFF_IGN):
 | 
						|
			pos = 0;
 | 
						|
			continue;
 | 
						|
		case (ROFF_ERR):
 | 
						|
			assert(MANDOCLEVEL_FATAL <= curp->file_status);
 | 
						|
			break;
 | 
						|
		case (ROFF_SO):
 | 
						|
			/*
 | 
						|
			 * We remove `so' clauses from our lookaside
 | 
						|
			 * buffer because we're going to descend into
 | 
						|
			 * the file recursively.
 | 
						|
			 */
 | 
						|
			if (curp->secondary) 
 | 
						|
				curp->secondary->sz -= pos + 1;
 | 
						|
			mparse_readfd_r(curp, -1, ln.buf + of, 1);
 | 
						|
			if (MANDOCLEVEL_FATAL <= curp->file_status)
 | 
						|
				break;
 | 
						|
			pos = 0;
 | 
						|
			continue;
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		/*
 | 
						|
		 * If we encounter errors in the recursive parse, make
 | 
						|
		 * sure we don't continue parsing.
 | 
						|
		 */
 | 
						|
 | 
						|
		if (MANDOCLEVEL_FATAL <= curp->file_status)
 | 
						|
			break;
 | 
						|
 | 
						|
		/*
 | 
						|
		 * If input parsers have not been allocated, do so now.
 | 
						|
		 * We keep these instanced between parsers, but set them
 | 
						|
		 * locally per parse routine since we can use different
 | 
						|
		 * parsers with each one.
 | 
						|
		 */
 | 
						|
 | 
						|
		if ( ! (curp->man || curp->mdoc))
 | 
						|
			pset(ln.buf + of, pos - of, curp);
 | 
						|
 | 
						|
		/* 
 | 
						|
		 * Lastly, push down into the parsers themselves.  One
 | 
						|
		 * of these will have already been set in the pset()
 | 
						|
		 * routine.
 | 
						|
		 * If libroff returns ROFF_TBL, then add it to the
 | 
						|
		 * currently open parse.  Since we only get here if
 | 
						|
		 * there does exist data (see tbl_data.c), we're
 | 
						|
		 * guaranteed that something's been allocated.
 | 
						|
		 * Do the same for ROFF_EQN.
 | 
						|
		 */
 | 
						|
 | 
						|
		rc = -1;
 | 
						|
 | 
						|
		if (ROFF_TBL == rr)
 | 
						|
			while (NULL != (span = roff_span(curp->roff))) {
 | 
						|
				rc = curp->man ?
 | 
						|
					man_addspan(curp->man, span) :
 | 
						|
					mdoc_addspan(curp->mdoc, span);
 | 
						|
				if (0 == rc)
 | 
						|
					break;
 | 
						|
			}
 | 
						|
		else if (ROFF_EQN == rr)
 | 
						|
			rc = curp->mdoc ? 
 | 
						|
				mdoc_addeqn(curp->mdoc, 
 | 
						|
					roff_eqn(curp->roff)) :
 | 
						|
				man_addeqn(curp->man,
 | 
						|
					roff_eqn(curp->roff));
 | 
						|
		else if (curp->man || curp->mdoc)
 | 
						|
			rc = curp->man ?
 | 
						|
				man_parseln(curp->man, 
 | 
						|
					curp->line, ln.buf, of) :
 | 
						|
				mdoc_parseln(curp->mdoc, 
 | 
						|
					curp->line, ln.buf, of);
 | 
						|
 | 
						|
		if (0 == rc) {
 | 
						|
			assert(MANDOCLEVEL_FATAL <= curp->file_status);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
 | 
						|
		/* Temporary buffers typically are not full. */
 | 
						|
 | 
						|
		if (0 == start && '\0' == blk.buf[i])
 | 
						|
			break;
 | 
						|
 | 
						|
		/* Start the next input line. */
 | 
						|
 | 
						|
		pos = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	free(ln.buf);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
 | 
						|
{
 | 
						|
	size_t		 off;
 | 
						|
	ssize_t		 ssz;
 | 
						|
 | 
						|
#ifdef	HAVE_MMAP
 | 
						|
	struct stat	 st;
 | 
						|
	if (-1 == fstat(fd, &st)) {
 | 
						|
		perror(file);
 | 
						|
		return(0);
 | 
						|
	}
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If we're a regular file, try just reading in the whole entry
 | 
						|
	 * via mmap().  This is faster than reading it into blocks, and
 | 
						|
	 * since each file is only a few bytes to begin with, I'm not
 | 
						|
	 * concerned that this is going to tank any machines.
 | 
						|
	 */
 | 
						|
 | 
						|
	if (S_ISREG(st.st_mode)) {
 | 
						|
		if (st.st_size >= (1U << 31)) {
 | 
						|
			fprintf(stderr, "%s: input too large\n", file);
 | 
						|
			return(0);
 | 
						|
		}
 | 
						|
		*with_mmap = 1;
 | 
						|
		fb->sz = (size_t)st.st_size;
 | 
						|
		fb->buf = mmap(NULL, fb->sz, PROT_READ, 
 | 
						|
				MAP_FILE|MAP_SHARED, fd, 0);
 | 
						|
		if (fb->buf != MAP_FAILED)
 | 
						|
			return(1);
 | 
						|
	}
 | 
						|
#endif
 | 
						|
 | 
						|
	/*
 | 
						|
	 * If this isn't a regular file (like, say, stdin), then we must
 | 
						|
	 * go the old way and just read things in bit by bit.
 | 
						|
	 */
 | 
						|
 | 
						|
	*with_mmap = 0;
 | 
						|
	off = 0;
 | 
						|
	fb->sz = 0;
 | 
						|
	fb->buf = NULL;
 | 
						|
	for (;;) {
 | 
						|
		if (off == fb->sz) {
 | 
						|
			if (fb->sz == (1U << 31)) {
 | 
						|
				fprintf(stderr, "%s: input too large\n", file);
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			resize_buf(fb, 65536);
 | 
						|
		}
 | 
						|
		ssz = read(fd, fb->buf + (int)off, fb->sz - off);
 | 
						|
		if (ssz == 0) {
 | 
						|
			fb->sz = off;
 | 
						|
			return(1);
 | 
						|
		}
 | 
						|
		if (ssz == -1) {
 | 
						|
			perror(file);
 | 
						|
			break;
 | 
						|
		}
 | 
						|
		off += (size_t)ssz;
 | 
						|
	}
 | 
						|
 | 
						|
	free(fb->buf);
 | 
						|
	fb->buf = NULL;
 | 
						|
	return(0);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
mparse_end(struct mparse *curp)
 | 
						|
{
 | 
						|
 | 
						|
	if (MANDOCLEVEL_FATAL <= curp->file_status)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
 | 
						|
		assert(MANDOCLEVEL_FATAL <= curp->file_status);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (curp->man && ! man_endparse(curp->man)) {
 | 
						|
		assert(MANDOCLEVEL_FATAL <= curp->file_status);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if ( ! (curp->man || curp->mdoc)) {
 | 
						|
		mandoc_msg(MANDOCERR_NOTMANUAL, curp, 1, 0, NULL);
 | 
						|
		curp->file_status = MANDOCLEVEL_FATAL;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	roff_endparse(curp->roff);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file,
 | 
						|
		int re)
 | 
						|
{
 | 
						|
	const char	*svfile;
 | 
						|
 | 
						|
	/* Line number is per-file. */
 | 
						|
	svfile = curp->file;
 | 
						|
	curp->file = file;
 | 
						|
	curp->line = 1;
 | 
						|
 | 
						|
	mparse_buf_r(curp, blk, 1);
 | 
						|
 | 
						|
	if (0 == re && MANDOCLEVEL_FATAL > curp->file_status)
 | 
						|
		mparse_end(curp);
 | 
						|
 | 
						|
	curp->file = svfile;
 | 
						|
}
 | 
						|
 | 
						|
enum mandoclevel
 | 
						|
mparse_readmem(struct mparse *curp, const void *buf, size_t len,
 | 
						|
		const char *file)
 | 
						|
{
 | 
						|
	struct buf blk;
 | 
						|
 | 
						|
	blk.buf = UNCONST(buf);
 | 
						|
	blk.sz = len;
 | 
						|
 | 
						|
	mparse_parse_buffer(curp, blk, file, 0);
 | 
						|
	return(curp->file_status);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
 | 
						|
{
 | 
						|
	struct buf	 blk;
 | 
						|
	int		 with_mmap;
 | 
						|
 | 
						|
	if (-1 == fd)
 | 
						|
		if (-1 == (fd = open(file, O_RDONLY, 0))) {
 | 
						|
			perror(file);
 | 
						|
			curp->file_status = MANDOCLEVEL_SYSERR;
 | 
						|
			return;
 | 
						|
		}
 | 
						|
	/*
 | 
						|
	 * Run for each opened file; may be called more than once for
 | 
						|
	 * each full parse sequence if the opened file is nested (i.e.,
 | 
						|
	 * from `so').  Simply sucks in the whole file and moves into
 | 
						|
	 * the parse phase for the file.
 | 
						|
	 */
 | 
						|
 | 
						|
	if ( ! read_whole_file(file, fd, &blk, &with_mmap)) {
 | 
						|
		curp->file_status = MANDOCLEVEL_SYSERR;
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	mparse_parse_buffer(curp, blk, file, re);
 | 
						|
 | 
						|
#ifdef	HAVE_MMAP
 | 
						|
	if (with_mmap)
 | 
						|
		munmap(blk.buf, blk.sz);
 | 
						|
	else
 | 
						|
#endif
 | 
						|
		free(blk.buf);
 | 
						|
 | 
						|
	if (STDIN_FILENO != fd && -1 == close(fd))
 | 
						|
		perror(file);
 | 
						|
}
 | 
						|
 | 
						|
enum mandoclevel
 | 
						|
mparse_readfd(struct mparse *curp, int fd, const char *file)
 | 
						|
{
 | 
						|
 | 
						|
	mparse_readfd_r(curp, fd, file, 0);
 | 
						|
	return(curp->file_status);
 | 
						|
}
 | 
						|
 | 
						|
struct mparse *
 | 
						|
mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, mandocmsg mmsg, void *arg)
 | 
						|
{
 | 
						|
	struct mparse	*curp;
 | 
						|
 | 
						|
	assert(wlevel <= MANDOCLEVEL_FATAL);
 | 
						|
 | 
						|
	curp = mandoc_calloc(1, sizeof(struct mparse));
 | 
						|
 | 
						|
	curp->wlevel = wlevel;
 | 
						|
	curp->mmsg = mmsg;
 | 
						|
	curp->arg = arg;
 | 
						|
	curp->inttype = inttype;
 | 
						|
 | 
						|
	curp->roff = roff_alloc(curp);
 | 
						|
	return(curp);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
mparse_reset(struct mparse *curp)
 | 
						|
{
 | 
						|
 | 
						|
	roff_reset(curp->roff);
 | 
						|
 | 
						|
	if (curp->mdoc)
 | 
						|
		mdoc_reset(curp->mdoc);
 | 
						|
	if (curp->man)
 | 
						|
		man_reset(curp->man);
 | 
						|
	if (curp->secondary)
 | 
						|
		curp->secondary->sz = 0;
 | 
						|
 | 
						|
	curp->file_status = MANDOCLEVEL_OK;
 | 
						|
	curp->mdoc = NULL;
 | 
						|
	curp->man = NULL;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
mparse_free(struct mparse *curp)
 | 
						|
{
 | 
						|
 | 
						|
	if (curp->pmdoc)
 | 
						|
		mdoc_free(curp->pmdoc);
 | 
						|
	if (curp->pman)
 | 
						|
		man_free(curp->pman);
 | 
						|
	if (curp->roff)
 | 
						|
		roff_free(curp->roff);
 | 
						|
	if (curp->secondary)
 | 
						|
		free(curp->secondary->buf);
 | 
						|
 | 
						|
	free(curp->secondary);
 | 
						|
	free(curp);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
mparse_result(struct mparse *curp, struct mdoc **mdoc, struct man **man)
 | 
						|
{
 | 
						|
 | 
						|
	if (mdoc)
 | 
						|
		*mdoc = curp->mdoc;
 | 
						|
	if (man)
 | 
						|
		*man = curp->man;
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
mandoc_vmsg(enum mandocerr t, struct mparse *m,
 | 
						|
		int ln, int pos, const char *fmt, ...)
 | 
						|
{
 | 
						|
	char		 buf[256];
 | 
						|
	va_list		 ap;
 | 
						|
 | 
						|
	va_start(ap, fmt);
 | 
						|
	vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
 | 
						|
	va_end(ap);
 | 
						|
 | 
						|
	mandoc_msg(t, m, ln, pos, buf);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
mandoc_msg(enum mandocerr er, struct mparse *m, 
 | 
						|
		int ln, int col, const char *msg)
 | 
						|
{
 | 
						|
	enum mandoclevel level;
 | 
						|
 | 
						|
	level = MANDOCLEVEL_FATAL;
 | 
						|
	while (er < mandoclimits[level])
 | 
						|
		level--;
 | 
						|
 | 
						|
	if (level < m->wlevel)
 | 
						|
		return;
 | 
						|
 | 
						|
	if (m->mmsg)
 | 
						|
		(*m->mmsg)(er, level, m->file, ln, col, msg);
 | 
						|
 | 
						|
	if (m->file_status < level)
 | 
						|
		m->file_status = level;
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
mparse_strerror(enum mandocerr er)
 | 
						|
{
 | 
						|
 | 
						|
	return(mandocerrs[er]);
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
mparse_strlevel(enum mandoclevel lvl)
 | 
						|
{
 | 
						|
	return(mandoclevels[lvl]);
 | 
						|
}
 | 
						|
 | 
						|
void
 | 
						|
mparse_keep(struct mparse *p)
 | 
						|
{
 | 
						|
 | 
						|
	assert(NULL == p->secondary);
 | 
						|
	p->secondary = mandoc_calloc(1, sizeof(struct buf));
 | 
						|
}
 | 
						|
 | 
						|
const char *
 | 
						|
mparse_getkeep(const struct mparse *p)
 | 
						|
{
 | 
						|
 | 
						|
	assert(p->secondary);
 | 
						|
	return(p->secondary->sz ? p->secondary->buf : NULL);
 | 
						|
}
 |