]>
git.cameronkatri.com Git - mandoc.git/blob - read.c
1 /* $Id: read.c,v 1.93 2014/10/25 01:03:52 schwarze Exp $ */
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2014 Ingo Schwarze <schwarze@openbsd.org>
5 * Copyright (c) 2010, 2012 Joerg Sonnenberger <joerg@netbsd.org>
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21 #include <sys/types.h>
40 #include "mandoc_aux.h"
41 #include "libmandoc.h"
46 #define REPARSE_LIMIT 1000
49 struct man
*pman
; /* persistent man parser */
50 struct mdoc
*pmdoc
; /* persistent mdoc parser */
51 struct man
*man
; /* man parser */
52 struct mdoc
*mdoc
; /* mdoc parser */
53 struct roff
*roff
; /* roff parser (!NULL) */
54 char *sodest
; /* filename pointed to by .so */
55 const char *file
; /* filename of current input file */
56 struct buf
*primary
; /* buffer currently being parsed */
57 struct buf
*secondary
; /* preprocessed copy of input */
58 const char *defos
; /* default operating system */
59 mandocmsg mmsg
; /* warning/error message handler */
60 enum mandoclevel file_status
; /* status of current parse */
61 enum mandoclevel wlevel
; /* ignore messages below this */
62 int options
; /* parser options */
63 int filenc
; /* encoding of the current file */
64 int reparse_count
; /* finite interp. stack */
65 int line
; /* line number in the file */
68 static void choose_parser(struct mparse
*);
69 static void resize_buf(struct buf
*, size_t);
70 static void mparse_buf_r(struct mparse
*, struct buf
, int);
71 static int read_whole_file(struct mparse
*, const char *, int,
73 static void mparse_end(struct mparse
*);
74 static void mparse_parse_buffer(struct mparse
*, struct buf
,
77 static const enum mandocerr mandoclimits
[MANDOCLEVEL_MAX
] = {
87 static const char * const mandocerrs
[MANDOCERR_MAX
] = {
92 /* related to the prologue */
93 "missing manual title, using UNTITLED",
94 "missing manual title, using \"\"",
95 "lower case character in document title",
96 "missing manual section, using \"\"",
97 "unknown manual section",
98 "unknown manual volume or arch",
99 "missing date, using today's date",
100 "cannot parse date, using it verbatim",
101 "missing Os macro, using \"\"",
102 "duplicate prologue macro",
103 "late prologue macro",
104 "skipping late title macro",
105 "prologue macros out of order",
107 /* related to document structure */
108 ".so is fragile, better use ln(1)",
110 "content before first section header",
111 "first section is not \"NAME\"",
112 "bad NAME section contents",
113 "sections out of conventional order",
114 "duplicate section title",
115 "unexpected section",
117 "unusual Xr punctuation",
118 "AUTHORS section without An macro",
120 /* related to macros and nesting */
122 "skipping paragraph macro",
123 "moving paragraph macro out of list",
124 "skipping no-space macro",
125 "blocks badly nested",
126 "nested displays are not portable",
127 "moving content out of list",
128 ".Vt block has child macro",
129 "fill mode already enabled, skipping",
130 "fill mode already disabled, skipping",
133 /* related to missing macro arguments */
134 "skipping empty request",
135 "conditional request controls empty scope",
136 "skipping empty macro",
137 "empty argument, using 0n",
138 "argument count wrong",
139 "missing display type, using -ragged",
140 "list type is not the first argument",
141 "missing -width in -tag list, using 8n",
142 "missing utility name, using \"\"",
143 "empty head in list item",
145 "missing font type, using \\fR",
146 "unknown font type, using \\fR",
147 "missing -std argument, adding it",
148 "missing eqn box, using \"\"",
150 /* related to bad macro arguments */
151 "unterminated quoted argument",
152 "duplicate argument",
153 "skipping duplicate argument",
154 "skipping duplicate display type",
155 "skipping duplicate list type",
156 "skipping -width argument",
157 "unknown AT&T UNIX version",
158 "comma in function argument",
159 "parenthesis in function name",
160 "invalid content in Rs block",
161 "invalid Boolean argument",
162 "unknown font, skipping request",
164 /* related to plain text */
165 "blank line in fill mode, using .sp",
166 "tab in filled text",
167 "whitespace at end of input line",
169 "invalid escape sequence",
170 "undefined string, using \"\"",
174 /* related to equations */
175 "unexpected equation scope closure",
176 "equation scope open on exit",
177 "overlapping equation scopes",
178 "unexpected end of equation",
180 /* related to tables */
184 "no table layout cells specified",
185 "no table data cells specified",
186 "ignore data in cell",
187 "data block still open",
188 "ignoring extra data cells",
190 /* related to document structure and macros */
191 "input stack limit exceeded, infinite loop?",
192 "skipping bad character",
193 "skipping unknown macro",
194 "skipping item outside list",
195 "skipping column outside column list",
196 "skipping end of block that is not open",
197 "inserting missing end of block",
198 "appending missing end of block",
200 /* related to request and macro arguments */
201 "escaped character not allowed in a name",
202 "argument count wrong",
203 "missing list type, using -item",
204 "missing manual name, using \"\"",
205 "uname(3) system call failed, using UNKNOWN",
206 "unknown standard specifier",
207 "skipping request without numeric argument",
208 "skipping all arguments",
209 "skipping excess arguments",
212 "generic fatal error",
215 "NOT IMPLEMENTED: Bd -file",
216 "NOT IMPLEMENTED: .so with absolute path or \"..\"",
217 ".so request failed",
220 "cannot dup file descriptor",
222 "gunzip failed with code",
227 "gunzip died from signal",
232 static const char * const mandoclevels
[MANDOCLEVEL_MAX
] = {
244 resize_buf(struct buf
*buf
, size_t initial
)
247 buf
->sz
= buf
->sz
> initial
/2 ? 2 * buf
->sz
: initial
;
248 buf
->buf
= mandoc_realloc(buf
->buf
, buf
->sz
);
252 choose_parser(struct mparse
*curp
)
258 * If neither command line arguments -mdoc or -man select
259 * a parser nor the roff parser found a .Dd or .TH macro
260 * yet, look ahead in the main input buffer.
263 if ((format
= roff_getformat(curp
->roff
)) == 0) {
264 cp
= curp
->primary
->buf
;
265 ep
= cp
+ curp
->primary
->sz
;
267 if (*cp
== '.' || *cp
== '\'') {
269 if (cp
[0] == 'D' && cp
[1] == 'd') {
270 format
= MPARSE_MDOC
;
273 if (cp
[0] == 'T' && cp
[1] == 'H') {
278 cp
= memchr(cp
, '\n', ep
- cp
);
285 if (format
== MPARSE_MDOC
) {
286 if (NULL
== curp
->pmdoc
)
287 curp
->pmdoc
= mdoc_alloc(
288 curp
->roff
, curp
, curp
->defos
,
289 MPARSE_QUICK
& curp
->options
? 1 : 0);
291 curp
->mdoc
= curp
->pmdoc
;
295 /* Fall back to man(7) as a last resort. */
297 if (NULL
== curp
->pman
)
298 curp
->pman
= man_alloc(curp
->roff
, curp
,
299 MPARSE_QUICK
& curp
->options
? 1 : 0);
301 curp
->man
= curp
->pman
;
305 * Main parse routine for an opened file. This is called for each
306 * opened file and simply loops around the full input file, possibly
307 * nesting (i.e., with `so').
310 mparse_buf_r(struct mparse
*curp
, struct buf blk
, int start
)
312 const struct tbl_span
*span
;
316 int pos
; /* byte number in the ln buffer */
317 int lnn
; /* line number in the real file */
320 memset(&ln
, 0, sizeof(struct buf
));
325 for (i
= blk
.offs
; i
< (int)blk
.sz
; ) {
326 if (0 == pos
&& '\0' == blk
.buf
[i
])
331 curp
->reparse_count
= 0;
334 curp
->filenc
& MPARSE_UTF8
&&
335 curp
->filenc
& MPARSE_LATIN1
) {
337 curp
->filenc
= preconv_cue(&blk
);
341 while (i
< (int)blk
.sz
&& (start
|| '\0' != blk
.buf
[i
])) {
344 * When finding an unescaped newline character,
345 * leave the character loop to process the line.
346 * Skip a preceding carriage return, if any.
349 if ('\r' == blk
.buf
[i
] && i
+ 1 < (int)blk
.sz
&&
350 '\n' == blk
.buf
[i
+ 1])
352 if ('\n' == blk
.buf
[i
]) {
359 * Make sure we have space for the worst
360 * case of 11 bytes: "\\[u10ffff]\0"
363 if (pos
+ 11 > (int)ln
.sz
)
364 resize_buf(&ln
, 256);
367 * Encode 8-bit input.
374 if (curp
->filenc
&& preconv_encode(
375 &blk
, &ln
, &curp
->filenc
)) {
379 mandoc_vmsg(MANDOCERR_BADCHAR
,
380 curp
, curp
->line
, pos
,
389 * Exclude control characters.
392 if (c
== 0x7f || (c
< 0x20 && c
!= 0x09)) {
393 mandoc_vmsg(MANDOCERR_BADCHAR
, curp
,
394 curp
->line
, pos
, "0x%x", c
);
400 /* Trailing backslash = a plain char. */
402 if ('\\' != blk
.buf
[i
] || i
+ 1 == (int)blk
.sz
) {
403 ln
.buf
[pos
++] = blk
.buf
[i
++];
408 * Found escape and at least one other character.
409 * When it's a newline character, skip it.
410 * When there is a carriage return in between,
411 * skip that one as well.
414 if ('\r' == blk
.buf
[i
+ 1] && i
+ 2 < (int)blk
.sz
&&
415 '\n' == blk
.buf
[i
+ 2])
417 if ('\n' == blk
.buf
[i
+ 1]) {
423 if ('"' == blk
.buf
[i
+ 1] || '#' == blk
.buf
[i
+ 1]) {
425 /* Comment, skip to end of line */
426 for (; i
< (int)blk
.sz
; ++i
) {
427 if ('\n' == blk
.buf
[i
]) {
434 /* Backout trailing whitespaces */
435 for (; pos
> 0; --pos
) {
436 if (ln
.buf
[pos
- 1] != ' ')
438 if (pos
> 2 && ln
.buf
[pos
- 2] == '\\')
444 /* Catch escaped bogus characters. */
446 c
= (unsigned char) blk
.buf
[i
+1];
448 if ( ! (isascii(c
) &&
449 (isgraph(c
) || isblank(c
)))) {
450 mandoc_vmsg(MANDOCERR_BADCHAR
, curp
,
451 curp
->line
, pos
, "0x%x", c
);
457 /* Some other escape sequence, copy & cont. */
459 ln
.buf
[pos
++] = blk
.buf
[i
++];
460 ln
.buf
[pos
++] = blk
.buf
[i
++];
463 if (pos
>= (int)ln
.sz
)
464 resize_buf(&ln
, 256);
469 * A significant amount of complexity is contained by
470 * the roff preprocessor. It's line-oriented but can be
471 * expressed on one line, so we need at times to
472 * readjust our starting point and re-run it. The roff
473 * preprocessor can also readjust the buffers with new
474 * data, so we pass them in wholesale.
480 * Maintain a lookaside buffer of all parsed lines. We
481 * only do this if mparse_keep() has been invoked (the
482 * buffer may be accessed with mparse_getkeep()).
485 if (curp
->secondary
) {
486 curp
->secondary
->buf
= mandoc_realloc(
487 curp
->secondary
->buf
,
488 curp
->secondary
->sz
+ pos
+ 2);
489 memcpy(curp
->secondary
->buf
+
492 curp
->secondary
->sz
+= pos
;
494 [curp
->secondary
->sz
] = '\n';
495 curp
->secondary
->sz
++;
497 [curp
->secondary
->sz
] = '\0';
500 rr
= roff_parseln(curp
->roff
, curp
->line
,
501 &ln
.buf
, &ln
.sz
, of
, &of
);
505 if (REPARSE_LIMIT
>= ++curp
->reparse_count
)
506 mparse_buf_r(curp
, ln
, 0);
508 mandoc_msg(MANDOCERR_ROFFLOOP
, curp
,
509 curp
->line
, pos
, NULL
);
513 pos
= (int)strlen(ln
.buf
);
521 assert(MANDOCLEVEL_FATAL
<= curp
->file_status
);
524 if (0 == (MPARSE_SO
& curp
->options
) &&
525 (i
>= (int)blk
.sz
|| '\0' == blk
.buf
[i
])) {
526 curp
->sodest
= mandoc_strdup(ln
.buf
+ of
);
531 * We remove `so' clauses from our lookaside
532 * buffer because we're going to descend into
533 * the file recursively.
536 curp
->secondary
->sz
-= pos
+ 1;
537 mparse_readfd(curp
, -1, ln
.buf
+ of
);
538 if (MANDOCLEVEL_FATAL
<= curp
->file_status
) {
539 mandoc_vmsg(MANDOCERR_SO_FAIL
,
540 curp
, curp
->line
, pos
,
541 ".so %s", ln
.buf
+ of
);
551 * If we encounter errors in the recursive parse, make
552 * sure we don't continue parsing.
555 if (MANDOCLEVEL_FATAL
<= curp
->file_status
)
559 * If input parsers have not been allocated, do so now.
560 * We keep these instanced between parsers, but set them
561 * locally per parse routine since we can use different
562 * parsers with each one.
565 if ( ! (curp
->man
|| curp
->mdoc
))
569 * Lastly, push down into the parsers themselves.
570 * If libroff returns ROFF_TBL, then add it to the
571 * currently open parse. Since we only get here if
572 * there does exist data (see tbl_data.c), we're
573 * guaranteed that something's been allocated.
574 * Do the same for ROFF_EQN.
580 while (NULL
!= (span
= roff_span(curp
->roff
))) {
582 man_addspan(curp
->man
, span
) :
583 mdoc_addspan(curp
->mdoc
, span
);
587 else if (ROFF_EQN
== rr
)
589 mdoc_addeqn(curp
->mdoc
,
590 roff_eqn(curp
->roff
)) :
591 man_addeqn(curp
->man
,
592 roff_eqn(curp
->roff
));
593 else if (curp
->man
|| curp
->mdoc
)
595 man_parseln(curp
->man
,
596 curp
->line
, ln
.buf
, of
) :
597 mdoc_parseln(curp
->mdoc
,
598 curp
->line
, ln
.buf
, of
);
601 assert(MANDOCLEVEL_FATAL
<= curp
->file_status
);
606 /* Temporary buffers typically are not full. */
608 if (0 == start
&& '\0' == blk
.buf
[i
])
611 /* Start the next input line. */
620 read_whole_file(struct mparse
*curp
, const char *file
, int fd
,
621 struct buf
*fb
, int *with_mmap
)
628 if (-1 == fstat(fd
, &st
)) {
629 curp
->file_status
= MANDOCLEVEL_SYSERR
;
631 (*curp
->mmsg
)(MANDOCERR_SYSSTAT
, curp
->file_status
,
632 file
, 0, 0, strerror(errno
));
637 * If we're a regular file, try just reading in the whole entry
638 * via mmap(). This is faster than reading it into blocks, and
639 * since each file is only a few bytes to begin with, I'm not
640 * concerned that this is going to tank any machines.
643 if (S_ISREG(st
.st_mode
)) {
644 if (st
.st_size
>= (1U << 31)) {
645 curp
->file_status
= MANDOCLEVEL_FATAL
;
647 (*curp
->mmsg
)(MANDOCERR_TOOLARGE
,
648 curp
->file_status
, file
, 0, 0, NULL
);
653 fb
->sz
= (size_t)st
.st_size
;
654 fb
->buf
= mmap(NULL
, fb
->sz
, PROT_READ
, MAP_SHARED
, fd
, 0);
655 if (fb
->buf
!= MAP_FAILED
)
661 * If this isn't a regular file (like, say, stdin), then we must
662 * go the old way and just read things in bit by bit.
671 if (fb
->sz
== (1U << 31)) {
672 curp
->file_status
= MANDOCLEVEL_FATAL
;
674 (*curp
->mmsg
)(MANDOCERR_TOOLARGE
,
679 resize_buf(fb
, 65536);
681 ssz
= read(fd
, fb
->buf
+ (int)off
, fb
->sz
- off
);
688 curp
->file_status
= MANDOCLEVEL_SYSERR
;
690 (*curp
->mmsg
)(MANDOCERR_SYSREAD
,
691 curp
->file_status
, file
, 0, 0,
704 mparse_end(struct mparse
*curp
)
707 if (MANDOCLEVEL_FATAL
<= curp
->file_status
)
710 if (curp
->mdoc
== NULL
&&
712 curp
->sodest
== NULL
) {
713 if (curp
->options
& MPARSE_MDOC
)
714 curp
->mdoc
= curp
->pmdoc
;
716 if (curp
->pman
== NULL
)
717 curp
->pman
= man_alloc(curp
->roff
, curp
,
718 curp
->options
& MPARSE_QUICK
? 1 : 0);
719 curp
->man
= curp
->pman
;
723 if (curp
->mdoc
&& ! mdoc_endparse(curp
->mdoc
)) {
724 assert(MANDOCLEVEL_FATAL
<= curp
->file_status
);
728 if (curp
->man
&& ! man_endparse(curp
->man
)) {
729 assert(MANDOCLEVEL_FATAL
<= curp
->file_status
);
733 roff_endparse(curp
->roff
);
737 mparse_parse_buffer(struct mparse
*curp
, struct buf blk
, const char *file
)
739 struct buf
*svprimary
;
741 static int recursion_depth
;
743 if (64 < recursion_depth
) {
744 mandoc_msg(MANDOCERR_ROFFLOOP
, curp
, curp
->line
, 0, NULL
);
748 /* Line number is per-file. */
751 svprimary
= curp
->primary
;
752 curp
->primary
= &blk
;
756 /* Skip an UTF-8 byte order mark. */
757 if (curp
->filenc
& MPARSE_UTF8
&& blk
.sz
> 2 &&
758 (unsigned char)blk
.buf
[0] == 0xef &&
759 (unsigned char)blk
.buf
[1] == 0xbb &&
760 (unsigned char)blk
.buf
[2] == 0xbf) {
762 curp
->filenc
&= ~MPARSE_LATIN1
;
765 mparse_buf_r(curp
, blk
, 1);
767 if (0 == --recursion_depth
&& MANDOCLEVEL_FATAL
> curp
->file_status
)
770 curp
->primary
= svprimary
;
775 mparse_readmem(struct mparse
*curp
, const void *buf
, size_t len
,
780 blk
.buf
= UNCONST(buf
);
784 mparse_parse_buffer(curp
, blk
, file
);
785 return(curp
->file_status
);
789 mparse_readfd(struct mparse
*curp
, int fd
, const char *file
)
795 if (-1 == fd
&& -1 == (fd
= open(file
, O_RDONLY
, 0))) {
796 curp
->file_status
= MANDOCLEVEL_SYSERR
;
798 (*curp
->mmsg
)(MANDOCERR_SYSOPEN
,
800 file
, 0, 0, strerror(errno
));
801 return(curp
->file_status
);
805 * Run for each opened file; may be called more than once for
806 * each full parse sequence if the opened file is nested (i.e.,
807 * from `so'). Simply sucks in the whole file and moves into
808 * the parse phase for the file.
811 if (read_whole_file(curp
, file
, fd
, &blk
, &with_mmap
)) {
812 save_filenc
= curp
->filenc
;
813 curp
->filenc
= curp
->options
&
814 (MPARSE_UTF8
| MPARSE_LATIN1
);
815 mparse_parse_buffer(curp
, blk
, file
);
816 curp
->filenc
= save_filenc
;
819 munmap(blk
.buf
, blk
.sz
);
825 if (STDIN_FILENO
!= fd
&& -1 == close(fd
))
828 return(curp
->file_status
);
832 mparse_open(struct mparse
*curp
, int *fd
, const char *file
,
841 if ((cp
= strrchr(file
, '.')) == NULL
||
842 strcmp(cp
+ 1, "gz")) {
844 if ((*fd
= open(file
, O_RDONLY
)) == -1) {
845 err
= MANDOCERR_SYSOPEN
;
848 return(MANDOCLEVEL_OK
);
851 if (pipe(pfd
) == -1) {
852 err
= MANDOCERR_SYSPIPE
;
856 switch (*child_pid
= fork()) {
858 err
= MANDOCERR_SYSFORK
;
865 if (dup2(pfd
[1], STDOUT_FILENO
) == -1) {
866 err
= MANDOCERR_SYSDUP
;
869 execlp("gunzip", "gunzip", "-c", file
, NULL
);
870 err
= MANDOCERR_SYSEXEC
;
875 return(MANDOCLEVEL_OK
);
881 curp
->file_status
= MANDOCLEVEL_SYSERR
;
883 (*curp
->mmsg
)(err
, curp
->file_status
, file
,
884 0, 0, strerror(errno
));
887 return(curp
->file_status
);
891 mparse_wait(struct mparse
*curp
, pid_t child_pid
)
895 if (waitpid(child_pid
, &status
, 0) == -1) {
896 mandoc_msg(MANDOCERR_SYSWAIT
, curp
, 0, 0,
898 curp
->file_status
= MANDOCLEVEL_SYSERR
;
899 return(curp
->file_status
);
901 if (WIFSIGNALED(status
)) {
902 mandoc_vmsg(MANDOCERR_SYSSIG
, curp
, 0, 0,
903 "%d", WTERMSIG(status
));
904 curp
->file_status
= MANDOCLEVEL_SYSERR
;
905 return(curp
->file_status
);
907 if (WEXITSTATUS(status
)) {
908 mandoc_vmsg(MANDOCERR_SYSEXIT
, curp
, 0, 0,
909 "%d", WEXITSTATUS(status
));
910 curp
->file_status
= MANDOCLEVEL_SYSERR
;
911 return(curp
->file_status
);
913 return(MANDOCLEVEL_OK
);
917 mparse_alloc(int options
, enum mandoclevel wlevel
,
918 mandocmsg mmsg
, const char *defos
)
922 assert(wlevel
<= MANDOCLEVEL_FATAL
);
924 curp
= mandoc_calloc(1, sizeof(struct mparse
));
926 curp
->options
= options
;
927 curp
->wlevel
= wlevel
;
931 curp
->roff
= roff_alloc(curp
, options
);
932 if (curp
->options
& MPARSE_MDOC
)
933 curp
->pmdoc
= mdoc_alloc(
934 curp
->roff
, curp
, curp
->defos
,
935 curp
->options
& MPARSE_QUICK
? 1 : 0);
936 if (curp
->options
& MPARSE_MAN
)
937 curp
->pman
= man_alloc(curp
->roff
, curp
,
938 curp
->options
& MPARSE_QUICK
? 1 : 0);
944 mparse_reset(struct mparse
*curp
)
947 roff_reset(curp
->roff
);
950 mdoc_reset(curp
->mdoc
);
952 man_reset(curp
->man
);
954 curp
->secondary
->sz
= 0;
956 curp
->file_status
= MANDOCLEVEL_OK
;
965 mparse_free(struct mparse
*curp
)
969 mdoc_free(curp
->pmdoc
);
971 man_free(curp
->pman
);
973 roff_free(curp
->roff
);
975 free(curp
->secondary
->buf
);
977 free(curp
->secondary
);
983 mparse_result(struct mparse
*curp
,
984 struct mdoc
**mdoc
, struct man
**man
, char **sodest
)
987 if (sodest
&& NULL
!= (*sodest
= curp
->sodest
)) {
999 mandoc_vmsg(enum mandocerr t
, struct mparse
*m
,
1000 int ln
, int pos
, const char *fmt
, ...)
1006 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
1009 mandoc_msg(t
, m
, ln
, pos
, buf
);
1013 mandoc_msg(enum mandocerr er
, struct mparse
*m
,
1014 int ln
, int col
, const char *msg
)
1016 enum mandoclevel level
;
1018 level
= MANDOCLEVEL_FATAL
;
1019 while (er
< mandoclimits
[level
])
1022 if (level
< m
->wlevel
)
1026 (*m
->mmsg
)(er
, level
, m
->file
, ln
, col
, msg
);
1028 if (m
->file_status
< level
)
1029 m
->file_status
= level
;
1033 mparse_strerror(enum mandocerr er
)
1036 return(mandocerrs
[er
]);
1040 mparse_strlevel(enum mandoclevel lvl
)
1042 return(mandoclevels
[lvl
]);
1046 mparse_keep(struct mparse
*p
)
1049 assert(NULL
== p
->secondary
);
1050 p
->secondary
= mandoc_calloc(1, sizeof(struct buf
));
1054 mparse_getkeep(const struct mparse
*p
)
1057 assert(p
->secondary
);
1058 return(p
->secondary
->sz
? p
->secondary
->buf
: NULL
);