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