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