xref: /openbsd/usr.bin/mandoc/read.c (revision cca36db2)
1 /*	$Id: read.c,v 1.6 2012/02/26 21:01:43 schwarze Exp $ */
2 /*
3  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4  * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 #include <sys/stat.h>
19 #include <sys/mman.h>
20 
21 #include <assert.h>
22 #include <ctype.h>
23 #include <fcntl.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <unistd.h>
29 
30 #include "mandoc.h"
31 #include "libmandoc.h"
32 #include "mdoc.h"
33 #include "man.h"
34 
35 #define	REPARSE_LIMIT	1000
36 
37 struct	buf {
38 	char	 	 *buf; /* binary input buffer */
39 	size_t		  sz; /* size of binary buffer */
40 };
41 
42 struct	mparse {
43 	enum mandoclevel  file_status; /* status of current parse */
44 	enum mandoclevel  wlevel; /* ignore messages below this */
45 	int		  line; /* line number in the file */
46 	enum mparset	  inttype; /* which parser to use */
47 	struct man	 *pman; /* persistent man parser */
48 	struct mdoc	 *pmdoc; /* persistent mdoc parser */
49 	struct man	 *man; /* man parser */
50 	struct mdoc	 *mdoc; /* mdoc parser */
51 	struct roff	 *roff; /* roff parser (!NULL) */
52 	int		  reparse_count; /* finite interp. stack */
53 	mandocmsg	  mmsg; /* warning/error message handler */
54 	void		 *arg; /* argument to mmsg */
55 	const char	 *file;
56 	struct buf	 *secondary;
57 };
58 
59 static	void	  resize_buf(struct buf *, size_t);
60 static	void	  mparse_buf_r(struct mparse *, struct buf, int);
61 static	void	  mparse_readfd_r(struct mparse *, int, const char *, int);
62 static	void	  pset(const char *, int, struct mparse *);
63 static	void	  pdesc(struct mparse *, const char *, int);
64 static	int	  read_whole_file(const char *, int, struct buf *, int *);
65 static	void	  mparse_end(struct mparse *);
66 
67 static	const enum mandocerr	mandoclimits[MANDOCLEVEL_MAX] = {
68 	MANDOCERR_OK,
69 	MANDOCERR_WARNING,
70 	MANDOCERR_WARNING,
71 	MANDOCERR_ERROR,
72 	MANDOCERR_FATAL,
73 	MANDOCERR_MAX,
74 	MANDOCERR_MAX
75 };
76 
77 static	const char * const	mandocerrs[MANDOCERR_MAX] = {
78 	"ok",
79 
80 	"generic warning",
81 
82 	/* related to the prologue */
83 	"no title in document",
84 	"document title should be all caps",
85 	"unknown manual section",
86 	"date missing, using today's date",
87 	"cannot parse date, using it verbatim",
88 	"prologue macros out of order",
89 	"duplicate prologue macro",
90 	"macro not allowed in prologue",
91 	"macro not allowed in body",
92 
93 	/* related to document structure */
94 	".so is fragile, better use ln(1)",
95 	"NAME section must come first",
96 	"bad NAME section contents",
97 	"manual name not yet set",
98 	"sections out of conventional order",
99 	"duplicate section name",
100 	"section not in conventional manual section",
101 
102 	/* related to macros and nesting */
103 	"skipping obsolete macro",
104 	"skipping paragraph macro",
105 	"skipping no-space macro",
106 	"blocks badly nested",
107 	"child violates parent syntax",
108 	"nested displays are not portable",
109 	"already in literal mode",
110 	"line scope broken",
111 
112 	/* related to missing macro arguments */
113 	"skipping empty macro",
114 	"argument count wrong",
115 	"missing display type",
116 	"list type must come first",
117 	"tag lists require a width argument",
118 	"missing font type",
119 	"skipping end of block that is not open",
120 
121 	/* related to bad macro arguments */
122 	"skipping argument",
123 	"duplicate argument",
124 	"duplicate display type",
125 	"duplicate list type",
126 	"unknown AT&T UNIX version",
127 	"bad Boolean value",
128 	"unknown font",
129 	"unknown standard specifier",
130 	"bad width argument",
131 
132 	/* related to plain text */
133 	"blank line in non-literal context",
134 	"tab in non-literal context",
135 	"end of line whitespace",
136 	"bad comment style",
137 	"bad escape sequence",
138 	"unterminated quoted string",
139 
140 	/* related to equations */
141 	"unexpected literal in equation",
142 
143 	"generic error",
144 
145 	/* related to equations */
146 	"unexpected equation scope closure",
147 	"equation scope open on exit",
148 	"overlapping equation scopes",
149 	"unexpected end of equation",
150 	"equation syntax error",
151 
152 	/* related to tables */
153 	"bad table syntax",
154 	"bad table option",
155 	"bad table layout",
156 	"no table layout cells specified",
157 	"no table data cells specified",
158 	"ignore data in cell",
159 	"data block still open",
160 	"ignoring extra data cells",
161 
162 	"input stack limit exceeded, infinite loop?",
163 	"skipping bad character",
164 	"escaped character not allowed in a name",
165 	"skipping text before the first section header",
166 	"skipping unknown macro",
167 	"NOT IMPLEMENTED, please use groff: skipping request",
168 	"argument count wrong",
169 	"skipping end of block that is not open",
170 	"missing end of block",
171 	"scope open on exit",
172 	"uname(3) system call failed",
173 	"macro requires line argument(s)",
174 	"macro requires body argument(s)",
175 	"macro requires argument(s)",
176 	"missing list type",
177 	"line argument(s) will be lost",
178 	"body argument(s) will be lost",
179 
180 	"generic fatal error",
181 
182 	"not a manual",
183 	"column syntax is inconsistent",
184 	"NOT IMPLEMENTED: .Bd -file",
185 	"argument count wrong, violates syntax",
186 	"child violates parent syntax",
187 	"argument count wrong, violates syntax",
188 	"NOT IMPLEMENTED: .so with absolute path or \"..\"",
189 	"no document body",
190 	"no document prologue",
191 	"static buffer exhausted",
192 };
193 
194 static	const char * const	mandoclevels[MANDOCLEVEL_MAX] = {
195 	"SUCCESS",
196 	"RESERVED",
197 	"WARNING",
198 	"ERROR",
199 	"FATAL",
200 	"BADARG",
201 	"SYSERR"
202 };
203 
204 static void
205 resize_buf(struct buf *buf, size_t initial)
206 {
207 
208 	buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
209 	buf->buf = mandoc_realloc(buf->buf, buf->sz);
210 }
211 
212 static void
213 pset(const char *buf, int pos, struct mparse *curp)
214 {
215 	int		 i;
216 
217 	/*
218 	 * Try to intuit which kind of manual parser should be used.  If
219 	 * passed in by command-line (-man, -mdoc), then use that
220 	 * explicitly.  If passed as -mandoc, then try to guess from the
221 	 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
222 	 * default to -man, which is more lenient.
223 	 *
224 	 * Separate out pmdoc/pman from mdoc/man: the first persists
225 	 * through all parsers, while the latter is used per-parse.
226 	 */
227 
228 	if ('.' == buf[0] || '\'' == buf[0]) {
229 		for (i = 1; buf[i]; i++)
230 			if (' ' != buf[i] && '\t' != buf[i])
231 				break;
232 		if ('\0' == buf[i])
233 			return;
234 	}
235 
236 	switch (curp->inttype) {
237 	case (MPARSE_MDOC):
238 		if (NULL == curp->pmdoc)
239 			curp->pmdoc = mdoc_alloc(curp->roff, curp);
240 		assert(curp->pmdoc);
241 		curp->mdoc = curp->pmdoc;
242 		return;
243 	case (MPARSE_MAN):
244 		if (NULL == curp->pman)
245 			curp->pman = man_alloc(curp->roff, curp);
246 		assert(curp->pman);
247 		curp->man = curp->pman;
248 		return;
249 	default:
250 		break;
251 	}
252 
253 	if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
254 		if (NULL == curp->pmdoc)
255 			curp->pmdoc = mdoc_alloc(curp->roff, curp);
256 		assert(curp->pmdoc);
257 		curp->mdoc = curp->pmdoc;
258 		return;
259 	}
260 
261 	if (NULL == curp->pman)
262 		curp->pman = man_alloc(curp->roff, curp);
263 	assert(curp->pman);
264 	curp->man = curp->pman;
265 }
266 
267 /*
268  * Main parse routine for an opened file.  This is called for each
269  * opened file and simply loops around the full input file, possibly
270  * nesting (i.e., with `so').
271  */
272 static void
273 mparse_buf_r(struct mparse *curp, struct buf blk, int start)
274 {
275 	const struct tbl_span	*span;
276 	struct buf	 ln;
277 	enum rofferr	 rr;
278 	int		 i, of, rc;
279 	int		 pos; /* byte number in the ln buffer */
280 	int		 lnn; /* line number in the real file */
281 	unsigned char	 c;
282 
283 	memset(&ln, 0, sizeof(struct buf));
284 
285 	lnn = curp->line;
286 	pos = 0;
287 
288 	for (i = 0; i < (int)blk.sz; ) {
289 		if (0 == pos && '\0' == blk.buf[i])
290 			break;
291 
292 		if (start) {
293 			curp->line = lnn;
294 			curp->reparse_count = 0;
295 		}
296 
297 		while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
298 
299 			/*
300 			 * When finding an unescaped newline character,
301 			 * leave the character loop to process the line.
302 			 * Skip a preceding carriage return, if any.
303 			 */
304 
305 			if ('\r' == blk.buf[i] && i + 1 < (int)blk.sz &&
306 			    '\n' == blk.buf[i + 1])
307 				++i;
308 			if ('\n' == blk.buf[i]) {
309 				++i;
310 				++lnn;
311 				break;
312 			}
313 
314 			/*
315 			 * Warn about bogus characters.  If you're using
316 			 * non-ASCII encoding, you're screwing your
317 			 * readers.  Since I'd rather this not happen,
318 			 * I'll be helpful and replace these characters
319 			 * with "?", so we don't display gibberish.
320 			 * Note to manual writers: use special characters.
321 			 */
322 
323 			c = (unsigned char) blk.buf[i];
324 
325 			if ( ! (isascii(c) &&
326 					(isgraph(c) || isblank(c)))) {
327 				mandoc_msg(MANDOCERR_BADCHAR, curp,
328 						curp->line, pos, NULL);
329 				i++;
330 				if (pos >= (int)ln.sz)
331 					resize_buf(&ln, 256);
332 				ln.buf[pos++] = '?';
333 				continue;
334 			}
335 
336 			/* Trailing backslash = a plain char. */
337 
338 			if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
339 				if (pos >= (int)ln.sz)
340 					resize_buf(&ln, 256);
341 				ln.buf[pos++] = blk.buf[i++];
342 				continue;
343 			}
344 
345 			/*
346 			 * Found escape and at least one other character.
347 			 * When it's a newline character, skip it.
348 			 * When there is a carriage return in between,
349 			 * skip that one as well.
350 			 */
351 
352 			if ('\r' == blk.buf[i + 1] && i + 2 < (int)blk.sz &&
353 			    '\n' == blk.buf[i + 2])
354 				++i;
355 			if ('\n' == blk.buf[i + 1]) {
356 				i += 2;
357 				++lnn;
358 				continue;
359 			}
360 
361 			if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) {
362 				i += 2;
363 				/* Comment, skip to end of line */
364 				for (; i < (int)blk.sz; ++i) {
365 					if ('\n' == blk.buf[i]) {
366 						++i;
367 						++lnn;
368 						break;
369 					}
370 				}
371 
372 				/* Backout trailing whitespaces */
373 				for (; pos > 0; --pos) {
374 					if (ln.buf[pos - 1] != ' ')
375 						break;
376 					if (pos > 2 && ln.buf[pos - 2] == '\\')
377 						break;
378 				}
379 				break;
380 			}
381 
382 			/* Some other escape sequence, copy & cont. */
383 
384 			if (pos + 1 >= (int)ln.sz)
385 				resize_buf(&ln, 256);
386 
387 			ln.buf[pos++] = blk.buf[i++];
388 			ln.buf[pos++] = blk.buf[i++];
389 		}
390 
391  		if (pos >= (int)ln.sz)
392 			resize_buf(&ln, 256);
393 
394 		ln.buf[pos] = '\0';
395 
396 		/*
397 		 * A significant amount of complexity is contained by
398 		 * the roff preprocessor.  It's line-oriented but can be
399 		 * expressed on one line, so we need at times to
400 		 * readjust our starting point and re-run it.  The roff
401 		 * preprocessor can also readjust the buffers with new
402 		 * data, so we pass them in wholesale.
403 		 */
404 
405 		of = 0;
406 
407 		/*
408 		 * Maintain a lookaside buffer of all parsed lines.  We
409 		 * only do this if mparse_keep() has been invoked (the
410 		 * buffer may be accessed with mparse_getkeep()).
411 		 */
412 
413 		if (curp->secondary) {
414 			curp->secondary->buf =
415 				mandoc_realloc
416 				(curp->secondary->buf,
417 				 curp->secondary->sz + pos + 2);
418 			memcpy(curp->secondary->buf +
419 					curp->secondary->sz,
420 					ln.buf, pos);
421 			curp->secondary->sz += pos;
422 			curp->secondary->buf
423 				[curp->secondary->sz] = '\n';
424 			curp->secondary->sz++;
425 			curp->secondary->buf
426 				[curp->secondary->sz] = '\0';
427 		}
428 rerun:
429 		rr = roff_parseln
430 			(curp->roff, curp->line,
431 			 &ln.buf, &ln.sz, of, &of);
432 
433 		switch (rr) {
434 		case (ROFF_REPARSE):
435 			if (REPARSE_LIMIT >= ++curp->reparse_count)
436 				mparse_buf_r(curp, ln, 0);
437 			else
438 				mandoc_msg(MANDOCERR_ROFFLOOP, curp,
439 					curp->line, pos, NULL);
440 			pos = 0;
441 			continue;
442 		case (ROFF_APPEND):
443 			pos = (int)strlen(ln.buf);
444 			continue;
445 		case (ROFF_RERUN):
446 			goto rerun;
447 		case (ROFF_IGN):
448 			pos = 0;
449 			continue;
450 		case (ROFF_ERR):
451 			assert(MANDOCLEVEL_FATAL <= curp->file_status);
452 			break;
453 		case (ROFF_SO):
454 			/*
455 			 * We remove `so' clauses from our lookaside
456 			 * buffer because we're going to descend into
457 			 * the file recursively.
458 			 */
459 			if (curp->secondary)
460 				curp->secondary->sz -= pos + 1;
461 			mparse_readfd_r(curp, -1, ln.buf + of, 1);
462 			if (MANDOCLEVEL_FATAL <= curp->file_status)
463 				break;
464 			pos = 0;
465 			continue;
466 		default:
467 			break;
468 		}
469 
470 		/*
471 		 * If we encounter errors in the recursive parse, make
472 		 * sure we don't continue parsing.
473 		 */
474 
475 		if (MANDOCLEVEL_FATAL <= curp->file_status)
476 			break;
477 
478 		/*
479 		 * If input parsers have not been allocated, do so now.
480 		 * We keep these instanced between parsers, but set them
481 		 * locally per parse routine since we can use different
482 		 * parsers with each one.
483 		 */
484 
485 		if ( ! (curp->man || curp->mdoc))
486 			pset(ln.buf + of, pos - of, curp);
487 
488 		/*
489 		 * Lastly, push down into the parsers themselves.  One
490 		 * of these will have already been set in the pset()
491 		 * routine.
492 		 * If libroff returns ROFF_TBL, then add it to the
493 		 * currently open parse.  Since we only get here if
494 		 * there does exist data (see tbl_data.c), we're
495 		 * guaranteed that something's been allocated.
496 		 * Do the same for ROFF_EQN.
497 		 */
498 
499 		rc = -1;
500 
501 		if (ROFF_TBL == rr)
502 			while (NULL != (span = roff_span(curp->roff))) {
503 				rc = curp->man ?
504 					man_addspan(curp->man, span) :
505 					mdoc_addspan(curp->mdoc, span);
506 				if (0 == rc)
507 					break;
508 			}
509 		else if (ROFF_EQN == rr)
510 			rc = curp->mdoc ?
511 				mdoc_addeqn(curp->mdoc,
512 					roff_eqn(curp->roff)) :
513 				man_addeqn(curp->man,
514 					roff_eqn(curp->roff));
515 		else if (curp->man || curp->mdoc)
516 			rc = curp->man ?
517 				man_parseln(curp->man,
518 					curp->line, ln.buf, of) :
519 				mdoc_parseln(curp->mdoc,
520 					curp->line, ln.buf, of);
521 
522 		if (0 == rc) {
523 			assert(MANDOCLEVEL_FATAL <= curp->file_status);
524 			break;
525 		}
526 
527 		/* Temporary buffers typically are not full. */
528 
529 		if (0 == start && '\0' == blk.buf[i])
530 			break;
531 
532 		/* Start the next input line. */
533 
534 		pos = 0;
535 	}
536 
537 	free(ln.buf);
538 }
539 
540 static void
541 pdesc(struct mparse *curp, const char *file, int fd)
542 {
543 	struct buf	 blk;
544 	int		 with_mmap;
545 
546 	/*
547 	 * Run for each opened file; may be called more than once for
548 	 * each full parse sequence if the opened file is nested (i.e.,
549 	 * from `so').  Simply sucks in the whole file and moves into
550 	 * the parse phase for the file.
551 	 */
552 
553 	if ( ! read_whole_file(file, fd, &blk, &with_mmap)) {
554 		curp->file_status = MANDOCLEVEL_SYSERR;
555 		return;
556 	}
557 
558 	/* Line number is per-file. */
559 
560 	curp->line = 1;
561 
562 	mparse_buf_r(curp, blk, 1);
563 
564 	if (with_mmap)
565 		munmap(blk.buf, blk.sz);
566 	else
567 		free(blk.buf);
568 }
569 
570 static int
571 read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
572 {
573 	struct stat	 st;
574 	size_t		 off;
575 	ssize_t		 ssz;
576 
577 	if (-1 == fstat(fd, &st)) {
578 		perror(file);
579 		return(0);
580 	}
581 
582 	/*
583 	 * If we're a regular file, try just reading in the whole entry
584 	 * via mmap().  This is faster than reading it into blocks, and
585 	 * since each file is only a few bytes to begin with, I'm not
586 	 * concerned that this is going to tank any machines.
587 	 */
588 
589 	if (S_ISREG(st.st_mode)) {
590 		if (st.st_size >= (1U << 31)) {
591 			fprintf(stderr, "%s: input too large\n", file);
592 			return(0);
593 		}
594 		*with_mmap = 1;
595 		fb->sz = (size_t)st.st_size;
596 		fb->buf = mmap(NULL, fb->sz, PROT_READ,
597 				MAP_FILE|MAP_SHARED, fd, 0);
598 		if (fb->buf != MAP_FAILED)
599 			return(1);
600 	}
601 
602 	/*
603 	 * If this isn't a regular file (like, say, stdin), then we must
604 	 * go the old way and just read things in bit by bit.
605 	 */
606 
607 	*with_mmap = 0;
608 	off = 0;
609 	fb->sz = 0;
610 	fb->buf = NULL;
611 	for (;;) {
612 		if (off == fb->sz) {
613 			if (fb->sz == (1U << 31)) {
614 				fprintf(stderr, "%s: input too large\n", file);
615 				break;
616 			}
617 			resize_buf(fb, 65536);
618 		}
619 		ssz = read(fd, fb->buf + (int)off, fb->sz - off);
620 		if (ssz == 0) {
621 			fb->sz = off;
622 			return(1);
623 		}
624 		if (ssz == -1) {
625 			perror(file);
626 			break;
627 		}
628 		off += (size_t)ssz;
629 	}
630 
631 	free(fb->buf);
632 	fb->buf = NULL;
633 	return(0);
634 }
635 
636 static void
637 mparse_end(struct mparse *curp)
638 {
639 
640 	if (MANDOCLEVEL_FATAL <= curp->file_status)
641 		return;
642 
643 	if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
644 		assert(MANDOCLEVEL_FATAL <= curp->file_status);
645 		return;
646 	}
647 
648 	if (curp->man && ! man_endparse(curp->man)) {
649 		assert(MANDOCLEVEL_FATAL <= curp->file_status);
650 		return;
651 	}
652 
653 	if ( ! (curp->man || curp->mdoc)) {
654 		mandoc_msg(MANDOCERR_NOTMANUAL, curp, 1, 0, NULL);
655 		curp->file_status = MANDOCLEVEL_FATAL;
656 		return;
657 	}
658 
659 	roff_endparse(curp->roff);
660 }
661 
662 static void
663 mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
664 {
665 	const char	*svfile;
666 
667 	if (-1 == fd)
668 		if (-1 == (fd = open(file, O_RDONLY, 0))) {
669 			perror(file);
670 			curp->file_status = MANDOCLEVEL_SYSERR;
671 			return;
672 		}
673 
674 	svfile = curp->file;
675 	curp->file = file;
676 
677 	pdesc(curp, file, fd);
678 
679 	if (0 == re && MANDOCLEVEL_FATAL > curp->file_status)
680 		mparse_end(curp);
681 
682 	if (STDIN_FILENO != fd && -1 == close(fd))
683 		perror(file);
684 
685 	curp->file = svfile;
686 }
687 
688 enum mandoclevel
689 mparse_readfd(struct mparse *curp, int fd, const char *file)
690 {
691 
692 	mparse_readfd_r(curp, fd, file, 0);
693 	return(curp->file_status);
694 }
695 
696 struct mparse *
697 mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, mandocmsg mmsg, void *arg)
698 {
699 	struct mparse	*curp;
700 
701 	assert(wlevel <= MANDOCLEVEL_FATAL);
702 
703 	curp = mandoc_calloc(1, sizeof(struct mparse));
704 
705 	curp->wlevel = wlevel;
706 	curp->mmsg = mmsg;
707 	curp->arg = arg;
708 	curp->inttype = inttype;
709 
710 	curp->roff = roff_alloc(curp);
711 	return(curp);
712 }
713 
714 void
715 mparse_reset(struct mparse *curp)
716 {
717 
718 	roff_reset(curp->roff);
719 
720 	if (curp->mdoc)
721 		mdoc_reset(curp->mdoc);
722 	if (curp->man)
723 		man_reset(curp->man);
724 	if (curp->secondary)
725 		curp->secondary->sz = 0;
726 
727 	curp->file_status = MANDOCLEVEL_OK;
728 	curp->mdoc = NULL;
729 	curp->man = NULL;
730 }
731 
732 void
733 mparse_free(struct mparse *curp)
734 {
735 
736 	if (curp->pmdoc)
737 		mdoc_free(curp->pmdoc);
738 	if (curp->pman)
739 		man_free(curp->pman);
740 	if (curp->roff)
741 		roff_free(curp->roff);
742 	if (curp->secondary)
743 		free(curp->secondary->buf);
744 
745 	free(curp->secondary);
746 	free(curp);
747 }
748 
749 void
750 mparse_result(struct mparse *curp, struct mdoc **mdoc, struct man **man)
751 {
752 
753 	if (mdoc)
754 		*mdoc = curp->mdoc;
755 	if (man)
756 		*man = curp->man;
757 }
758 
759 void
760 mandoc_vmsg(enum mandocerr t, struct mparse *m,
761 		int ln, int pos, const char *fmt, ...)
762 {
763 	char		 buf[256];
764 	va_list		 ap;
765 
766 	va_start(ap, fmt);
767 	vsnprintf(buf, sizeof(buf) - 1, fmt, ap);
768 	va_end(ap);
769 
770 	mandoc_msg(t, m, ln, pos, buf);
771 }
772 
773 void
774 mandoc_msg(enum mandocerr er, struct mparse *m,
775 		int ln, int col, const char *msg)
776 {
777 	enum mandoclevel level;
778 
779 	level = MANDOCLEVEL_FATAL;
780 	while (er < mandoclimits[level])
781 		level--;
782 
783 	if (level < m->wlevel)
784 		return;
785 
786 	if (m->mmsg)
787 		(*m->mmsg)(er, level, m->file, ln, col, msg);
788 
789 	if (m->file_status < level)
790 		m->file_status = level;
791 }
792 
793 const char *
794 mparse_strerror(enum mandocerr er)
795 {
796 
797 	return(mandocerrs[er]);
798 }
799 
800 const char *
801 mparse_strlevel(enum mandoclevel lvl)
802 {
803 	return(mandoclevels[lvl]);
804 }
805 
806 void
807 mparse_keep(struct mparse *p)
808 {
809 
810 	assert(NULL == p->secondary);
811 	p->secondary = mandoc_calloc(1, sizeof(struct buf));
812 }
813 
814 const char *
815 mparse_getkeep(const struct mparse *p)
816 {
817 
818 	assert(p->secondary);
819 	return(p->secondary->sz ? p->secondary->buf : NULL);
820 }
821