]>
git.cameronkatri.com Git - mandoc.git/blob - read.c
1 /* $Id: read.c,v 1.107 2015/01/14 17:49:15 schwarze Exp $ */
3 * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
4 * Copyright (c) 2010-2015 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"
45 #define REPARSE_LIMIT 1000
48 struct man
*pman
; /* persistent man parser */
49 struct mdoc
*pmdoc
; /* persistent mdoc parser */
50 struct man
*man
; /* man parser */
51 struct mdoc
*mdoc
; /* mdoc parser */
52 struct roff
*roff
; /* roff parser (!NULL) */
53 const struct mchars
*mchars
; /* character table */
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 */
66 pid_t child
; /* the gunzip(1) process */
69 static void choose_parser(struct mparse
*);
70 static void resize_buf(struct buf
*, size_t);
71 static void mparse_buf_r(struct mparse
*, struct buf
, size_t, int);
72 static int read_whole_file(struct mparse
*, const char *, int,
74 static void mparse_end(struct mparse
*);
75 static void mparse_parse_buffer(struct mparse
*, struct buf
,
78 static const enum mandocerr mandoclimits
[MANDOCLEVEL_MAX
] = {
88 static const char * const mandocerrs
[MANDOCERR_MAX
] = {
93 /* related to the prologue */
94 "missing manual title, using UNTITLED",
95 "missing manual title, using \"\"",
96 "lower case character in document title",
97 "missing manual section, using \"\"",
98 "unknown manual section",
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 "macro neither callable nor escaped",
123 "skipping paragraph macro",
124 "moving paragraph macro out of list",
125 "skipping no-space macro",
126 "blocks badly nested",
127 "nested displays are not portable",
128 "moving content out of list",
129 ".Vt block has child macro",
130 "fill mode already enabled, skipping",
131 "fill mode already disabled, skipping",
134 /* related to missing macro arguments */
135 "skipping empty request",
136 "conditional request controls empty scope",
137 "skipping empty macro",
138 "empty argument, using 0n",
139 "argument count wrong",
140 "missing display type, using -ragged",
141 "list type is not the first argument",
142 "missing -width in -tag list, using 8n",
143 "missing utility name, using \"\"",
144 "empty head in list item",
146 "missing font type, using \\fR",
147 "unknown font type, using \\fR",
148 "nothing follows prefix",
149 "missing -std argument, adding it",
150 "missing eqn box, using \"\"",
152 /* related to bad macro arguments */
153 "unterminated quoted argument",
154 "duplicate argument",
155 "skipping duplicate argument",
156 "skipping duplicate display type",
157 "skipping duplicate list type",
158 "skipping -width argument",
159 "unknown AT&T UNIX version",
160 "comma in function argument",
161 "parenthesis in function name",
162 "invalid content in Rs block",
163 "invalid Boolean argument",
164 "unknown font, skipping request",
166 /* related to plain text */
167 "blank line in fill mode, using .sp",
168 "tab in filled text",
169 "whitespace at end of input line",
171 "invalid escape sequence",
172 "undefined string, using \"\"",
176 /* related to equations */
177 "unexpected equation scope closure",
178 "equation scope open on exit",
179 "overlapping equation scopes",
180 "unexpected end of equation",
182 /* related to tables */
186 "no table layout cells specified",
187 "no table data cells specified",
188 "ignore data in cell",
189 "data block still open",
190 "ignoring extra data cells",
191 "ignoring macro in table",
193 /* related to document structure and macros */
194 "input stack limit exceeded, infinite loop?",
195 "skipping bad character",
196 "skipping unknown macro",
197 "skipping item outside list",
198 "skipping column outside column list",
199 "skipping end of block that is not open",
200 "inserting missing end of block",
201 "appending missing end of block",
203 /* related to request and macro arguments */
204 "escaped character not allowed in a name",
205 "argument count wrong",
206 "NOT IMPLEMENTED: Bd -file",
207 "missing list type, using -item",
208 "missing manual name, using \"\"",
209 "uname(3) system call failed, using UNKNOWN",
210 "unknown standard specifier",
211 "skipping request without numeric argument",
212 "skipping all arguments",
213 "skipping excess arguments",
216 "generic fatal error",
219 "NOT IMPLEMENTED: .so with absolute path or \"..\"",
220 ".so request failed",
223 "gunzip failed with code",
225 "gunzip died from signal",
228 static const char * const mandoclevels
[MANDOCLEVEL_MAX
] = {
240 resize_buf(struct buf
*buf
, size_t initial
)
243 buf
->sz
= buf
->sz
> initial
/2 ? 2 * buf
->sz
: initial
;
244 buf
->buf
= mandoc_realloc(buf
->buf
, buf
->sz
);
248 choose_parser(struct mparse
*curp
)
254 * If neither command line arguments -mdoc or -man select
255 * a parser nor the roff parser found a .Dd or .TH macro
256 * yet, look ahead in the main input buffer.
259 if ((format
= roff_getformat(curp
->roff
)) == 0) {
260 cp
= curp
->primary
->buf
;
261 ep
= cp
+ curp
->primary
->sz
;
263 if (*cp
== '.' || *cp
== '\'') {
265 if (cp
[0] == 'D' && cp
[1] == 'd') {
266 format
= MPARSE_MDOC
;
269 if (cp
[0] == 'T' && cp
[1] == 'H') {
274 cp
= memchr(cp
, '\n', ep
- cp
);
281 if (format
== MPARSE_MDOC
) {
282 if (NULL
== curp
->pmdoc
)
283 curp
->pmdoc
= mdoc_alloc(
284 curp
->roff
, curp
, curp
->defos
,
285 MPARSE_QUICK
& curp
->options
? 1 : 0);
287 curp
->mdoc
= curp
->pmdoc
;
291 /* Fall back to man(7) as a last resort. */
293 if (NULL
== curp
->pman
)
294 curp
->pman
= man_alloc(
295 curp
->roff
, curp
, curp
->defos
,
296 MPARSE_QUICK
& curp
->options
? 1 : 0);
298 curp
->man
= curp
->pman
;
302 * Main parse routine for a buffer.
303 * It assumes encoding and line numbering are already set up.
304 * It can recurse directly (for invocations of user-defined
305 * macros, inline equations, and input line traps)
306 * and indirectly (for .so file inclusion).
309 mparse_buf_r(struct mparse
*curp
, struct buf blk
, size_t i
, int start
)
311 const struct tbl_span
*span
;
313 size_t pos
; /* byte number in the ln buffer */
316 int lnn
; /* line number in the real file */
319 memset(&ln
, 0, sizeof(ln
));
325 if (0 == pos
&& '\0' == blk
.buf
[i
])
330 curp
->reparse_count
= 0;
333 curp
->filenc
& MPARSE_UTF8
&&
334 curp
->filenc
& MPARSE_LATIN1
)
335 curp
->filenc
= preconv_cue(&blk
, i
);
338 while (i
< blk
.sz
&& (start
|| blk
.buf
[i
] != '\0')) {
341 * When finding an unescaped newline character,
342 * leave the character loop to process the line.
343 * Skip a preceding carriage return, if any.
346 if ('\r' == blk
.buf
[i
] && i
+ 1 < blk
.sz
&&
347 '\n' == blk
.buf
[i
+ 1])
349 if ('\n' == blk
.buf
[i
]) {
356 * Make sure we have space for the worst
357 * case of 11 bytes: "\\[u10ffff]\0"
360 if (pos
+ 11 > ln
.sz
)
361 resize_buf(&ln
, 256);
364 * Encode 8-bit input.
369 if ( ! (curp
->filenc
&& preconv_encode(
370 &blk
, &i
, &ln
, &pos
, &curp
->filenc
))) {
371 mandoc_vmsg(MANDOCERR_BADCHAR
,
372 curp
, curp
->line
, pos
,
381 * Exclude control characters.
384 if (c
== 0x7f || (c
< 0x20 && c
!= 0x09)) {
385 mandoc_vmsg(MANDOCERR_BADCHAR
, curp
,
386 curp
->line
, pos
, "0x%x", c
);
392 /* Trailing backslash = a plain char. */
394 if (blk
.buf
[i
] != '\\' || i
+ 1 == blk
.sz
) {
395 ln
.buf
[pos
++] = blk
.buf
[i
++];
400 * Found escape and at least one other character.
401 * When it's a newline character, skip it.
402 * When there is a carriage return in between,
403 * skip that one as well.
406 if ('\r' == blk
.buf
[i
+ 1] && i
+ 2 < blk
.sz
&&
407 '\n' == blk
.buf
[i
+ 2])
409 if ('\n' == blk
.buf
[i
+ 1]) {
415 if ('"' == blk
.buf
[i
+ 1] || '#' == blk
.buf
[i
+ 1]) {
417 /* Comment, skip to end of line */
418 for (; i
< blk
.sz
; ++i
) {
419 if ('\n' == blk
.buf
[i
]) {
426 /* Backout trailing whitespaces */
427 for (; pos
> 0; --pos
) {
428 if (ln
.buf
[pos
- 1] != ' ')
430 if (pos
> 2 && ln
.buf
[pos
- 2] == '\\')
436 /* Catch escaped bogus characters. */
438 c
= (unsigned char) blk
.buf
[i
+1];
440 if ( ! (isascii(c
) &&
441 (isgraph(c
) || isblank(c
)))) {
442 mandoc_vmsg(MANDOCERR_BADCHAR
, curp
,
443 curp
->line
, pos
, "0x%x", c
);
449 /* Some other escape sequence, copy & cont. */
451 ln
.buf
[pos
++] = blk
.buf
[i
++];
452 ln
.buf
[pos
++] = blk
.buf
[i
++];
456 resize_buf(&ln
, 256);
461 * A significant amount of complexity is contained by
462 * the roff preprocessor. It's line-oriented but can be
463 * expressed on one line, so we need at times to
464 * readjust our starting point and re-run it. The roff
465 * preprocessor can also readjust the buffers with new
466 * data, so we pass them in wholesale.
472 * Maintain a lookaside buffer of all parsed lines. We
473 * only do this if mparse_keep() has been invoked (the
474 * buffer may be accessed with mparse_getkeep()).
477 if (curp
->secondary
) {
478 curp
->secondary
->buf
= mandoc_realloc(
479 curp
->secondary
->buf
,
480 curp
->secondary
->sz
+ pos
+ 2);
481 memcpy(curp
->secondary
->buf
+
484 curp
->secondary
->sz
+= pos
;
486 [curp
->secondary
->sz
] = '\n';
487 curp
->secondary
->sz
++;
489 [curp
->secondary
->sz
] = '\0';
492 rr
= roff_parseln(curp
->roff
, curp
->line
, &ln
, &of
);
496 if (REPARSE_LIMIT
>= ++curp
->reparse_count
)
497 mparse_buf_r(curp
, ln
, of
, 0);
499 mandoc_msg(MANDOCERR_ROFFLOOP
, curp
,
500 curp
->line
, pos
, NULL
);
504 pos
= strlen(ln
.buf
);
512 assert(MANDOCLEVEL_FATAL
<= curp
->file_status
);
515 if ( ! (curp
->options
& MPARSE_SO
) &&
516 (i
>= blk
.sz
|| blk
.buf
[i
] == '\0')) {
517 curp
->sodest
= mandoc_strdup(ln
.buf
+ of
);
522 * We remove `so' clauses from our lookaside
523 * buffer because we're going to descend into
524 * the file recursively.
527 curp
->secondary
->sz
-= pos
+ 1;
528 mparse_readfd(curp
, -1, ln
.buf
+ of
);
529 if (MANDOCLEVEL_FATAL
<= curp
->file_status
) {
530 mandoc_vmsg(MANDOCERR_SO_FAIL
,
531 curp
, curp
->line
, pos
,
532 ".so %s", ln
.buf
+ of
);
542 * If we encounter errors in the recursive parse, make
543 * sure we don't continue parsing.
546 if (MANDOCLEVEL_FATAL
<= curp
->file_status
)
550 * If input parsers have not been allocated, do so now.
551 * We keep these instanced between parsers, but set them
552 * locally per parse routine since we can use different
553 * parsers with each one.
556 if ( ! (curp
->man
|| curp
->mdoc
))
560 * Lastly, push down into the parsers themselves.
561 * If libroff returns ROFF_TBL, then add it to the
562 * currently open parse. Since we only get here if
563 * there does exist data (see tbl_data.c), we're
564 * guaranteed that something's been allocated.
565 * Do the same for ROFF_EQN.
568 if (rr
== ROFF_TBL
) {
569 while ((span
= roff_span(curp
->roff
)) != NULL
)
570 if (curp
->man
== NULL
)
571 mdoc_addspan(curp
->mdoc
, span
);
573 man_addspan(curp
->man
, span
);
574 } else if (rr
== ROFF_EQN
) {
575 if (curp
->man
== NULL
)
576 mdoc_addeqn(curp
->mdoc
, roff_eqn(curp
->roff
));
578 man_addeqn(curp
->man
, roff_eqn(curp
->roff
));
579 } else if ((curp
->man
== NULL
?
580 mdoc_parseln(curp
->mdoc
, curp
->line
, ln
.buf
, of
) :
581 man_parseln(curp
->man
, curp
->line
, ln
.buf
, of
)) == 2)
584 /* Temporary buffers typically are not full. */
586 if (0 == start
&& '\0' == blk
.buf
[i
])
589 /* Start the next input line. */
598 read_whole_file(struct mparse
*curp
, const char *file
, int fd
,
599 struct buf
*fb
, int *with_mmap
)
606 if (-1 == fstat(fd
, &st
)) {
608 exit((int)MANDOCLEVEL_SYSERR
);
612 * If we're a regular file, try just reading in the whole entry
613 * via mmap(). This is faster than reading it into blocks, and
614 * since each file is only a few bytes to begin with, I'm not
615 * concerned that this is going to tank any machines.
618 if (S_ISREG(st
.st_mode
)) {
619 if (st
.st_size
>= (1U << 31)) {
620 curp
->file_status
= MANDOCLEVEL_FATAL
;
622 (*curp
->mmsg
)(MANDOCERR_TOOLARGE
,
623 curp
->file_status
, file
, 0, 0, NULL
);
627 fb
->sz
= (size_t)st
.st_size
;
628 fb
->buf
= mmap(NULL
, fb
->sz
, PROT_READ
, MAP_SHARED
, fd
, 0);
629 if (fb
->buf
!= MAP_FAILED
)
635 * If this isn't a regular file (like, say, stdin), then we must
636 * go the old way and just read things in bit by bit.
645 if (fb
->sz
== (1U << 31)) {
646 curp
->file_status
= MANDOCLEVEL_FATAL
;
648 (*curp
->mmsg
)(MANDOCERR_TOOLARGE
,
653 resize_buf(fb
, 65536);
655 ssz
= read(fd
, fb
->buf
+ (int)off
, fb
->sz
- off
);
662 exit((int)MANDOCLEVEL_SYSERR
);
673 mparse_end(struct mparse
*curp
)
676 if (MANDOCLEVEL_FATAL
<= curp
->file_status
)
679 if (curp
->mdoc
== NULL
&&
681 curp
->sodest
== NULL
) {
682 if (curp
->options
& MPARSE_MDOC
)
683 curp
->mdoc
= curp
->pmdoc
;
685 if (curp
->pman
== NULL
)
686 curp
->pman
= man_alloc(
687 curp
->roff
, curp
, curp
->defos
,
688 curp
->options
& MPARSE_QUICK
? 1 : 0);
689 curp
->man
= curp
->pman
;
693 if (curp
->mdoc
&& ! mdoc_endparse(curp
->mdoc
)) {
694 assert(MANDOCLEVEL_FATAL
<= curp
->file_status
);
698 if (curp
->man
&& ! man_endparse(curp
->man
)) {
699 assert(MANDOCLEVEL_FATAL
<= curp
->file_status
);
703 roff_endparse(curp
->roff
);
707 mparse_parse_buffer(struct mparse
*curp
, struct buf blk
, const char *file
)
709 struct buf
*svprimary
;
712 static int recursion_depth
;
714 if (64 < recursion_depth
) {
715 mandoc_msg(MANDOCERR_ROFFLOOP
, curp
, curp
->line
, 0, NULL
);
719 /* Line number is per-file. */
722 svprimary
= curp
->primary
;
723 curp
->primary
= &blk
;
727 /* Skip an UTF-8 byte order mark. */
728 if (curp
->filenc
& MPARSE_UTF8
&& blk
.sz
> 2 &&
729 (unsigned char)blk
.buf
[0] == 0xef &&
730 (unsigned char)blk
.buf
[1] == 0xbb &&
731 (unsigned char)blk
.buf
[2] == 0xbf) {
733 curp
->filenc
&= ~MPARSE_LATIN1
;
737 mparse_buf_r(curp
, blk
, offset
, 1);
739 if (0 == --recursion_depth
&& MANDOCLEVEL_FATAL
> curp
->file_status
)
742 curp
->primary
= svprimary
;
747 mparse_readmem(struct mparse
*curp
, void *buf
, size_t len
,
755 mparse_parse_buffer(curp
, blk
, file
);
756 return(curp
->file_status
);
760 * If a file descriptor is given, use it and assume it points
761 * to the named file. Otherwise, open the named file.
762 * Read the whole file into memory and call the parsers.
763 * Called recursively when an .so request is encountered.
766 mparse_readfd(struct mparse
*curp
, int fd
, const char *file
)
773 save_child
= curp
->child
;
776 else if (mparse_open(curp
, &fd
, file
) >= MANDOCLEVEL_SYSERR
)
779 if (read_whole_file(curp
, file
, fd
, &blk
, &with_mmap
)) {
780 save_filenc
= curp
->filenc
;
781 curp
->filenc
= curp
->options
&
782 (MPARSE_UTF8
| MPARSE_LATIN1
);
783 mparse_parse_buffer(curp
, blk
, file
);
784 curp
->filenc
= save_filenc
;
787 munmap(blk
.buf
, blk
.sz
);
793 if (fd
!= STDIN_FILENO
&& close(fd
) == -1)
798 curp
->child
= save_child
;
799 return(curp
->file_status
);
803 mparse_open(struct mparse
*curp
, int *fd
, const char *file
)
813 /* Unless zipped, try to just open the file. */
815 if ((cp
= strrchr(file
, '.')) == NULL
||
816 strcmp(cp
+ 1, "gz")) {
818 if ((*fd
= open(file
, O_RDONLY
)) != -1)
819 return(MANDOCLEVEL_OK
);
821 /* Open failed; try to append ".gz". */
823 mandoc_asprintf(&cp
, "%s.gz", file
);
828 /* Before forking, make sure the file can be read. */
831 if (access(file
, R_OK
) == -1) {
834 err
= MANDOCERR_SYSOPEN
;
840 if (pipe(pfd
) == -1) {
842 exit((int)MANDOCLEVEL_SYSERR
);
845 switch (curp
->child
= fork()) {
848 exit((int)MANDOCLEVEL_SYSERR
);
851 if (dup2(pfd
[1], STDOUT_FILENO
) == -1) {
853 exit((int)MANDOCLEVEL_SYSERR
);
855 execlp("gunzip", "gunzip", "-c", file
, NULL
);
857 exit((int)MANDOCLEVEL_SYSERR
);
861 return(MANDOCLEVEL_OK
);
868 curp
->file_status
= MANDOCLEVEL_SYSERR
;
870 (*curp
->mmsg
)(err
, curp
->file_status
, curp
->file
,
871 0, 0, strerror(errno
));
874 return(curp
->file_status
);
878 mparse_wait(struct mparse
*curp
)
882 if (curp
->child
== 0)
883 return(MANDOCLEVEL_OK
);
885 if (waitpid(curp
->child
, &status
, 0) == -1) {
887 exit((int)MANDOCLEVEL_SYSERR
);
889 if (WIFSIGNALED(status
)) {
890 mandoc_vmsg(MANDOCERR_SYSSIG
, curp
, 0, 0,
891 "%d", WTERMSIG(status
));
892 curp
->file_status
= MANDOCLEVEL_SYSERR
;
893 return(curp
->file_status
);
895 if (WEXITSTATUS(status
)) {
896 mandoc_vmsg(MANDOCERR_SYSEXIT
, curp
, 0, 0,
897 "%d", WEXITSTATUS(status
));
898 curp
->file_status
= MANDOCLEVEL_SYSERR
;
899 return(curp
->file_status
);
901 return(MANDOCLEVEL_OK
);
905 mparse_alloc(int options
, enum mandoclevel wlevel
, mandocmsg mmsg
,
906 const struct mchars
*mchars
, const char *defos
)
910 assert(wlevel
<= MANDOCLEVEL_FATAL
);
912 curp
= mandoc_calloc(1, sizeof(struct mparse
));
914 curp
->options
= options
;
915 curp
->wlevel
= wlevel
;
919 curp
->mchars
= mchars
;
920 curp
->roff
= roff_alloc(curp
, curp
->mchars
, options
);
921 if (curp
->options
& MPARSE_MDOC
)
922 curp
->pmdoc
= mdoc_alloc(
923 curp
->roff
, curp
, curp
->defos
,
924 curp
->options
& MPARSE_QUICK
? 1 : 0);
925 if (curp
->options
& MPARSE_MAN
)
926 curp
->pman
= man_alloc(
927 curp
->roff
, curp
, curp
->defos
,
928 curp
->options
& MPARSE_QUICK
? 1 : 0);
934 mparse_reset(struct mparse
*curp
)
937 roff_reset(curp
->roff
);
940 mdoc_reset(curp
->mdoc
);
942 man_reset(curp
->man
);
944 curp
->secondary
->sz
= 0;
946 curp
->file_status
= MANDOCLEVEL_OK
;
955 mparse_free(struct mparse
*curp
)
959 mdoc_free(curp
->pmdoc
);
961 man_free(curp
->pman
);
963 roff_free(curp
->roff
);
965 free(curp
->secondary
->buf
);
967 free(curp
->secondary
);
973 mparse_result(struct mparse
*curp
,
974 struct mdoc
**mdoc
, struct man
**man
, char **sodest
)
977 if (sodest
&& NULL
!= (*sodest
= curp
->sodest
)) {
989 mandoc_vmsg(enum mandocerr t
, struct mparse
*m
,
990 int ln
, int pos
, const char *fmt
, ...)
996 (void)vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
999 mandoc_msg(t
, m
, ln
, pos
, buf
);
1003 mandoc_msg(enum mandocerr er
, struct mparse
*m
,
1004 int ln
, int col
, const char *msg
)
1006 enum mandoclevel level
;
1008 level
= MANDOCLEVEL_FATAL
;
1009 while (er
< mandoclimits
[level
])
1012 if (level
< m
->wlevel
)
1016 (*m
->mmsg
)(er
, level
, m
->file
, ln
, col
, msg
);
1018 if (m
->file_status
< level
)
1019 m
->file_status
= level
;
1023 mparse_strerror(enum mandocerr er
)
1026 return(mandocerrs
[er
]);
1030 mparse_strlevel(enum mandoclevel lvl
)
1032 return(mandoclevels
[lvl
]);
1036 mparse_keep(struct mparse
*p
)
1039 assert(NULL
== p
->secondary
);
1040 p
->secondary
= mandoc_calloc(1, sizeof(struct buf
));
1044 mparse_getkeep(const struct mparse
*p
)
1047 assert(p
->secondary
);
1048 return(p
->secondary
->sz
? p
->secondary
->buf
: NULL
);