+
+ if (pos >= (int)ln.sz)
+ resize_buf(&ln, 256);
+ ln.buf[pos] = '\0';
+
+ /*
+ * A significant amount of complexity is contained by
+ * the roff preprocessor. It's line-oriented but can be
+ * expressed on one line, so we need at times to
+ * readjust our starting point and re-run it. The roff
+ * preprocessor can also readjust the buffers with new
+ * data, so we pass them in wholesale.
+ */
+
+ of = 0;
+ do {
+ re = roff_parseln(roff, lnn_start,
+ &ln.buf, &ln.sz, of, &of);
+ } while (ROFF_RERUN == re);
+
+ if (ROFF_IGN == re) {
+ continue;
+ } else if (ROFF_ERR == re) {
+ assert(MANDOCLEVEL_FATAL <= exit_status);
+ goto cleanup;
+ }
+
+ /*
+ * If input parsers have not been allocated, do so now.
+ * We keep these instanced betwen parsers, but set them
+ * locally per parse routine since we can use different
+ * parsers with each one.
+ */
+
+ if ( ! (man || mdoc))
+ pset(ln.buf + of, pos - of, curp, &man, &mdoc);
+
+ /* Lastly, push down into the parsers themselves. */
+
+ if (man && ! man_parseln(man, lnn_start, ln.buf, of)) {
+ assert(MANDOCLEVEL_FATAL <= exit_status);
+ goto cleanup;
+ }
+ if (mdoc && ! mdoc_parseln(mdoc, lnn_start, ln.buf, of)) {
+ assert(MANDOCLEVEL_FATAL <= exit_status);
+ goto cleanup;
+ }
+ }
+
+ /* NOTE a parser may not have been assigned, yet. */
+
+ if ( ! (man || mdoc)) {
+ fprintf(stderr, "%s: Not a manual\n", curp->file);
+ exit_status = MANDOCLEVEL_FATAL;
+ goto cleanup;
+ }
+
+ /* Clean up the parse routine ASTs. */
+
+ if (mdoc && ! mdoc_endparse(mdoc)) {
+ assert(MANDOCLEVEL_FATAL <= exit_status);
+ goto cleanup;
+ }
+ if (man && ! man_endparse(man)) {
+ assert(MANDOCLEVEL_FATAL <= exit_status);
+ goto cleanup;
+ }
+ if (roff && ! roff_endparse(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 (man && curp->outman)
+ (*curp->outman)(curp->outdata, man);
+ if (mdoc && curp->outmdoc)
+ (*curp->outmdoc)(curp->outdata, mdoc);
+
+ cleanup:
+ memset(&curp->regs, 0, sizeof(struct regset));
+ if (mdoc)
+ mdoc_reset(mdoc);
+ if (man)
+ man_reset(man);
+ if (roff)
+ roff_reset(roff);
+ if (ln.buf)
+ free(ln.buf);
+ if (with_mmap)
+ munmap(blk.buf, blk.sz);
+ else
+ free(blk.buf);
+
+ return;
+}
+
+
+static void
+pset(const char *buf, int pos, struct curparse *curp,
+ struct man **man, struct mdoc **mdoc)
+{
+ int i;
+
+ /*
+ * Try to intuit which kind of manual parser should be used. If
+ * passed in by command-line (-man, -mdoc), then use that
+ * explicitly. If passed as -mandoc, then try to guess from the
+ * line: either skip dot-lines, use -mdoc when finding `.Dt', or
+ * default to -man, which is more lenient.
+ */
+
+ if ('.' == buf[0] || '\'' == buf[0]) {
+ for (i = 1; buf[i]; i++)
+ if (' ' != buf[i] && '\t' != buf[i])
+ break;
+ if ('\0' == buf[i])
+ return;