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