+ if (MANDOCLEVEL_FATAL <= exit_status)
+ goto cleanup;
+
+ /* NOTE a parser may not have been assigned, yet. */
+
+ if ( ! (curp->man || curp->mdoc)) {
+ fprintf(stderr, "%s: Not a manual\n", curp->file);
+ exit_status = MANDOCLEVEL_FATAL;
+ goto cleanup;
+ }
+
+ /* Clean up the parse routine ASTs. */
+
+ if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
+ assert(MANDOCLEVEL_FATAL <= exit_status);
+ goto cleanup;
+ }
+
+ if (curp->man && ! man_endparse(curp->man)) {
+ assert(MANDOCLEVEL_FATAL <= exit_status);
+ goto cleanup;
+ }
+
+ assert(curp->roff);
+ if ( ! roff_endparse(curp->roff)) {
+ assert(MANDOCLEVEL_FATAL <= exit_status);
+ goto cleanup;
+ }
+
+ /*
+ * With -Wstop and warnings or errors of at least
+ * the requested level, do not produce output.
+ */
+
+ if (MANDOCLEVEL_OK != exit_status && curp->wstop)
+ goto cleanup;
+
+ /* If unset, allocate output dev now (if applicable). */
+
+ if ( ! (curp->outman && curp->outmdoc)) {
+ switch (curp->outtype) {
+ case (OUTT_XHTML):
+ curp->outdata = xhtml_alloc(curp->outopts);
+ break;
+ case (OUTT_HTML):
+ curp->outdata = html_alloc(curp->outopts);
+ break;
+ case (OUTT_ASCII):
+ curp->outdata = ascii_alloc(curp->outopts);
+ curp->outfree = ascii_free;
+ break;
+ case (OUTT_PDF):
+ curp->outdata = pdf_alloc(curp->outopts);
+ curp->outfree = pspdf_free;
+ break;
+ case (OUTT_PS):
+ curp->outdata = ps_alloc(curp->outopts);
+ curp->outfree = pspdf_free;
+ break;
+ default:
+ break;
+ }
+
+ switch (curp->outtype) {
+ case (OUTT_HTML):
+ /* FALLTHROUGH */
+ case (OUTT_XHTML):
+ curp->outman = html_man;
+ curp->outmdoc = html_mdoc;
+ curp->outfree = html_free;
+ break;
+ case (OUTT_TREE):
+ curp->outman = tree_man;
+ curp->outmdoc = tree_mdoc;
+ break;
+ case (OUTT_PDF):
+ /* FALLTHROUGH */
+ case (OUTT_ASCII):
+ /* FALLTHROUGH */
+ case (OUTT_PS):
+ curp->outman = terminal_man;
+ curp->outmdoc = terminal_mdoc;
+ break;
+ default:
+ break;
+ }
+ }
+
+ /* Execute the out device, if it exists. */
+
+ if (curp->man && curp->outman)
+ (*curp->outman)(curp->outdata, curp->man);
+ if (curp->mdoc && curp->outmdoc)
+ (*curp->outmdoc)(curp->outdata, curp->mdoc);
+
+ cleanup:
+
+ memset(&curp->regs, 0, sizeof(struct regset));
+
+ /* Reset the current-parse compilers. */
+
+ if (curp->mdoc)
+ mdoc_reset(curp->mdoc);
+ if (curp->man)
+ man_reset(curp->man);
+
+ assert(curp->roff);
+ roff_reset(curp->roff);
+
+ return;
+}
+
+static void
+pdesc(struct curparse *curp)
+{
+ struct buf blk;
+ int with_mmap;
+
+ /*
+ * Run for each opened file; may be called more than once for
+ * each full parse sequence if the opened file is nested (i.e.,
+ * from `so'). Simply sucks in the whole file and moves into
+ * the parse phase for the file.
+ */
+
+ if ( ! read_whole_file(curp, &blk, &with_mmap)) {
+ exit_status = MANDOCLEVEL_SYSERR;
+ return;
+ }
+
+ /* Line number is per-file. */
+
+ curp->line = 1;
+
+ parsebuf(curp, blk, 1);
+
+ if (with_mmap)
+ munmap(blk.buf, blk.sz);
+ else
+ free(blk.buf);
+}
+
+static void
+parsebuf(struct curparse *curp, struct buf blk, int start)
+{
+ struct buf ln;
+ int i, of, rc;
+ int pos; /* byte number in the ln buffer */
+ int lnn; /* line number in the real file */
+ unsigned char c;
+
+ /*
+ * Main parse routine for an opened file. This is called for
+ * each opened file and simply loops around the full input file,
+ * possibly nesting (i.e., with `so').
+ */
+
+ memset(&ln, 0, sizeof(struct buf));
+
+ lnn = curp->line;
+ pos = 0;
+
+ for (i = 0; i < (int)blk.sz; ) {
+ if (0 == pos && '\0' == blk.buf[i])
+ break;
+
+ if (start)
+ curp->line = lnn;
+
+ while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
+ if ('\n' == blk.buf[i]) {
+ ++i;
+ ++lnn;
+ break;
+ }
+
+ /*
+ * Warn about bogus characters. If you're using
+ * non-ASCII encoding, you're screwing your
+ * readers. Since I'd rather this not happen,
+ * I'll be helpful and drop these characters so
+ * we don't display gibberish. Note to manual
+ * writers: use special characters.
+ */
+
+ c = (unsigned char) blk.buf[i];
+
+ if ( ! (isascii(c) &&
+ (isgraph(c) || isblank(c)))) {
+ mmsg(MANDOCERR_BADCHAR, curp,
+ curp->line, pos, "ignoring byte");
+ i++;
+ continue;