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