diff --git a/contrib/mdocml/NEWS b/contrib/mdocml/NEWS new file mode 100644 index 00000000000..6f21a38dea3 --- /dev/null +++ b/contrib/mdocml/NEWS @@ -0,0 +1,370 @@ +$Id: NEWS,v 1.3 2013/10/13 16:06:50 schwarze Exp $ + +This file lists the most important changes in the mdocml.bsd.lv distribution. + +Changes in version 1.12.3, released on December 31, 2013 + + * In the mdoc(7) SYNOPSIS, line breaks and hanging indentation + now work correctly for .Fo/.Fa/.Fc and .Fn blocks. + Thanks to Franco Fichtner for doing part of the work. + * The mdoc(7) .Bk macro got some addititonal bugfixes. + * In mdoc(7) macro arguments, double quotes can now be quoted + by doubling them, just like in man(7). + Thanks to Tsugutomo ENAMI for the patch. + * At the end of man(7) macro lines, end-of-sentence spacing + now works. Thanks to Franco Fichtner for the patch. + * For backward compatibility, the man(7) parser now supports the + man-ext .UR/.UE (uniform resource identifier) block macros. + * The man(7) parser now handles closing blocks that are not open + more gracefully. + * The man(7) parser now ignores blank lines right after .SH and .SS. + * In the man(7) formatter, reset indentation when leaving a block, + not just when entering the next one. + * The roff(7) .nr request now supports incrementing and decrementing + number registers and stops parsing the number right before the + first non-digit character. + * The roff(7) parser now supports the alternative escape sequence + syntax \C'uXXXX' for Unicode characters. + * The roff(7) parser now parses and ignores the .fam (font family) + and .hw (hyphenation points) requests and the \d and \u escape + sequences. + * The roff(7) manual got a new ESCAPE SEQUENCE REFERENCE. + +Changes in version 1.12.2, released on Oktober 5, 2013 + + * The mdoc(7) to man(7) converter, to be called as mandoc -Tman, + is now fully functional. + * The mandoc(1) utility now supports the -Ios (default operating system) + input option, and the -Tutf8 output mode now actually works. + * The mandocdb(8) utility no longer truncates existing databases when + starting to build new ones, but only replaces them when the build + actually succeeds. + * The man(7) parser now supports the PD macro (paragraph distance), + and (for GNU man-ext compatibility only) EX (example block) and EE + (example end). Plus several bugfixes regarding indentation, line + breaks, and vertical spacing, and regarding RS following TP. + * The roff(7) parser now supports the \f(BI (bold+italic) font escape, + the \z (zero cursor advance) escape and the cc (change control + character) and it (input line trap) requests. Plus bugfixes regarding + the \t (tab) escape, nested escape sequences, and conditional requests. + * In mdoc(7), several bugs were fixed related to UTF-8 output of quoting + enclosures, delimiter handling, list indentation and horizontal and + vertical spacing, formatting of the Lk, %U, and %C macros, plus some + bugfixes related to the handling of syntax errors like badly nested + font blocks, stray Ta macros outside column lists, unterminated It Xo + blocks, and non-text children of Nm blocks. + * In tbl(7), the width of horizontal spans and the vertical spacing + around tables was corrected, and in man(7) files, a crash was fixed + that was triggered by some particular unclosed T{ macros. + * For mandoc developers, we now provide a tbl(3) library manual and + gmdiff, a very small, very simplistic groff-versus-mandoc output + comparison tool. + * Provide this NEWS file. + +Changes in version 1.12.1, released on March 23, 2012 + + * Significant work on apropos(1) and mandocdb(8). These tools are now + much more robust. A whatis(1) implementation is now handled as an + apropos(1) mode. These tools are also able to minimally handle + pre-formatted pages, that is, those already formatted by another + utility such as GNU troff. + * The man.cgi(7) script is also now available for wider testing. + It interfaces with mandocdb(8) manuals cached by catman(8). + HTML output is generated on-the-fly by libmandoc or internal + methods to convert pre-formatted pages. + * The mailing list archive for the discuss and tech lists are being + hosted by Gmane at gmane.comp.tools.mdocml.user and + gmane.comp.tools.mdocml.devel, respectively. + +Changes in version 1.12.0, released on October 8, 2011 + + * This version features a new, work-in-progress mandoc(1) output mode: + -Tman. This mode allows a system maintainer to distribute man(7) + media for older systems that may not natively support mdoc(7), such + as old Solaris systems. + * The -Ofragment option was added to mandoc(1)'s -Thtml and -Txhtml modes. + * While adding features, an apropos(1) utility has been merged from the + mandoc-tools sandbox. This interfaces with mandocdb(8) for semantic + search of manual content. apropos(1) is different from the traditional + apropos primarily in allowing keyword search (such as for functions, + utilities, etc.) and regular expressions. Note that the calling + syntax for apropos is likely to change as it settles down. + * In documentation news, the mdoc(7) and man(7) manuals have been + made considerably more readable by adding MACRO OVERVIEW sections, by + moving the gory details of the LANGUAGE SYNTAX to the roff(7) manual, + and by moving the very technical MACRO SYNTAX sections down to the + bottom of the page. + * Furthermore, for tbl(7), the -Tascii mode horizontal spacing of tables + was rewritten completely. It is now compatible with groff(1), both + with and without frames and rulers. + * Nesting of indented blocks is now supported in man(7), and several + bugs were fixed regarding indentation and alignment. + * The page headers in mdoc(7) are now nicer for very long titles. + +Changes in version 1.11.7, released on September 2, 2011 + + * Added demandoc(1) utility for stripping away macros and escapes. + This replaces the historical deroff(1) utility. + * Also improved the mdoc(7) and man(7) manuals. + +Changes in version 1.11.6, released on August 16, 2011 + + * Handling of tr macro in roff(7) implemented. This makes Perl + documentation much more readable. Hyphenation is also now enabled in + man(7) format documents. Many other general improvements have been + implemented. + +Changes in version 1.11.5, released on July 24, 2011 + + * Significant eqn(7) improvements. mdocml can now parse arbitrary eqn + input (although few GNU extensions are accepted, nor is mixing + low-level roff with eqn). See the eqn(7) manual for details. + For the time being, equations are rendered as simple in-line text. + The equation parser satisfies the language specified in the + Second Edition User's Guide: + http://www.kohala.com/start/troff/v7man/eqn/eqn2e.ps + +Changes in version 1.11.4, released on July 12, 2011 + + * Bug-fixes and clean-ups across all systems, especially in mandocdb(8) + and the man(7) parser. This release was significantly assisted by + participants in OpenBSD's c2k11. Thanks! + +Changes in version 1.11.3, released on May 26, 2011 + + * Introduce locale-encoding of output with the -Tlocale output option and + Unicode escaped-character input. See mandoc(1) and mandoc_char(7), + respectively, for details. This allows for non-ASCII characters (e.g., + \[u5000]) to be rendered in the locale's encoding, if said environment + supports wide-character encoding (if it does not, -Tascii is used + instead). Locale support can be turned off at compile time by removing + -DUSE_WCHAR in the Makefile, in which case -Tlocale is always a synonym + for -Tascii. + * Furthermore, multibyte-encoded documents, such as those in UTF-8, may + be on-the-fly recoded into mandoc(1) input by using the newly-added + preconv(1) utility. Note: in the future, this feature may be + integrated into mandoc(1). + +Changes in version 1.11.2, released on May 12, 2011 + + * Corrected some installation issues in version 1.11.1. + * Further migration to libmandoc. + * Initial public release (this utility is very much under development) + of mandocdb(8). This utility produces keyword databases of manual + content, which features semantic querying of manual content. + +Changes in version 1.11.1, released on April 4, 2011 + + * The earlier libroff, libmdoc, and libman soup have been merged into + a single library, libmandoc, which manages all aspects of parsing + real manuals, from line-handling to tbl(7) parsing. + * As usual, many general fixes and improvements have also occurred. + In particular, a great deal of redundancy and superfluous code has + been removed with the merging of the backend libraries. + * see also the changes in 1.10.10 + +Changes in version 1.10.10, March 20, 2011, NOT released + + * Initial eqn(7) functionality is in place. For the time being, + this is limited to the recognition of equation blocks; + future version of mdocml will expand upon this framework. + +Changes in version 1.10.9, released on January 7, 2011 + + * Many back-end fixes have been implemented: argument handling (quoting), + man(7) improvements, error/warning classes, and many more. + * Initial tbl(7) functionality (see the "TS", "TE", and "T&" macros in + the roff(7) manual) has been merged from tbl.bsd.lv. Output is still + minimal, especially for -Thtml and -Txhtml, but manages to at least + display data. This means that mandoc(1) now has built-in support + for two troff preprocessors via libroff: soelim(1) and tbl(1). + +Changes in version 1.10.8, released on December 24, 2010 + + * Overhauled the -Thtml and -Txhtml output modes. They now display + readable output in arbitrary browsers, including text-based ones like + lynx(1). See HTML and XHTML manuals in the DOCUMENTATION section + for examples. Attention: available style-sheet classes have been + considerably changed! See the example.style.css file for details. + Lastly, libmdoc and libman have been cleaned up and reduced in size + and complexity. + * see also the changes in 1.10.7 + +Changes in version 1.10.7, December 6, 2010, NOT released + + Significant improvements merged from OpenBSD downstream, including: + * many new roff(7) components, + * in-line implementation of troff's soelim(1), + * broken-block handling, + * overhauled error classifications, and + * cleaned up handling of error conditions. + +Changes in version 1.10.6, released on September 27, 2010 + + * Calling conventions for mandoc(1) have changed: -W improved and -f + deprecated. + * Non-ASCII characters are also now uniformly discarded. + * Lots of documentation improvements. + * Many incremental fixes accomodating for groff's more interesting + productions. + * Lastly, pod2man(1) preambles are now fully accepted after some + considerable roff(7) and special character support. + +Changes in version 1.10.5, released on July 27, 2010 + + * Primarily a bug-fix and polish release, but including -Tpdf support + in mandoc(1) by way of "Summer of Code". Highlights: + * fix "Sm" and "Bd" handling + * fix end-of-sentence handling for embedded sentences + * polish man(7) documentation + * document all mdoc(7) macros + * polish mandoc(1) -Tps output + * lots of internal clean-ups in character escapes + * un-break literal contexts in man(7) documents + * improve -Thtml output for -man + * add mandoc(1) -Tpdf support + +Changes in version 1.10.4, released on July 12, 2010 + + * Lots of features developed during both "Summer of Code" and the + OpenBSD c2k10 hackathon: + * minimal "ds" roff(7) symbols are supported + * beautified SYNOPSIS section output + * acceptance of scope-block breakage in mdoc(7) + * clarify error message status + * many minor bug-fixes and formatting issues resolved + * see also changes in 1.10.3 + +Changes in version 1.10.3, June 29, 2010, NOT released + + * variable font-width and paper-size support in mandoc(1) -Tps output + * "Bk" mdoc(7) support + +Changes in version 1.10.2, released on June 19, 2010 + + * Small release featuring text-decoration in -Tps output, + a few minor relaxations of errors, and some optimisations. + +Changes in version 1.10.1, released on June 7, 2010 + + * This primarily focusses on the "Bl" and "It" macros described in + mdoc(7). Multi-line column support is now fully compatible with groff, + as are implicit list entries for columns. + * Removed manuals(7) in favour of http://manpages.bsd.lv. + * The way we handle the SYNOPSIS section (see the SYNOPSIS documentation + in MANUAL STRUCTURE) has also been considerably simplified compared + to groff's method. + * Furthermore, the -Owidth=width output option has been added to -Tascii, + see mandoc(1). + * Lastly, initial PostScript output has been added with the -Tps option + to mandoc(1). It's brutally simple at the moment: fixed-font, with no + font decorations. + +Changes in version 1.10.0, released on May 29, 2010 + + * Release consisting of the results from the m2k10 hackathon and up-merge + from OpenBSD. This requires a significant note of thanks to Ingo + Schwarze (OpenBSD) and Joerg Sonnenberger (NetBSD) for their hard work, + and again to Joerg for hosting m2k10. Highlights (mostly cribbed from + Ingo's m2k10 report) follow in no particular order: + * a libroff preprocessor in front of libmdoc and libman stripping out + roff(7) instructions; + * end-of-sentence (EOS) detection in free-form and macro lines; + * correct handling of tab-separated columnar lists in mdoc(7); + * improved main calling routines to optionally use mmap(3) for better + performance; + * cleaned up exiting when invoked as -Tlint or over multiple files + with -fign-errors; + * error and warning message handling re-written to be unified for + libroff, libmdoc, and libman; + * handling of badly-nested explicit-scoped macros; + * improved free-form text parsing in libman and libmdoc; + * significant GNU troff compatibility improvements in -Tascii, + largely in terms of spacing; + * a regression framework for making sure the many fragilities of GNU + troff aren't trampled in subsequent work; + * support for -Tascii breaking at hyphens encountered in free-form text; + * and many more minor fixes and improvements + +Changes in version 1.9.25, released on May 13, 2010 + + * Fixed handling of "\*(Ba" escape. + * Backed out -fno-ign-chars (pointless complexity). + * Fixed erroneous breaking of literal lines. + * Fixed SYNOPSIS breaking lines before non-initial macros. + * Changed default section ordering. + * Most importantly, the framework for end-of-sentence double-spacing is + in place, now implemented for the "end-of-sentence, end-of-line" rule. + * This is a stable roll-back point before the mandoc hackathon in Rostock! + +Changes in version 1.9.24, released on May 9, 2010 + + * Rolled back break-at-hyphen. + * -DUGLY is now the default (no feature splits!). + * Free-form text is not de-chunked any more: lines are passed + whole-sale into the front-end, including whitespace. + * Added mailing lists. + +Changes in version 1.9.23, released on April 7, 2010 + + * mdocml has been linked to the OpenBSD build. + * This version incorporates many small changes, mostly from patches + by OpenBSD, allowing crufty manuals to slip by with warnings instead + of erroring-out. + * Some subtle semantic issues, such as punctuation scope, have also + been fixed. + * Lastly, some issues with -Thtml have been fixed, which prompted an + update to the online manual pages style layout. + +Changes in version 1.9.22, released on March 31, 2010 + + * Adjusted merge of the significant work by Ingo Schwarze + in getting "Xo" blocks (block full implicit, e.g., "It" + for non-columnar lists) to work properly. This isn't + enabled by default: you must specify -DUGLY as a compiler + flag (see the Makefile for details). + +Changes in version 1.9.20, released on March 30, 2010 + + * More efforts to get roff instructions in man(7) documents under + control. Note that roff instructions embedded in line-scoped, + next-line macros (e.g. "B") are not supported. + * Leading punctuation for mdoc(7) macros, such as "Fl ( ( a", + are now correctly handled. + +Changes in version 1.9.18, released on March 27, 2010 + + * Many fixes (largely pertaining to scope) + and improvements (e.g., handling of apostrophe-control macros, + which fixes the strange "BR" seen in some macro output) + to handling roff instructions in man(7) documents. + +Changes in version 1.9.17, released on March 25, 2010 + + * Accept perlpod(1) standard preamble. + * Also accept (and discard) "de", "dei", "am", "ami", and "ig" + roff macro blocks. + +Changes in version 1.9.16, released on March 22, 2010 + + * Inspired by patches and bug reports by Ingo Schwarze, + allowed man(7) to accept non-printing elements to be nested + within next-line scopes, such as "br" within "B" or "TH", + which is valid roff. + * Longsoon architecture also noted and Makefile cleaned up. + +Changes in version 1.9.15, released on February 18, 2010 + + * Moved to our new BSD.lv home. + * XHTML is now an acceptable output mode for mandoc(1); + * "Xr" made more compatible with groff; + * "Vt" fixed when invoked in SYNOPSIS; + * "\\" escape removed; + * end-of-line white-space detected for all lines; + * subtle bug fixed in list display for some modes; + * compatibility layer checked in for compilation in diverse + UNIX systems; + * and column lengths handled correctly. + +For older releases, see the ChangeLog files +in http://mdocml.bsd.lv/snapshots/ . diff --git a/contrib/mdocml/TODO b/contrib/mdocml/TODO new file mode 100644 index 00000000000..26f42c23b41 --- /dev/null +++ b/contrib/mdocml/TODO @@ -0,0 +1,330 @@ +************************************************************************ +* Official mandoc TODO. +* $Id: TODO,v 1.162 2013/12/25 14:40:34 schwarze Exp $ +************************************************************************ + +************************************************************************ +* crashes +************************************************************************ + +None known. + +************************************************************************ +* missing features +************************************************************************ + +--- missing roff features ---------------------------------------------- + +- roff.c should treat \n(.H>23 and \n(.V>19 in the pod2man(1) + preamble as true, see for example AUTHORS in MooseX::Getopt.3p + reported by Andreas Voegele + Tue, 22 Nov 2011 15:34:47 +0100 on ports@ + +- .ad (adjust margins) + .ad l -- adjust left margin only (flush left) + .ad r -- adjust right margin only (flush right) + .ad c -- center text on line + .ad b -- adjust both margins (alias: .ad n) + .na -- temporarily disable adjustment without changing the mode + .ad -- re-enable adjustment without changing the mode + Adjustment mode is ignored while in no-fill mode (.nf). + +- .as (append to string) + found by jca@ in ratpoison(1) Sun, 30 Jun 2013 12:01:09 +0200 + +- .ce (center N lines) + found by naddy@ in xloadimage(1) + found by Juan Francisco Cantero Hurtado + in lang/racket(1) Thu, 20 Jun 2013 03:19:11 +0200 + +- .fc (field control) + found by naddy@ in xloadimage(1) + +- .ll (line length) + found by naddy@ in textproc/enchant(1) Sat, 12 Oct 2013 03:27:10 +0200 + +- .nr third argument (auto-increment step size, requires \n+) + found by bentley@ in sbcl(1) Mon, 9 Dec 2013 18:36:57 -0700 + +- .ns (no-space mode) occurs in xine-config(1) + reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500 + +- .ta (tab settings) occurs in ircbug(1) and probably gnats(1) + reported by brad@ Sat, 15 Jan 2011 15:50:51 -0500 + +- .ti (temporary indent) + found by naddy@ in xloadimage(1) + found by bentley@ in nmh(1) Mon, 23 Apr 2012 13:38:28 -0600 + +- .while and .shift + found by jca@ in ratpoison(1) Sun, 30 Jun 2013 12:01:09 +0200 + +- \c (interrupted text) should prevent the line break + even inside .Bd literal; that occurs in chat(8) + also found in cclive(1) - DocBook output + +- \h horizontal move + found in cclive(1) DocBook output + Anthony J. Bentley on discuss@ Sat, 21 Sep 2013 22:29:34 -0600 + +- \n+ and \n- numerical register increment and decrement + found by bentley@ in sbcl(1) Mon, 9 Dec 2013 18:36:57 -0700 + +- using undefined strings or macros defines them to be empty + wl@ Mon, 14 Nov 2011 14:37:01 +0000 + +--- missing mdoc features ---------------------------------------------- + +- fix bad block nesting involving multiple identical explicit blocks + see the OpenBSD mdoc_macro.c 1.47 commit message + +- .Bl -column .Xo support is missing + ultimate goal: + restore .Xr and .Dv to + lib/libc/compat-43/sigvec.3 + lib/libc/gen/signal.3 + lib/libc/sys/sigaction.2 + +- edge case: decide how to deal with blk_full bad nesting, e.g. + .Sh .Nm .Bk .Nm .Ek .Sh found by jmc@ in ssh-keygen(1) + from jmc@ Wed, 14 Jul 2010 18:10:32 +0100 + +- \\ is now implemented correctly + * when defining strings and macros using .ds and .de + * when parsing roff(7) and man(7) macro arguments + It does not yet work in mdoc(7) macro arguments + because libmdoc does not yet use mandoc_getarg(). + Also check what happens in plain text, it must be identical to \e. + +- .Bd -filled should not be the same as .Bd -ragged, but align both + the left and right margin. In groff, it is implemented in terms + of .ad b, which we don't have either. Found in cksum(1). + +- implement blank `Bl -column', such as + .Bl -column + .It foo Ta bar + .El + +- explicitly disallow nested `Bl -column', which would clobber internal + flags defined for struct mdoc_macro + +- In .Bl -column .It, the end of the line probably has to be regarded + as an implicit .Ta, if there could be one, see the following mildly + ugly code from login.conf(5): + .Bl -column minpasswordlen program xetcxmotd + .It path Ta path Ta value of Dv _PATH_DEFPATH + .br + Default search path. + reported by Michal Mazurek + via jmc@ Thu, 7 Apr 2011 16:00:53 +0059 + +- inside `.Bl -column' phrases, punctuation is handled like normal + text, e.g. `.Bl -column .It Fl x . Ta ...' should give "-x -." + +- inside `.Bl -column' phrases, TERMP_IGNDELIM handling by `Pf' + is not safe, e.g. `.Bl -column .It Pf a b .' gives "ab." + but should give "ab ." + +- set a meaningful default if no `Bl' list type is assigned + +- have a blank `It' head for `Bl -tag' not puke + +- prohibit `Nm' from having non-text HEAD children + (e.g., NetBSD mDNSShared/dns-sd.1) + (mdoc_html.c and mdoc_term.c `Nm' handlers can be slightly simplified) + +- When there is free text in the SYNOPSIS and that free text contains + the .Nm macro, groff somehow understands to treat the .Nm as an in-line + macro, while mandoc treats it as a block macro and breaks the line. + No idea how the logic for distinguishing in-line and block instances + should be, needs investigation. + uqs@ Thu, 2 Jun 2011 11:03:51 +0200 + uqs@ Thu, 2 Jun 2011 11:33:35 +0200 + +--- missing man features ----------------------------------------------- + +- groff an-ext.tmac macros (.UR, .UE) occur in xine(5) + reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500 + also occur in freeciv-client(6) freeciv-server(6) freeciv-modpack(6) + reported by bentley@ Tue, 30 Oct 2012 01:05:57 -0600 + +- -T[x]html doesn't stipulate non-collapsing spaces in literal mode + +--- missing tbl features ----------------------------------------------- + +- implement basic non-parametric .de to support e.g. sox(1) + reported by naddy@ Sat, 16 Oct 2010 23:51:57 +0200 + *** sox(1) still doesn't work, tbl(1) errors need investigation + +- allow standalone `.' to be interpreted as an end-of-layout + delimiter instead of being thrown away as a no-op roff line + reported by Yuri Pankov, Wed 18 May 2011 11:34:59 CEST + +--- missing misc features ---------------------------------------------- + +- italic correction (\/) in PostScript mode + Werner LEMBERG on groff at gnu dot org Sun, 10 Nov 2013 12:47:46 + +- The whatis(1) utility looks for whole words in Nm. + If the file name of a page does not agree with the contents of any + of its Nm macros (e.g. pool(9)), add the file name as an Nm entry + to the mandoc.db as well, such that whatis(1) finds it. + If there is a page with a file name that does not appear as a substring + neither in Nm nor in Nd, the same fix would allow finding that page + with apropos(1) using the file name as a key, as well. + Issue reported by tedu@ Fri, 05 Jul 2013 21:15:23 -0400 + +- clean up escape sequence handling, creating three classes: + (1) fully implemented, or parsed and ignored without loss of content + (2) unimplemented, potentially causing loss of content + or serious mangling of formatting (e.g. \n) -> ERROR + see textproc/mgdiff(1) for nice examples + (3) undefined, just output the character -> perhaps WARNING + +- look at pages generated from reStructeredText, e.g. devel/mercurial hg(1) + These are a weird mixture of man(7) and custom autogenerated low-level + roff stuff. Figure out to what extent we can cope. + For details, see http://docutils.sourceforge.net/rst.html + noted by stsp@ Sat, 24 Apr 2010 09:17:55 +0200 + reminded by nicm@ Mon, 3 May 2010 09:52:41 +0100 + +- look at pages generated from Texinfo source by yat2m, e.g. security/gnupg + First impression is not that bad. + +- check compatibility with Plan9: + http://swtch.com/usr/local/plan9/tmac/tmac.an + http://swtch.com/plan9port/man/man7/man.html + "Anthony J. Bentley" 28 Dec 2010 21:58:40 -0700 + +************************************************************************ +* formatting issues: ugly output +************************************************************************ + +- a column list with blank `Ta' cells triggers a spurrious + start-with-whitespace printing of a newline + +- In .Bl -column, + .It Em AuthenticationKey Length + ought to render "Key Length" with emphasis, too, + see OpenBSD iked.conf(5). + reported again Nicolas Joly via wiz@ Wed, 12 Oct 2011 00:20:00 +0200 + +- empty phrases in .Bl column produce too few blanks + try e.g. .Bl -column It Ta Ta + reported by millert Fri, 02 Apr 2010 16:13:46 -0400 + +- .%T can have trailing punctuation. Currently, it puts the trailing + punctuation into a trailing MDOC_TEXT element inside its own scope. + That element should rather be outside its scope, such that the + punctuation does not get underlines. This is not trivial to + implement because .%T then needs some features of in_line_eoln() - + slurp all arguments into one single text element - and one feature + of in_line() - put trailing punctuation out of scope. + Found in mount_nfs(8) and exports(5), search for "Appendix". + +- Trailing punctuation after .%T triggers EOS spacing, at least + outside .Rs (eek!). Simply setting ARGSFL_DELIM for .%T is not + the right solution, it sends mandoc into an endless loop. + reported by Nicolas Joly Sat, 17 Nov 2012 11:49:54 +0100 + +- in enclosures, mandoc sometimes fancies a bogus end of sentence + reminded by jmc@ Thu, 23 Sep 2010 18:13:39 +0059 + +- formatting /usr/local/man/man1/latex2man.1 with groff and mandoc + reveals lots of bugs both in groff and mandoc... + reported by bentley@ Wed, 22 May 2013 23:49:30 -0600 + +************************************************************************ +* formatting issues: gratuitous differences +************************************************************************ + +- .Rv (and probably .Ex) print different text if an `Nm' has been named + or not (run a manual without `Nm blah' to see this). I'm not sure + that this exists in the wild, but it's still an error. + +- In .Bl -bullet, the groff bullet is "+\b+\bo\bo", the mandoc bullet + is just "o\bo". + see for example OpenBSD ksh(1) + +- .Pp between two .It in .Bl -column should produce one, + not two blank lines, see e.g. login.conf(5). + reported by jmc@ Sun, 17 Apr 2011 14:04:58 +0059 + reported again by sthen@ Wed, 18 Jan 2012 02:09:39 +0000 (UTC) + +- If the *first* line after .It is .Pp, break the line right after + the tag, do not pad with space characters before breaking. + See the description of the a, c, and i commands in sed(1). + +- If the first line after .It is .D1, do not assert a blank line + in between, see for example tmux(1). + reported by nicm@ 13 Jan 2011 00:18:57 +0000 + +- Trailing punctuation after .It should trigger EOS spacing. + reported by Nicolas Joly Sat, 17 Nov 2012 11:49:54 +0100 + Probably, this should be fixed somewhere in termp_it_pre(), not sure. + +- .Nx 1.0a + should be "NetBSD 1.0A", not "NetBSD 1.0a", + see OpenBSD ccdconfig(8). + +- In .Bl -tag, if a tag exceeds the right margin and must be continued + on the next line, it must be indented by -width, not width+1; + see "rule block|pass" in OpenBSD ifconfig(8). + +- When the -width string contains macros, the macros must be rendered + before measuring the width, for example + .Bl -tag -width ".Dv message" + in magic(5), located in src/usr.bin/file, is the same + as -width 7n, not -width 11n. + The same applies to .Bl -column column widths; + reported again by Nicolas Joly Thu, 1 Mar 2012 13:41:26 +0100 via wiz@ 5 Mar + reported again by Franco Fichtner Fri, 27 Sep 2013 21:02:28 +0200 + An easy partial fix would be to just skip the first word if it starts + with a dot, including any following white space, when measuring. + +- The \& zero-width character counts as output. + That is, when it is alone on a line between two .Pp, + we want three blank lines, not two as in mandoc. + +- Header lines of excessive length: + Port OpenBSD man_term.c rev. 1.25 to mdoc_term.c + and document it in mdoc(7) and man(7) COMPATIBILITY + found while talking to Chris Bennett + +- trailing whitespace must be ignored even when followed by a font escape, + see for example + makes + \fBdig \fR + operate in batch mode + in dig(1). + +************************************************************************ +* performance issues +************************************************************************ + +Several areas can be cleaned up to make mandoc even faster. These are + +- improve hashing mechanism for macros (quite important: performance) + +- improve hashing mechanism for characters (not as important) + +- the PDF file is HUGE: this can be reduced by using relative offsets + +- instead of re-initialising the roff predefined-strings set before each + parse, create a read-only version the first time and copy it + +************************************************************************ +* structural issues +************************************************************************ + +- We use the input line number at several places to distinguish + same-line from different-line input. That plainly doesn't work + with user-defined macros, leading to random breakage. + +- Find better ways to prevent endless loops + in roff(7) macro and string expansion. + +- Finish cleanup of date handling. + Decide which formats should be recognized where. + Update both mdoc(7) and man(7) documentation. + Triggered by Tim van der Molen Tue, 22 Feb 2011 20:30:45 +0100 diff --git a/contrib/mdocml/arch.in b/contrib/mdocml/arch.in index 5113446e468..d0c445f308b 100644 --- a/contrib/mdocml/arch.in +++ b/contrib/mdocml/arch.in @@ -1,4 +1,4 @@ -/* $Id: arch.in,v 1.12 2012/01/28 14:02:17 joerg Exp $ */ +/* $Id: arch.in,v 1.14 2013/09/16 22:12:57 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * @@ -38,9 +38,9 @@ LINE("arm", "ARM") LINE("arm26", "ARM26") LINE("arm32", "ARM32") LINE("armish", "ARMISH") +LINE("armv7", "ARMv7") LINE("aviion", "AViiON") LINE("atari", "ATARI") -LINE("beagle", "Beagle") LINE("bebox", "BeBox") LINE("cats", "cats") LINE("cesfic", "CESFIC") @@ -81,6 +81,7 @@ LINE("netwinder", "NetWinder") LINE("news68k", "NeWS68k") LINE("newsmips", "NeWSMIPS") LINE("next68k", "NeXT68k") +LINE("octeon", "OCTEON") LINE("ofppc", "OFPPC") LINE("palm", "Palm") LINE("pc532", "PC532") diff --git a/contrib/mdocml/chars.c b/contrib/mdocml/chars.c index ce03347b5d8..3ad1f57471c 100644 --- a/contrib/mdocml/chars.c +++ b/contrib/mdocml/chars.c @@ -1,4 +1,4 @@ -/* $Id: chars.c,v 1.52 2011/11/08 00:15:23 kristaps Exp $ */ +/* $Id: chars.c,v 1.54 2013/06/20 22:39:30 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2011 Ingo Schwarze @@ -37,7 +37,7 @@ struct ln { int unicode; }; -#define LINES_MAX 328 +#define LINES_MAX 329 #define CHAR(in, ch, code) \ { NULL, (in), (ch), (code) }, @@ -77,7 +77,7 @@ mchars_alloc(void) */ tab = mandoc_malloc(sizeof(struct mchars)); - htab = mandoc_calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln **)); + htab = mandoc_calloc(PRINT_HI - PRINT_LO + 1, sizeof(struct ln *)); for (i = 0; i < LINES_MAX; i++) { hash = (int)lines[i].code[0] - PRINT_LO; diff --git a/contrib/mdocml/chars.in b/contrib/mdocml/chars.in index a4c45b3c43e..cc6549e7e5b 100644 --- a/contrib/mdocml/chars.in +++ b/contrib/mdocml/chars.in @@ -1,4 +1,4 @@ -/* $Id: chars.in,v 1.42 2011/10/02 10:02:26 kristaps Exp $ */ +/* $Id: chars.in,v 1.43 2013/06/20 22:39:30 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons * @@ -42,6 +42,7 @@ CHAR("&", "", 0) CHAR("^", "", 0) CHAR("|", "", 0) CHAR("}", "", 0) +CHAR("t", "", 0) /* Accents. */ CHAR("a\"", "\"", 779) diff --git a/contrib/mdocml/config.h b/contrib/mdocml/config.h index 12d33558dbd..0b070d587bc 100644 --- a/contrib/mdocml/config.h +++ b/contrib/mdocml/config.h @@ -7,6 +7,7 @@ #include +#define VERSION "1.12.3" #define HAVE_FGETLN #define HAVE_STRPTIME #define HAVE_GETSUBOPT @@ -31,14 +32,16 @@ # endif #endif -#if defined(__APPLE__) -# define htobe32(x) OSSwapHostToBigInt32(x) -# define betoh32(x) OSSwapBigToHostInt32(x) -# define htobe64(x) OSSwapHostToBigInt64(x) -# define betoh64(x) OSSwapBigToHostInt64(x) -#elif defined(__linux__) -# define betoh32(x) be32toh(x) -# define betoh64(x) be64toh(x) +#ifndef HAVE_BETOH64 +# if defined(__APPLE__) +# define betoh64(x) OSSwapBigToHostInt64(x) +# define htobe64(x) OSSwapHostToBigInt64(x) +# elif defined(__sun) +# define betoh64(x) BE_64(x) +# define htobe64(x) BE_64(x) +# else +# define betoh64(x) be64toh(x) +# endif #endif #ifndef HAVE_STRLCAT diff --git a/contrib/mdocml/eqn.7 b/contrib/mdocml/eqn.7 index f86b9c496bb..dcbad41cc15 100644 --- a/contrib/mdocml/eqn.7 +++ b/contrib/mdocml/eqn.7 @@ -1,4 +1,4 @@ -.\" $Id: eqn.7,v 1.28 2011/09/25 18:37:09 schwarze Exp $ +.\" $Id: eqn.7,v 1.29 2013/07/13 19:41:16 schwarze Exp $ .\" .\" Copyright (c) 2011 Kristaps Dzonsons .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: September 25 2011 $ +.Dd $Mdocdate: July 13 2013 $ .Dt EQN 7 .Os .Sh NAME @@ -276,5 +276,4 @@ was added in 2011. This .Nm reference was written by -.An Kristaps Dzonsons , -.Mt kristaps@bsd.lv . +.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . diff --git a/contrib/mdocml/html.c b/contrib/mdocml/html.c index 326df035fc4..9d28b4270e4 100644 --- a/contrib/mdocml/html.c +++ b/contrib/mdocml/html.c @@ -1,7 +1,7 @@ -/* $Id: html.c,v 1.150 2011/10/05 21:35:17 kristaps Exp $ */ +/* $Id: html.c,v 1.152 2013/08/08 20:07:47 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons - * Copyright (c) 2011 Ingo Schwarze + * Copyright (c) 2011, 2012, 2013 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -235,6 +235,9 @@ print_metaf(struct html *h, enum mandoc_esc deco) case (ESCAPE_FONTBOLD): font = HTMLFONT_BOLD; break; + case (ESCAPE_FONTBI): + font = HTMLFONT_BI; + break; case (ESCAPE_FONT): /* FALLTHROUGH */ case (ESCAPE_FONTROMAN): @@ -253,17 +256,27 @@ print_metaf(struct html *h, enum mandoc_esc deco) h->metal = h->metac; h->metac = font; - if (HTMLFONT_NONE != font) - h->metaf = HTMLFONT_BOLD == font ? - print_otag(h, TAG_B, 0, NULL) : - print_otag(h, TAG_I, 0, NULL); + switch (font) { + case (HTMLFONT_ITALIC): + h->metaf = print_otag(h, TAG_I, 0, NULL); + break; + case (HTMLFONT_BOLD): + h->metaf = print_otag(h, TAG_B, 0, NULL); + break; + case (HTMLFONT_BI): + h->metaf = print_otag(h, TAG_B, 0, NULL); + print_otag(h, TAG_I, 0, NULL); + break; + default: + break; + } } int html_strlen(const char *cp) { - int ssz, sz; - const char *seq, *p; + size_t rsz; + int skip, sz; /* * Account for escaped sequences within string length @@ -274,10 +287,21 @@ html_strlen(const char *cp) */ sz = 0; - while (NULL != (p = strchr(cp, '\\'))) { - sz += (int)(p - cp); - ++cp; - switch (mandoc_escape(&cp, &seq, &ssz)) { + skip = 0; + while (1) { + rsz = strcspn(cp, "\\"); + if (rsz) { + cp += rsz; + if (skip) { + skip = 0; + rsz--; + } + sz += rsz; + } + if ('\0' == *cp) + break; + cp++; + switch (mandoc_escape(&cp, NULL, NULL)) { case (ESCAPE_ERROR): return(sz); case (ESCAPE_UNICODE): @@ -285,15 +309,19 @@ html_strlen(const char *cp) case (ESCAPE_NUMBERED): /* FALLTHROUGH */ case (ESCAPE_SPECIAL): - sz++; + if (skip) + skip = 0; + else + sz++; + break; + case (ESCAPE_SKIPCHAR): + skip = 1; break; default: break; } } - - assert(sz >= 0); - return(sz + strlen(cp)); + return(sz); } static int @@ -308,6 +336,12 @@ print_encode(struct html *h, const char *p, int norecurse) nospace = 0; while ('\0' != *p) { + if (HTML_SKIPCHAR & h->flags && '\\' != *p) { + h->flags &= ~HTML_SKIPCHAR; + p++; + continue; + } + sz = strcspn(p, rejs); fwrite(p, 1, sz, stdout); @@ -337,6 +371,33 @@ print_encode(struct html *h, const char *p, int norecurse) if (ESCAPE_ERROR == esc) break; + switch (esc) { + case (ESCAPE_FONT): + /* FALLTHROUGH */ + case (ESCAPE_FONTPREV): + /* FALLTHROUGH */ + case (ESCAPE_FONTBOLD): + /* FALLTHROUGH */ + case (ESCAPE_FONTITALIC): + /* FALLTHROUGH */ + case (ESCAPE_FONTBI): + /* FALLTHROUGH */ + case (ESCAPE_FONTROMAN): + if (0 == norecurse) + print_metaf(h, esc); + continue; + case (ESCAPE_SKIPCHAR): + h->flags |= HTML_SKIPCHAR; + continue; + default: + break; + } + + if (h->flags & HTML_SKIPCHAR) { + h->flags &= ~HTML_SKIPCHAR; + continue; + } + switch (esc) { case (ESCAPE_UNICODE): /* Skip passed "u" header. */ @@ -356,19 +417,6 @@ print_encode(struct html *h, const char *p, int norecurse) else if (-1 == c && 1 == len) putchar((int)*seq); break; - case (ESCAPE_FONT): - /* FALLTHROUGH */ - case (ESCAPE_FONTPREV): - /* FALLTHROUGH */ - case (ESCAPE_FONTBOLD): - /* FALLTHROUGH */ - case (ESCAPE_FONTITALIC): - /* FALLTHROUGH */ - case (ESCAPE_FONTROMAN): - if (norecurse) - break; - print_metaf(h, esc); - break; case (ESCAPE_NOSPACE): if ('\0' == *p) nospace = 1; @@ -511,10 +559,20 @@ print_text(struct html *h, const char *word) } assert(NULL == h->metaf); - if (HTMLFONT_NONE != h->metac) - h->metaf = HTMLFONT_BOLD == h->metac ? - print_otag(h, TAG_B, 0, NULL) : - print_otag(h, TAG_I, 0, NULL); + switch (h->metac) { + case (HTMLFONT_ITALIC): + h->metaf = print_otag(h, TAG_I, 0, NULL); + break; + case (HTMLFONT_BOLD): + h->metaf = print_otag(h, TAG_B, 0, NULL); + break; + case (HTMLFONT_BI): + h->metaf = print_otag(h, TAG_B, 0, NULL); + print_otag(h, TAG_I, 0, NULL); + break; + default: + break; + } assert(word); if ( ! print_encode(h, word, 0)) { diff --git a/contrib/mdocml/html.h b/contrib/mdocml/html.h index 60960702f19..894cfc4cff4 100644 --- a/contrib/mdocml/html.h +++ b/contrib/mdocml/html.h @@ -1,4 +1,4 @@ -/* $Id: html.h,v 1.47 2011/10/05 21:35:17 kristaps Exp $ */ +/* $Id: html.h,v 1.49 2013/08/08 20:07:47 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * @@ -75,6 +75,7 @@ enum htmlfont { HTMLFONT_NONE = 0, HTMLFONT_BOLD, HTMLFONT_ITALIC, + HTMLFONT_BI, HTMLFONT_MAX }; @@ -117,6 +118,7 @@ struct html { #define HTML_PREKEEP (1 << 3) #define HTML_NONOSPACE (1 << 4) /* never add spaces */ #define HTML_LITERAL (1 << 5) /* literal (e.g.,
) context */
+#define	HTML_SKIPCHAR	 (1 << 6) /* skip the next character */
 	struct tagq	  tags; /* stack of open tags */
 	struct rofftbl	  tbl; /* current table */
 	struct tag	 *tblt; /* current open table scope */
diff --git a/contrib/mdocml/lib.in b/contrib/mdocml/lib.in
index 6af91106dbd..6e7bb7849be 100644
--- a/contrib/mdocml/lib.in
+++ b/contrib/mdocml/lib.in
@@ -1,4 +1,4 @@
-/*	$Id: lib.in,v 1.13 2012/01/28 23:46:28 joerg Exp $ */
+/*	$Id: lib.in,v 1.17 2013/10/13 15:24:03 schwarze Exp $ */
 /*
  * Copyright (c) 2009 Kristaps Dzonsons 
  *
@@ -38,18 +38,24 @@ LINE("libcipher",	"FreeSec Crypt Library (libcipher, \\-lcipher)")
 LINE("libcompat",	"Compatibility Library (libcompat, \\-lcompat)")
 LINE("libcrypt",	"Crypt Library (libcrypt, \\-lcrypt)")
 LINE("libcurses",	"Curses Library (libcurses, \\-lcurses)")
+LINE("libdevattr",	"Device attribute and event library (libdevattr, \\-ldevattr)")
 LINE("libdevinfo",	"Device and Resource Information Utility Library (libdevinfo, \\-ldevinfo)")
 LINE("libdevstat",	"Device Statistics Library (libdevstat, \\-ldevstat)")
 LINE("libdisk",		"Interface to Slice and Partition Labels Library (libdisk, \\-ldisk)")
+LINE("libdm",		"Device Mapper Library (libdm, \\-ldm)")
 LINE("libdwarf",	"DWARF Access Library (libdwarf, \\-ldwarf)")
 LINE("libedit",		"Command Line Editor Library (libedit, \\-ledit)")
 LINE("libefi",		"EFI Runtime Services Library (libefi, \\-lefi)")
 LINE("libelf",		"ELF Access Library (libelf, \\-lelf)")
 LINE("libevent",	"Event Notification Library (libevent, \\-levent)")
-LINE("libfetch",	"File Transfer Library for URLs (libfetch, \\-lfetch)")
+LINE("libexecinfo",	"Backtrace Information Library (libexecinfo, \\-lexecinfo)")
+LINE("libfetch",	"File Transfer Library (libfetch, \\-lfetch)")
+LINE("libfsid",		"Filesystem Identification Library (libfsid, \\-lfsid)")
+LINE("libftpio",	"FTP Connection Management Library (libftpio, \\-lftpio)")
 LINE("libform",		"Curses Form Library (libform, \\-lform)")
-LINE("libgeom",		"Userland API Library for kernel GEOM subsystem (libgeom, \\-lgeom)")
+LINE("libgeom",		"Userland API Library for Kernel GEOM subsystem (libgeom, \\-lgeom)")
 LINE("libgpib",		"General-Purpose Instrument Bus (GPIB) library (libgpib, \\-lgpib)")
+LINE("libhammer",	"HAMMER Filesystem Userland Library (libhammer, \\-lhammer)")
 LINE("libi386",		"i386 Architecture Library (libi386, \\-li386)")
 LINE("libintl",		"Internationalized Message Handling Library (libintl, \\-lintl)")
 LINE("libipsec",	"IPsec Policy Control Library (libipsec, \\-lipsec)")
@@ -57,24 +63,31 @@ LINE("libipx",		"IPX Address Conversion Support Library (libipx, \\-lipx)")
 LINE("libiscsi",	"iSCSI protocol library (libiscsi, \\-liscsi)")
 LINE("libisns",		"Internet Storage Name Service Library (libisns, \\-lisns)")
 LINE("libjail",		"Jail Library (libjail, \\-ljail)")
-LINE("libkiconv",	"Kernel side iconv library (libkiconv, \\-lkiconv)")
+LINE("libkcore",	"Kernel Memory Core Access Library (libkcore, \\-lkcore)")
+LINE("libkiconv",	"Kernel-side iconv Library (libkiconv, \\-lkiconv)")
 LINE("libkse",		"N:M Threading Library (libkse, \\-lkse)")
 LINE("libkvm",		"Kernel Data Access Library (libkvm, \\-lkvm)")
 LINE("libm",		"Math Library (libm, \\-lm)")
 LINE("libm68k",		"m68k Architecture Library (libm68k, \\-lm68k)")
 LINE("libmagic",	"Magic Number Recognition Library (libmagic, \\-lmagic)")
+LINE("libmandoc",	"Mandoc Macro Compiler Library (libmandoc, \\-lmandoc)")
 LINE("libmd",		"Message Digest (MD4, MD5, etc.) Support Library (libmd, \\-lmd)")
 LINE("libmemstat",	"Kernel Memory Allocator Statistics Library (libmemstat, \\-lmemstat)")
 LINE("libmenu",		"Curses Menu Library (libmenu, \\-lmenu)")
+LINE("libmj",		"Minimalist JSON library (libmj, \\-lmj)")
 LINE("libnetgraph",	"Netgraph User Library (libnetgraph, \\-lnetgraph)")
-LINE("libnetpgp",	"Netpgp signing, verification, encryption and decryption (libnetpgp, \\-lnetpgp)")
+LINE("libnetpgp",	"Netpgp Signing, Verification, Encryption and Decryption (libnetpgp, \\-lnetpgp)")
+LINE("libnetpgpverify",	"Netpgp Verification (libnetpgpverify, \\-lnetpgpverify)")
+LINE("libnpf",		"NPF Packet Filter Library (libnpf, \\-lnpf)")
 LINE("libnv",		"Name/value pairs library (libnv, \\-lnv)")
 LINE("libossaudio",	"OSS Audio Emulation Library (libossaudio, \\-lossaudio)")
 LINE("libpam",		"Pluggable Authentication Module Library (libpam, \\-lpam)")
 LINE("libpcap",		"Packet Capture Library (libpcap, \\-lpcap)")
 LINE("libpci",		"PCI Bus Access Library (libpci, \\-lpci)")
 LINE("libpmc",		"Performance Counters Library (libpmc, \\-lpmc)")
+LINE("libppath",	"Property-List Paths Library (libppath, \\-lppath)")
 LINE("libposix",	"POSIX Compatibility Library (libposix, \\-lposix)")
+LINE("libposix1e",	"POSIX.1e Security API Library (libposix1e, \\-lposix1e)")
 LINE("libppath",	"Property-List Paths Library (libppath, \\-lppath)")
 LINE("libproc",		"Processor Monitoring and Analysis Library (libproc, \\-lproc)")
 LINE("libprocstat",	"Process and Files Information Retrieval (libprocstat, \\-lprocstat)")
@@ -82,6 +95,7 @@ LINE("libprop",		"Property Container Object Library (libprop, \\-lprop)")
 LINE("libpthread",	"POSIX Threads Library (libpthread, \\-lpthread)")
 LINE("libpuffs",	"puffs Convenience Library (libpuffs, \\-lpuffs)")
 LINE("libquota",	"Disk Quota Access and Control Library (libquota, \\-lquota)")
+LINE("libradius",	"RADIUS Client Library (libradius, \\-lradius)")
 LINE("librefuse",	"File System in Userspace Convenience Library (librefuse, \\-lrefuse)")
 LINE("libresolv",	"DNS Resolver Library (libresolv, \\-lresolv)")
 LINE("librpcsec_gss",	"RPC GSS-API Authentication Library (librpcsec_gss, \\-lrpcsec_gss)")
@@ -94,6 +108,8 @@ LINE("libsdp",		"Bluetooth Service Discovery Protocol User Library (libsdp, \\-l
 LINE("libssp",		"Buffer Overflow Protection Library (libssp, \\-lssp)")
 LINE("libstdthreads",	"C11 Threads Library (libstdthreads, \\-lstdthreads)")
 LINE("libSystem",	"System Library (libSystem, \\-lSystem)")
+LINE("libtacplus",	"TACACS+ Client Library (libtacplus, \\-ltacplus)")
+LINE("libtcplay",	"TrueCrypt-compatible API library (libtcplay, \\-ltcplay)")
 LINE("libtermcap",	"Termcap Access Library (libtermcap, \\-ltermcap)")
 LINE("libterminfo",	"Terminal Information Library (libterminfo, \\-lterminfo)")
 LINE("libthr",		"1:1 Threading Library (libthr, \\-lthr)")
diff --git a/contrib/mdocml/libman.h b/contrib/mdocml/libman.h
index 4bc5128204f..f2ba6a12563 100644
--- a/contrib/mdocml/libman.h
+++ b/contrib/mdocml/libman.h
@@ -1,4 +1,4 @@
-/*	$Id: libman.h,v 1.55 2011/11/07 01:24:40 schwarze Exp $ */
+/*	$Id: libman.h,v 1.56 2012/11/17 00:26:33 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  *
@@ -39,7 +39,7 @@ struct	man {
 	struct roff	*roff;
 };
 
-#define	MACRO_PROT_ARGS	  struct man *m, \
+#define	MACRO_PROT_ARGS	  struct man *man, \
 			  enum mant tok, \
 			  int line, \
 			  int ppos, \
@@ -61,10 +61,10 @@ extern	const struct man_macro *const man_macros;
 
 __BEGIN_DECLS
 
-#define		  man_pmsg(m, l, p, t) \
-		  mandoc_msg((t), (m)->parse, (l), (p), NULL)
-#define		  man_nmsg(m, n, t) \
-		  mandoc_msg((t), (m)->parse, (n)->line, (n)->pos, NULL)
+#define		  man_pmsg(man, l, p, t) \
+		  mandoc_msg((t), (man)->parse, (l), (p), NULL)
+#define		  man_nmsg(man, n, t) \
+		  mandoc_msg((t), (man)->parse, (n)->line, (n)->pos, NULL)
 int		  man_word_alloc(struct man *, int, int, const char *);
 int		  man_block_alloc(struct man *, int, int, enum mant);
 int		  man_head_alloc(struct man *, int, int, enum mant);
diff --git a/contrib/mdocml/libmandoc.h b/contrib/mdocml/libmandoc.h
index de422884a21..3c005e106da 100644
--- a/contrib/mdocml/libmandoc.h
+++ b/contrib/mdocml/libmandoc.h
@@ -1,6 +1,7 @@
-/*	$Id: libmandoc.h,v 1.29 2011/12/02 01:37:14 schwarze Exp $ */
+/*	$Id: libmandoc.h,v 1.35 2013/12/15 21:23:52 schwarze Exp $ */
 /*
- * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons 
+ * Copyright (c) 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -29,11 +30,6 @@ enum	rofferr {
 	ROFF_ERR /* badness: puke and stop */
 };
 
-enum	regs {
-	REG_nS = 0, /* nS register */
-	REG__MAX
-};
-
 __BEGIN_DECLS
 
 struct	roff;
@@ -47,12 +43,11 @@ void		 mandoc_vmsg(enum mandocerr, struct mparse *,
 char		*mandoc_getarg(struct mparse *, char **, int, int *);
 char		*mandoc_normdate(struct mparse *, char *, int, int);
 int		 mandoc_eos(const char *, size_t, int);
-int		 mandoc_getcontrol(const char *, int *);
 int		 mandoc_strntoi(const char *, size_t, int);
 const char	*mandoc_a2msec(const char*);
 
 void	 	 mdoc_free(struct mdoc *);
-struct	mdoc	*mdoc_alloc(struct roff *, struct mparse *);
+struct	mdoc	*mdoc_alloc(struct roff *, struct mparse *, char *);
 void		 mdoc_reset(struct mdoc *);
 int	 	 mdoc_parseln(struct mdoc *, int, char *, int);
 int		 mdoc_endparse(struct mdoc *);
@@ -68,15 +63,16 @@ int		 man_addspan(struct man *, const struct tbl_span *);
 int		 man_addeqn(struct man *, const struct eqn *);
 
 void	 	 roff_free(struct roff *);
-struct roff	*roff_alloc(struct mparse *);
+struct roff	*roff_alloc(enum mparset, struct mparse *);
 void		 roff_reset(struct roff *);
 enum rofferr	 roff_parseln(struct roff *, int, 
 			char **, size_t *, int, int *);
 void		 roff_endparse(struct roff *);
-int		 roff_regisset(const struct roff *, enum regs);
-unsigned int	 roff_regget(const struct roff *, enum regs);
-void		 roff_regunset(struct roff *, enum regs);
+void		 roff_setreg(struct roff *, const char *, int, char sign);
+int		 roff_getreg(const struct roff *, const char *);
 char		*roff_strdup(const struct roff *, const char *);
+int		 roff_getcontrol(const struct roff *, 
+			const char *, int *);
 #if 0
 char		 roff_eqndelim(const struct roff *);
 void		 roff_openeqn(struct roff *, const char *, 
diff --git a/contrib/mdocml/libmdoc.h b/contrib/mdocml/libmdoc.h
index af1729268a4..3f14519d3b4 100644
--- a/contrib/mdocml/libmdoc.h
+++ b/contrib/mdocml/libmdoc.h
@@ -1,6 +1,7 @@
-/*	$Id: libmdoc.h,v 1.78 2011/12/02 01:37:14 schwarze Exp $ */
+/*	$Id: libmdoc.h,v 1.82 2013/10/21 23:47:58 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -24,6 +25,7 @@ enum	mdoc_next {
 
 struct	mdoc {
 	struct mparse	 *parse; /* parse pointer */
+	char		 *defos; /* default argument for .Os */
 	int		  flags; /* parse flags */
 #define	MDOC_HALT	 (1 << 0) /* error in parse: halt */
 #define	MDOC_LITERAL	 (1 << 1) /* in a literal scope */
@@ -33,6 +35,8 @@ struct	mdoc {
 #define	MDOC_PPHRASE	 (1 << 5) /* within a partial phrase */
 #define	MDOC_FREECOL	 (1 << 6) /* `It' invocation should close */
 #define	MDOC_SYNOPSIS	 (1 << 7) /* SYNOPSIS-style formatting */
+#define	MDOC_KEEP	 (1 << 8) /* in a word keep */
+#define	MDOC_SMOFF	 (1 << 9) /* spacing is off */
 	enum mdoc_next	  next; /* where to put the next node */
 	struct mdoc_node *last; /* the last node parsed */
 	struct mdoc_node *first; /* the first node parsed */
@@ -42,7 +46,7 @@ struct	mdoc {
 	struct roff	 *roff;
 };
 
-#define	MACRO_PROT_ARGS	struct mdoc *m, \
+#define	MACRO_PROT_ARGS	struct mdoc *mdoc, \
 			enum mdoct tok, \
 			int line, \
 			int ppos, \
@@ -56,8 +60,8 @@ struct	mdoc_macro {
 #define	MDOC_PARSED	 (1 << 1)
 #define	MDOC_EXPLICIT	 (1 << 2)
 #define	MDOC_PROLOGUE	 (1 << 3)
-#define	MDOC_IGNDELIM	 (1 << 4) 
-	/* Reserved words in arguments treated as text. */
+#define	MDOC_IGNDELIM	 (1 << 4)
+#define	MDOC_JOIN	 (1 << 5)
 };
 
 enum	margserr {
@@ -99,13 +103,14 @@ extern	const struct mdoc_macro *const mdoc_macros;
 
 __BEGIN_DECLS
 
-#define		  mdoc_pmsg(m, l, p, t) \
-		  mandoc_msg((t), (m)->parse, (l), (p), NULL)
-#define		  mdoc_nmsg(m, n, t) \
-		  mandoc_msg((t), (m)->parse, (n)->line, (n)->pos, NULL)
+#define		  mdoc_pmsg(mdoc, l, p, t) \
+		  mandoc_msg((t), (mdoc)->parse, (l), (p), NULL)
+#define		  mdoc_nmsg(mdoc, n, t) \
+		  mandoc_msg((t), (mdoc)->parse, (n)->line, (n)->pos, NULL)
 int		  mdoc_macro(MACRO_PROT_ARGS);
 int		  mdoc_word_alloc(struct mdoc *, 
 			int, int, const char *);
+void		  mdoc_word_append(struct mdoc *, const char *);
 int		  mdoc_elem_alloc(struct mdoc *, int, int, 
 			enum mdoct, struct mdoc_arg *);
 int		  mdoc_block_alloc(struct mdoc *, int, int, 
@@ -113,10 +118,10 @@ int		  mdoc_block_alloc(struct mdoc *, int, int,
 int		  mdoc_head_alloc(struct mdoc *, int, int, enum mdoct);
 int		  mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct);
 int		  mdoc_body_alloc(struct mdoc *, int, int, enum mdoct);
-int		  mdoc_endbody_alloc(struct mdoc *m, int line, int pos,
-			enum mdoct tok, struct mdoc_node *body,
-			enum mdoc_endbody end);
+int		  mdoc_endbody_alloc(struct mdoc *, int, int, enum mdoct,
+			struct mdoc_node *, enum mdoc_endbody);
 void		  mdoc_node_delete(struct mdoc *, struct mdoc_node *);
+int		  mdoc_node_relink(struct mdoc *, struct mdoc_node *);
 void		  mdoc_hash_init(void);
 enum mdoct	  mdoc_hash_find(const char *);
 const char	 *mdoc_a2att(const char *);
diff --git a/contrib/mdocml/libroff.h b/contrib/mdocml/libroff.h
index 0bdd5a36047..5b84c5fc454 100644
--- a/contrib/mdocml/libroff.h
+++ b/contrib/mdocml/libroff.h
@@ -1,4 +1,4 @@
-/*	$Id: libroff.h,v 1.27 2011/07/25 15:37:00 kristaps Exp $ */
+/*	$Id: libroff.h,v 1.28 2013/05/31 21:37:17 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  *
@@ -31,7 +31,7 @@ struct	tbl_node {
 	int		  pos; /* invocation column */
 	int		  line; /* invocation line */
 	enum tbl_part	  part;
-	struct tbl	  opts;
+	struct tbl_opts	  opts;
 	struct tbl_row	 *first_row;
 	struct tbl_row	 *last_row;
 	struct tbl_span	 *first_span;
diff --git a/contrib/mdocml/main.c b/contrib/mdocml/main.c
index fec83fba513..7e5c7a98aef 100644
--- a/contrib/mdocml/main.c
+++ b/contrib/mdocml/main.c
@@ -1,7 +1,7 @@
-/*	$Id: main.c,v 1.165 2011/10/06 22:29:12 kristaps Exp $ */
+/*	$Id: main.c,v 1.167 2012/11/19 17:22:26 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010, 2011 Ingo Schwarze 
+ * Copyright (c) 2010, 2011, 2012 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -85,6 +85,7 @@ main(int argc, char *argv[])
 	struct curparse	 curp;
 	enum mparset	 type;
 	enum mandoclevel rc;
+	char		*defos;
 
 	progname = strrchr(argv[0], '/');
 	if (progname == NULL)
@@ -97,10 +98,24 @@ main(int argc, char *argv[])
 	type = MPARSE_AUTO;
 	curp.outtype = OUTT_ASCII;
 	curp.wlevel  = MANDOCLEVEL_FATAL;
+	defos = NULL;
 
 	/* LINTED */
-	while (-1 != (c = getopt(argc, argv, "m:O:T:VW:")))
+	while (-1 != (c = getopt(argc, argv, "I:m:O:T:VW:")))
 		switch (c) {
+		case ('I'):
+			if (strncmp(optarg, "os=", 3)) {
+				fprintf(stderr, "-I%s: Bad argument\n",
+						optarg);
+				return((int)MANDOCLEVEL_BADARG);
+			}
+			if (defos) {
+				fprintf(stderr, "-I%s: Duplicate argument\n",
+						optarg);
+				return((int)MANDOCLEVEL_BADARG);
+			}
+			defos = mandoc_strdup(optarg + 3);
+			break;
 		case ('m'):
 			if ( ! moptions(&type, optarg))
 				return((int)MANDOCLEVEL_BADARG);
@@ -125,7 +140,7 @@ main(int argc, char *argv[])
 			/* NOTREACHED */
 		}
 
-	curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp);
+	curp.mp = mparse_alloc(type, curp.wlevel, mmsg, &curp, defos);
 
 	/*
 	 * Conditionally start up the lookaside buffer before parsing.
@@ -152,6 +167,7 @@ main(int argc, char *argv[])
 		(*curp.outfree)(curp.outdata);
 	if (curp.mp)
 		mparse_free(curp.mp);
+	free(defos);
 
 	return((int)rc);
 }
@@ -170,12 +186,12 @@ usage(void)
 
 	fprintf(stderr, "usage: %s "
 			"[-V] "
-			"[-foption] "
+			"[-Ios=name] "
 			"[-mformat] "
 			"[-Ooption] "
 			"[-Toutput] "
-			"[-Wlevel] "
-			"[file...]\n", 
+			"[-Wlevel]\n"
+			"\t      [file ...]\n", 
 			progname);
 
 	exit((int)MANDOCLEVEL_BADARG);
diff --git a/contrib/mdocml/man.7 b/contrib/mdocml/man.7
index 1715a7ca119..f2f4d1d8c09 100644
--- a/contrib/mdocml/man.7
+++ b/contrib/mdocml/man.7
@@ -1,7 +1,7 @@
-.\"	$Id: man.7,v 1.113 2012/01/03 15:16:24 kristaps Exp $
+.\"	$Id: man.7,v 1.120 2013/09/16 22:58:57 schwarze Exp $
 .\"
-.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
-.\" Copyright (c) 2011 Ingo Schwarze 
+.\" Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons 
+.\" Copyright (c) 2011, 2012 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: January 3 2012 $
+.Dd $Mdocdate: September 16 2013 $
 .Dt MAN 7
 .Os
 .Sh NAME
@@ -253,6 +253,7 @@ in the alphabetical reference below.
 .It Sx IP Ta indented paragraph: Op Ar head Op Ar width
 .It Sx TP Ta tagged paragraph: Op Ar width
 .It Sx HP Ta hanged paragraph: Op Ar width
+.It Sx PD Ta set vertical paragraph distance: Op Ar height
 .It Sx \&br Ta force output line break in text mode (no arguments)
 .It Sx \&sp Ta force vertical space: Op Ar height
 .It Sx fi , nf Ta fill mode and no-fill mode (no arguments)
@@ -272,10 +273,6 @@ in the alphabetical reference below.
 .It Sx RB Ta alternate between roman and boldface fonts
 .It Sx RI Ta alternate between roman and italic fonts
 .El
-.Ss Semantic markup
-.Bl -column "PP, LP, P" description
-.It Sx OP Ta optional arguments
-.El
 .Sh MACRO REFERENCE
 This section is a canonical reference to all macros, arranged
 alphabetically.
@@ -343,6 +340,18 @@ and
 .Ss \&DT
 Has no effect.
 Included for compatibility.
+.Ss \&EE
+This is a non-standard GNU extension, included only for compatibility.
+In
+.Xr mandoc 1 ,
+it does the same as
+.Sx \&fi .
+.Ss \&EX
+This is a non-standard GNU extension, included only for compatibility.
+In
+.Xr mandoc 1 ,
+it does the same as
+.Sx \&nf .
 .Ss \&HP
 Begin a paragraph whose initial output line is left-justified, but
 subsequent output lines are indented, with the following syntax:
@@ -353,8 +362,9 @@ subsequent output lines are indented, with the following syntax:
 .Pp
 The
 .Cm width
-argument must conform to
-.Sx Scaling Widths .
+argument is a
+.Xr roff 7
+scaling width.
 If specified, it's saved for later paragraph left-margins; if unspecified, the
 saved or default width is used.
 .Pp
@@ -396,8 +406,9 @@ Begin an indented paragraph with the following syntax:
 .Pp
 The
 .Cm width
-argument defines the width of the left margin and is defined by
-.Sx Scaling Widths .
+argument is a
+.Xr roff 7
+scaling width defining the left margin.
 It's saved for later paragraph left-margins; if unspecified, the saved or
 default width is used.
 .Pp
@@ -443,7 +454,8 @@ and
 .Sx \&TP .
 .Ss \&OP
 Optional command-line argument.
-This has the following syntax:
+This is a non-standard GNU extension, included only for compatibility.
+It has the following syntax:
 .Bd -filled -offset indent
 .Pf \. Sx \&OP
 .Cm key Op Cm value
@@ -465,6 +477,36 @@ See also
 .Sx \&PP ,
 and
 .Sx \&TP .
+.Ss \&PD
+Specify the vertical space to be inserted before each new paragraph.
+.br
+The syntax is as follows:
+.Bd -filled -offset indent
+.Pf \. Sx \&PD
+.Op Cm height
+.Ed
+.Pp
+The
+.Cm height
+argument is a
+.Xr roff 7
+scaling width.
+It defaults to
+.Cm 1v .
+If the unit is omitted,
+.Cm v
+is assumed.
+.Pp
+This macro affects the spacing before any subsequent instances of
+.Sx \&HP ,
+.Sx \&IP ,
+.Sx \&LP ,
+.Sx \&P ,
+.Sx \&PP ,
+.Sx \&SH ,
+.Sx \&SS ,
+and
+.Sx \&TP .
 .Ss \&PP
 Synonym for
 .Sx \&LP .
@@ -529,8 +571,9 @@ This has the following syntax:
 .Pp
 The
 .Cm width
-argument must conform to
-.Sx Scaling Widths .
+argument is a
+.Xr roff 7
+scaling width.
 If not specified, the saved or default width is used.
 .Pp
 See also
@@ -595,8 +638,9 @@ The syntax is as follows:
 .Pp
 The
 .Cm width
-argument must conform to
-.Sx Scaling Widths .
+argument is a
+.Xr roff 7
+scaling width.
 If specified, it's saved for later paragraph left-margins; if
 unspecified, the saved or default width is used.
 .Pp
@@ -609,7 +653,8 @@ and
 .Sx \&PP .
 .Ss \&UC
 Sets the volume for the footer for compatibility with man pages from
-BSD releases.
+.Bx
+releases.
 The optional first argument specifies which release it is from.
 .Ss \&br
 Breaks the current line.
@@ -653,10 +698,10 @@ Insert vertical spaces into output with the following syntax:
 .Op Cm height
 .Ed
 .Pp
-Insert
+The
 .Cm height
-spaces, which must conform to
-.Sx Scaling Widths .
+argument is a scaling width as described in
+.Xr roff 7 .
 If 0, this is equivalent to the
 .Sx \&br
 macro.
@@ -904,8 +949,7 @@ utility written by Kristaps Dzonsons appeared in
 This
 .Nm
 reference was written by
-.An Kristaps Dzonsons ,
-.Mt kristaps@bsd.lv .
+.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
 .Sh CAVEATS
 Do not use this language.
 Use
diff --git a/contrib/mdocml/man.c b/contrib/mdocml/man.c
index 1bea5610e3d..e6e1c289920 100644
--- a/contrib/mdocml/man.c
+++ b/contrib/mdocml/man.c
@@ -1,4 +1,4 @@
-/*	$Id: man.c,v 1.115 2012/01/03 15:16:24 kristaps Exp $ */
+/*	$Id: man.c,v 1.121 2013/11/10 22:54:40 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  *
@@ -40,7 +40,8 @@ const	char *const __man_macronames[MAN_MAX] = {
 	"RI",		"na",		"sp",		"nf",
 	"fi",		"RE",		"RS",		"DT",
 	"UC",		"PD",		"AT",		"in",
-	"ft",		"OP"
+	"ft",		"OP",		"EX",		"EE",
+	"UR",		"UE"
 	};
 
 const	char * const *man_macronames = __man_macronames;
@@ -60,20 +61,20 @@ static	int		 man_descope(struct man *, int, int);
 
 
 const struct man_node *
-man_node(const struct man *m)
+man_node(const struct man *man)
 {
 
-	assert( ! (MAN_HALT & m->flags));
-	return(m->first);
+	assert( ! (MAN_HALT & man->flags));
+	return(man->first);
 }
 
 
 const struct man_meta *
-man_meta(const struct man *m)
+man_meta(const struct man *man)
 {
 
-	assert( ! (MAN_HALT & m->flags));
-	return(&m->meta);
+	assert( ! (MAN_HALT & man->flags));
+	return(&man->meta);
 }
 
 
@@ -112,28 +113,28 @@ man_alloc(struct roff *roff, struct mparse *parse)
 
 
 int
-man_endparse(struct man *m)
+man_endparse(struct man *man)
 {
 
-	assert( ! (MAN_HALT & m->flags));
-	if (man_macroend(m))
+	assert( ! (MAN_HALT & man->flags));
+	if (man_macroend(man))
 		return(1);
-	m->flags |= MAN_HALT;
+	man->flags |= MAN_HALT;
 	return(0);
 }
 
 
 int
-man_parseln(struct man *m, int ln, char *buf, int offs)
+man_parseln(struct man *man, int ln, char *buf, int offs)
 {
 
-	m->flags |= MAN_NEWLINE;
+	man->flags |= MAN_NEWLINE;
 
-	assert( ! (MAN_HALT & m->flags));
+	assert( ! (MAN_HALT & man->flags));
 
-	return (mandoc_getcontrol(buf, &offs) ?
-			man_pmacro(m, ln, buf, offs) : 
-			man_ptext(m, ln, buf, offs));
+	return (roff_getcontrol(man->roff, buf, &offs) ?
+			man_pmacro(man, ln, buf, offs) : 
+			man_ptext(man, ln, buf, offs));
 }
 
 
@@ -157,16 +158,16 @@ man_free1(struct man *man)
 
 
 static void
-man_alloc1(struct man *m)
+man_alloc1(struct man *man)
 {
 
-	memset(&m->meta, 0, sizeof(struct man_meta));
-	m->flags = 0;
-	m->last = mandoc_calloc(1, sizeof(struct man_node));
-	m->first = m->last;
-	m->last->type = MAN_ROOT;
-	m->last->tok = MAN_MAX;
-	m->next = MAN_NEXT_CHILD;
+	memset(&man->meta, 0, sizeof(struct man_meta));
+	man->flags = 0;
+	man->last = mandoc_calloc(1, sizeof(struct man_node));
+	man->first = man->last;
+	man->last->type = MAN_ROOT;
+	man->last->tok = MAN_MAX;
+	man->next = MAN_NEXT_CHILD;
 }
 
 
@@ -234,7 +235,7 @@ man_node_append(struct man *man, struct man_node *p)
 
 
 static struct man_node *
-man_node_alloc(struct man *m, int line, int pos, 
+man_node_alloc(struct man *man, int line, int pos, 
 		enum man_type type, enum mant tok)
 {
 	struct man_node *p;
@@ -245,89 +246,89 @@ man_node_alloc(struct man *m, int line, int pos,
 	p->type = type;
 	p->tok = tok;
 
-	if (MAN_NEWLINE & m->flags)
+	if (MAN_NEWLINE & man->flags)
 		p->flags |= MAN_LINE;
-	m->flags &= ~MAN_NEWLINE;
+	man->flags &= ~MAN_NEWLINE;
 	return(p);
 }
 
 
 int
-man_elem_alloc(struct man *m, int line, int pos, enum mant tok)
+man_elem_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
-	p = man_node_alloc(m, line, pos, MAN_ELEM, tok);
-	if ( ! man_node_append(m, p))
+	p = man_node_alloc(man, line, pos, MAN_ELEM, tok);
+	if ( ! man_node_append(man, p))
 		return(0);
-	m->next = MAN_NEXT_CHILD;
+	man->next = MAN_NEXT_CHILD;
 	return(1);
 }
 
 
 int
-man_tail_alloc(struct man *m, int line, int pos, enum mant tok)
+man_tail_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
-	p = man_node_alloc(m, line, pos, MAN_TAIL, tok);
-	if ( ! man_node_append(m, p))
+	p = man_node_alloc(man, line, pos, MAN_TAIL, tok);
+	if ( ! man_node_append(man, p))
 		return(0);
-	m->next = MAN_NEXT_CHILD;
+	man->next = MAN_NEXT_CHILD;
 	return(1);
 }
 
 
 int
-man_head_alloc(struct man *m, int line, int pos, enum mant tok)
+man_head_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
-	p = man_node_alloc(m, line, pos, MAN_HEAD, tok);
-	if ( ! man_node_append(m, p))
+	p = man_node_alloc(man, line, pos, MAN_HEAD, tok);
+	if ( ! man_node_append(man, p))
 		return(0);
-	m->next = MAN_NEXT_CHILD;
+	man->next = MAN_NEXT_CHILD;
 	return(1);
 }
 
 
 int
-man_body_alloc(struct man *m, int line, int pos, enum mant tok)
+man_body_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
-	p = man_node_alloc(m, line, pos, MAN_BODY, tok);
-	if ( ! man_node_append(m, p))
+	p = man_node_alloc(man, line, pos, MAN_BODY, tok);
+	if ( ! man_node_append(man, p))
 		return(0);
-	m->next = MAN_NEXT_CHILD;
+	man->next = MAN_NEXT_CHILD;
 	return(1);
 }
 
 
 int
-man_block_alloc(struct man *m, int line, int pos, enum mant tok)
+man_block_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
-	p = man_node_alloc(m, line, pos, MAN_BLOCK, tok);
-	if ( ! man_node_append(m, p))
+	p = man_node_alloc(man, line, pos, MAN_BLOCK, tok);
+	if ( ! man_node_append(man, p))
 		return(0);
-	m->next = MAN_NEXT_CHILD;
+	man->next = MAN_NEXT_CHILD;
 	return(1);
 }
 
 int
-man_word_alloc(struct man *m, int line, int pos, const char *word)
+man_word_alloc(struct man *man, int line, int pos, const char *word)
 {
 	struct man_node	*n;
 
-	n = man_node_alloc(m, line, pos, MAN_TEXT, MAN_MAX);
-	n->string = roff_strdup(m->roff, word);
+	n = man_node_alloc(man, line, pos, MAN_TEXT, MAN_MAX);
+	n->string = roff_strdup(man->roff, word);
 
-	if ( ! man_node_append(m, n))
+	if ( ! man_node_append(man, n))
 		return(0);
 
-	m->next = MAN_NEXT_SIBLING;
+	man->next = MAN_NEXT_SIBLING;
 	return(1);
 }
 
@@ -347,52 +348,52 @@ man_node_free(struct man_node *p)
 
 
 void
-man_node_delete(struct man *m, struct man_node *p)
+man_node_delete(struct man *man, struct man_node *p)
 {
 
 	while (p->child)
-		man_node_delete(m, p->child);
+		man_node_delete(man, p->child);
 
-	man_node_unlink(m, p);
+	man_node_unlink(man, p);
 	man_node_free(p);
 }
 
 int
-man_addeqn(struct man *m, const struct eqn *ep)
+man_addeqn(struct man *man, const struct eqn *ep)
 {
 	struct man_node	*n;
 
-	assert( ! (MAN_HALT & m->flags));
+	assert( ! (MAN_HALT & man->flags));
 
-	n = man_node_alloc(m, ep->ln, ep->pos, MAN_EQN, MAN_MAX);
+	n = man_node_alloc(man, ep->ln, ep->pos, MAN_EQN, MAN_MAX);
 	n->eqn = ep;
 
-	if ( ! man_node_append(m, n))
+	if ( ! man_node_append(man, n))
 		return(0);
 
-	m->next = MAN_NEXT_SIBLING;
-	return(man_descope(m, ep->ln, ep->pos));
+	man->next = MAN_NEXT_SIBLING;
+	return(man_descope(man, ep->ln, ep->pos));
 }
 
 int
-man_addspan(struct man *m, const struct tbl_span *sp)
+man_addspan(struct man *man, const struct tbl_span *sp)
 {
 	struct man_node	*n;
 
-	assert( ! (MAN_HALT & m->flags));
+	assert( ! (MAN_HALT & man->flags));
 
-	n = man_node_alloc(m, sp->line, 0, MAN_TBL, MAN_MAX);
+	n = man_node_alloc(man, sp->line, 0, MAN_TBL, MAN_MAX);
 	n->span = sp;
 
-	if ( ! man_node_append(m, n))
+	if ( ! man_node_append(man, n))
 		return(0);
 
-	m->next = MAN_NEXT_SIBLING;
-	return(man_descope(m, sp->line, 0));
+	man->next = MAN_NEXT_SIBLING;
+	return(man_descope(man, sp->line, 0));
 }
 
 static int
-man_descope(struct man *m, int line, int offs)
+man_descope(struct man *man, int line, int offs)
 {
 	/*
 	 * Co-ordinate what happens with having a next-line scope open:
@@ -400,44 +401,51 @@ man_descope(struct man *m, int line, int offs)
 	 * out the block scope (also if applicable).
 	 */
 
-	if (MAN_ELINE & m->flags) {
-		m->flags &= ~MAN_ELINE;
-		if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
+	if (MAN_ELINE & man->flags) {
+		man->flags &= ~MAN_ELINE;
+		if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
 			return(0);
 	}
 
-	if ( ! (MAN_BLINE & m->flags))
+	if ( ! (MAN_BLINE & man->flags))
 		return(1);
-	m->flags &= ~MAN_BLINE;
+	man->flags &= ~MAN_BLINE;
 
-	if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
+	if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
 		return(0);
-	return(man_body_alloc(m, line, offs, m->last->tok));
+	return(man_body_alloc(man, line, offs, man->last->tok));
 }
 
 static int
-man_ptext(struct man *m, int line, char *buf, int offs)
+man_ptext(struct man *man, int line, char *buf, int offs)
 {
 	int		 i;
 
 	/* Literal free-form text whitespace is preserved. */
 
-	if (MAN_LITERAL & m->flags) {
-		if ( ! man_word_alloc(m, line, offs, buf + offs))
+	if (MAN_LITERAL & man->flags) {
+		if ( ! man_word_alloc(man, line, offs, buf + offs))
 			return(0);
-		return(man_descope(m, line, offs));
+		return(man_descope(man, line, offs));
 	}
 
-	/* Pump blank lines directly into the backend. */
-
 	for (i = offs; ' ' == buf[i]; i++)
 		/* Skip leading whitespace. */ ;
 
+	/*
+	 * Blank lines are ignored right after headings
+	 * but add a single vertical space elsewhere.
+	 */
+
 	if ('\0' == buf[i]) {
 		/* Allocate a blank entry. */
-		if ( ! man_word_alloc(m, line, offs, ""))
-			return(0);
-		return(man_descope(m, line, offs));
+		if (MAN_SH != man->last->tok &&
+		    MAN_SS != man->last->tok) {
+			if ( ! man_elem_alloc(man, line, offs, MAN_sp))
+				return(0);
+			man->next = MAN_NEXT_SIBLING;
+		}
+		return(1);
 	}
 
 	/* 
@@ -450,7 +458,7 @@ man_ptext(struct man *m, int line, char *buf, int offs)
 
 	if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
 		if (i > 1 && '\\' != buf[i - 2])
-			man_pmsg(m, line, i - 1, MANDOCERR_EOLNSPACE);
+			man_pmsg(man, line, i - 1, MANDOCERR_EOLNSPACE);
 
 		for (--i; i && ' ' == buf[i]; i--)
 			/* Spin back to non-space. */ ;
@@ -461,7 +469,7 @@ man_ptext(struct man *m, int line, char *buf, int offs)
 		buf[i] = '\0';
 	}
 
-	if ( ! man_word_alloc(m, line, offs, buf + offs))
+	if ( ! man_word_alloc(man, line, offs, buf + offs))
 		return(0);
 
 	/*
@@ -472,13 +480,13 @@ man_ptext(struct man *m, int line, char *buf, int offs)
 
 	assert(i);
 	if (mandoc_eos(buf, (size_t)i, 0))
-		m->last->flags |= MAN_EOS;
+		man->last->flags |= MAN_EOS;
 
-	return(man_descope(m, line, offs));
+	return(man_descope(man, line, offs));
 }
 
 static int
-man_pmacro(struct man *m, int ln, char *buf, int offs)
+man_pmacro(struct man *man, int ln, char *buf, int offs)
 {
 	int		 i, ppos;
 	enum mant	 tok;
@@ -486,7 +494,7 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 	struct man_node	*n;
 
 	if ('"' == buf[offs]) {
-		man_pmsg(m, ln, offs, MANDOCERR_BADCOMMENT);
+		man_pmsg(man, ln, offs, MANDOCERR_BADCOMMENT);
 		return(1);
 	} else if ('\0' == buf[offs])
 		return(1);
@@ -508,7 +516,7 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 	tok = (i > 0 && i < 4) ? man_hash_find(mac) : MAN_MAX;
 
 	if (MAN_MAX == tok) {
-		mandoc_vmsg(MANDOCERR_MACRO, m->parse, ln, 
+		mandoc_vmsg(MANDOCERR_MACRO, man->parse, ln, 
 				ppos, "%s", buf + ppos - 1);
 		return(1);
 	}
@@ -524,7 +532,7 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 	 */
 
 	if ('\0' == buf[offs] && ' ' == buf[offs - 1])
-		man_pmsg(m, ln, offs - 1, MANDOCERR_EOLNSPACE);
+		man_pmsg(man, ln, offs - 1, MANDOCERR_EOLNSPACE);
 
 	/* 
 	 * Remove prior ELINE macro, as it's being clobbered by a new
@@ -533,8 +541,8 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 	 */
 
 	if ( ! (MAN_NSCOPED & man_macros[tok].flags) &&
-			m->flags & MAN_ELINE) {
-		n = m->last;
+			man->flags & MAN_ELINE) {
+		n = man->last;
 		assert(MAN_TEXT != n->type);
 
 		/* Remove repeated NSCOPED macros causing ELINE. */
@@ -542,20 +550,20 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 		if (MAN_NSCOPED & man_macros[n->tok].flags)
 			n = n->parent;
 
-		mandoc_vmsg(MANDOCERR_LINESCOPE, m->parse, n->line, 
+		mandoc_vmsg(MANDOCERR_LINESCOPE, man->parse, n->line, 
 		    n->pos, "%s breaks %s", man_macronames[tok],
 		    man_macronames[n->tok]);
 
-		man_node_delete(m, n);
-		m->flags &= ~MAN_ELINE;
+		man_node_delete(man, n);
+		man->flags &= ~MAN_ELINE;
 	}
 
 	/*
 	 * Remove prior BLINE macro that is being clobbered.
 	 */
-	if ((m->flags & MAN_BLINE) &&
+	if ((man->flags & MAN_BLINE) &&
 	    (MAN_BSCOPE & man_macros[tok].flags)) {
-		n = m->last;
+		n = man->last;
 
 		/* Might be a text node like 8 in
 		 * .TP 8
@@ -573,12 +581,12 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 		assert(MAN_BLOCK == n->type);
 		assert(MAN_SCOPED & man_macros[n->tok].flags);
 
-		mandoc_vmsg(MANDOCERR_LINESCOPE, m->parse, n->line, 
+		mandoc_vmsg(MANDOCERR_LINESCOPE, man->parse, n->line, 
 		    n->pos, "%s breaks %s", man_macronames[tok],
 		    man_macronames[n->tok]);
 
-		man_node_delete(m, n);
-		m->flags &= ~MAN_BLINE;
+		man_node_delete(man, n);
+		man->flags &= ~MAN_BLINE;
 	}
 
 	/*
@@ -587,13 +595,13 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 	 * when they exit.
 	 */
 
-	if (MAN_BLINE & m->flags)
-		m->flags |= MAN_BPLINE;
+	if (MAN_BLINE & man->flags)
+		man->flags |= MAN_BPLINE;
 
 	/* Call to handler... */
 
 	assert(man_macros[tok].fp);
-	if ( ! (*man_macros[tok].fp)(m, tok, ln, ppos, &offs, buf))
+	if ( ! (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf))
 		goto err;
 
 	/* 
@@ -601,19 +609,19 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 	 * above-parsed macro, so return.
 	 */
 
-	if ( ! (MAN_BPLINE & m->flags)) {
-		m->flags &= ~MAN_ILINE; 
+	if ( ! (MAN_BPLINE & man->flags)) {
+		man->flags &= ~MAN_ILINE; 
 		return(1);
 	}
-	m->flags &= ~MAN_BPLINE;
+	man->flags &= ~MAN_BPLINE;
 
 	/*
 	 * If we're in a block scope, then allow this macro to slip by
 	 * without closing scope around it.
 	 */
 
-	if (MAN_ILINE & m->flags) {
-		m->flags &= ~MAN_ILINE;
+	if (MAN_ILINE & man->flags) {
+		man->flags &= ~MAN_ILINE;
 		return(1);
 	}
 
@@ -622,30 +630,30 @@ man_pmacro(struct man *m, int ln, char *buf, int offs)
 	 * now, as the next line will close out the block scope.
 	 */
 
-	if (MAN_ELINE & m->flags)
+	if (MAN_ELINE & man->flags)
 		return(1);
 
 	/* Close out the block scope opened in the prior line.  */
 
-	assert(MAN_BLINE & m->flags);
-	m->flags &= ~MAN_BLINE;
+	assert(MAN_BLINE & man->flags);
+	man->flags &= ~MAN_BLINE;
 
-	if ( ! man_unscope(m, m->last->parent, MANDOCERR_MAX))
+	if ( ! man_unscope(man, man->last->parent, MANDOCERR_MAX))
 		return(0);
-	return(man_body_alloc(m, ln, ppos, m->last->tok));
+	return(man_body_alloc(man, ln, ppos, man->last->tok));
 
 err:	/* Error out. */
 
-	m->flags |= MAN_HALT;
+	man->flags |= MAN_HALT;
 	return(0);
 }
 
 /*
- * Unlink a node from its context.  If "m" is provided, the last parse
+ * Unlink a node from its context.  If "man" is provided, the last parse
  * point will also be adjusted accordingly.
  */
 static void
-man_node_unlink(struct man *m, struct man_node *n)
+man_node_unlink(struct man *man, struct man_node *n)
 {
 
 	/* Adjust siblings. */
@@ -665,26 +673,26 @@ man_node_unlink(struct man *m, struct man_node *n)
 
 	/* Adjust parse point, if applicable. */
 
-	if (m && m->last == n) {
+	if (man && man->last == n) {
 		/*XXX: this can occur when bailing from validation. */
 		/*assert(NULL == n->next);*/
 		if (n->prev) {
-			m->last = n->prev;
-			m->next = MAN_NEXT_SIBLING;
+			man->last = n->prev;
+			man->next = MAN_NEXT_SIBLING;
 		} else {
-			m->last = n->parent;
-			m->next = MAN_NEXT_CHILD;
+			man->last = n->parent;
+			man->next = MAN_NEXT_CHILD;
 		}
 	}
 
-	if (m && m->first == n)
-		m->first = NULL;
+	if (man && man->first == n)
+		man->first = NULL;
 }
 
 const struct mparse *
-man_mparse(const struct man *m)
+man_mparse(const struct man *man)
 {
 
-	assert(m && m->parse);
-	return(m->parse);
+	assert(man && man->parse);
+	return(man->parse);
 }
diff --git a/contrib/mdocml/man.h b/contrib/mdocml/man.h
index 4fc3934e6f6..ef9480f2768 100644
--- a/contrib/mdocml/man.h
+++ b/contrib/mdocml/man.h
@@ -1,4 +1,4 @@
-/*	$Id: man.h,v 1.60 2012/01/03 15:16:24 kristaps Exp $ */
+/*	$Id: man.h,v 1.62 2013/10/17 20:54:58 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  *
@@ -52,6 +52,10 @@ enum	mant {
 	MAN_in,
 	MAN_ft,
 	MAN_OP,
+	MAN_EX,
+	MAN_EE,
+	MAN_UR,
+	MAN_UE,
 	MAN_MAX
 };
 
diff --git a/contrib/mdocml/man_html.c b/contrib/mdocml/man_html.c
index a76ea2d7070..2c4e220a118 100644
--- a/contrib/mdocml/man_html.c
+++ b/contrib/mdocml/man_html.c
@@ -1,6 +1,7 @@
-/*	$Id: man_html.c,v 1.86 2012/01/03 15:16:24 kristaps Exp $ */
+/*	$Id: man_html.c,v 1.90 2013/10/17 20:54:58 schwarze Exp $ */
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2008-2012 Kristaps Dzonsons 
+ * Copyright (c) 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -37,7 +38,7 @@
 
 #define	INDENT		  5
 
-#define	MAN_ARGS	  const struct man_meta *m, \
+#define	MAN_ARGS	  const struct man_meta *man, \
 			  const struct man_node *n, \
 			  struct mhtml *mh, \
 			  struct html *h
@@ -70,6 +71,7 @@ static	int		  man_RS_pre(MAN_ARGS);
 static	int		  man_SH_pre(MAN_ARGS);
 static	int		  man_SM_pre(MAN_ARGS);
 static	int		  man_SS_pre(MAN_ARGS);
+static	int		  man_UR_pre(MAN_ARGS);
 static	int		  man_alt_pre(MAN_ARGS);
 static	int		  man_br_pre(MAN_ARGS);
 static	int		  man_ign_pre(MAN_ARGS);
@@ -113,6 +115,10 @@ static	const struct htmlman mans[MAN_MAX] = {
 	{ man_in_pre, NULL }, /* in */
 	{ man_ign_pre, NULL }, /* ft */
 	{ man_OP_pre, NULL }, /* OP */
+	{ man_literal_pre, NULL }, /* EX */
+	{ man_literal_pre, NULL }, /* EE */
+	{ man_UR_pre, NULL }, /* UR */
+	{ NULL, NULL }, /* UE */
 };
 
 /*
@@ -139,12 +145,12 @@ print_bvspace(struct html *h, const struct man_node *n)
 }
 
 void
-html_man(void *arg, const struct man *m)
+html_man(void *arg, const struct man *man)
 {
 	struct mhtml	 mh;
 
 	memset(&mh, 0, sizeof(struct mhtml));
-	print_man(man_meta(m), man_node(m), &mh, (struct html *)arg);
+	print_man(man_meta(man), man_node(man), &mh, (struct html *)arg);
 	putchar('\n');
 }
 
@@ -160,14 +166,14 @@ print_man(MAN_ARGS)
 		print_gen_decls(h);
 		t = print_otag(h, TAG_HTML, 0, NULL);
 		tt = print_otag(h, TAG_HEAD, 0, NULL);
-		print_man_head(m, n, mh, h);
+		print_man_head(man, n, mh, h);
 		print_tagq(h, tt);
 		print_otag(h, TAG_BODY, 0, NULL);
 		print_otag(h, TAG_DIV, 1, &tag);
 	} else 
 		t = print_otag(h, TAG_DIV, 1, &tag);
 
-	print_man_nodelist(m, n, mh, h);
+	print_man_nodelist(man, n, mh, h);
 	print_tagq(h, t);
 }
 
@@ -178,9 +184,9 @@ print_man_head(MAN_ARGS)
 {
 
 	print_gen_head(h);
-	assert(m->title);
-	assert(m->msec);
-	bufcat_fmt(h, "%s(%s)", m->title, m->msec);
+	assert(man->title);
+	assert(man->msec);
+	bufcat_fmt(h, "%s(%s)", man->title, man->msec);
 	print_otag(h, TAG_TITLE, 0, NULL);
 	print_text(h, h->buf);
 }
@@ -190,9 +196,9 @@ static void
 print_man_nodelist(MAN_ARGS)
 {
 
-	print_man_node(m, n, mh, h);
+	print_man_node(man, n, mh, h);
 	if (n->next)
-		print_man_nodelist(m, n->next, mh, h);
+		print_man_nodelist(man, n->next, mh, h);
 }
 
 
@@ -207,7 +213,7 @@ print_man_node(MAN_ARGS)
 
 	switch (n->type) {
 	case (MAN_ROOT):
-		man_root_pre(m, n, mh, h);
+		man_root_pre(man, n, mh, h);
 		break;
 	case (MAN_TEXT):
 		/*
@@ -258,25 +264,25 @@ print_man_node(MAN_ARGS)
 			t = h->tags.head;
 		}
 		if (mans[n->tok].pre)
-			child = (*mans[n->tok].pre)(m, n, mh, h);
+			child = (*mans[n->tok].pre)(man, n, mh, h);
 		break;
 	}
 
 	if (child && n->child)
-		print_man_nodelist(m, n->child, mh, h);
+		print_man_nodelist(man, n->child, mh, h);
 
 	/* This will automatically close out any font scope. */
 	print_stagq(h, t);
 
 	switch (n->type) {
 	case (MAN_ROOT):
-		man_root_post(m, n, mh, h);
+		man_root_post(man, n, mh, h);
 		break;
 	case (MAN_EQN):
 		break;
 	default:
 		if (mans[n->tok].post)
-			(*mans[n->tok].post)(m, n, mh, h);
+			(*mans[n->tok].post)(man, n, mh, h);
 		break;
 	}
 }
@@ -304,12 +310,12 @@ man_root_pre(MAN_ARGS)
 	char		 b[BUFSIZ], title[BUFSIZ];
 
 	b[0] = 0;
-	if (m->vol)
-		(void)strlcat(b, m->vol, BUFSIZ);
+	if (man->vol)
+		(void)strlcat(b, man->vol, BUFSIZ);
 
-	assert(m->title);
-	assert(m->msec);
-	snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
+	assert(man->title);
+	assert(man->msec);
+	snprintf(title, BUFSIZ - 1, "%s(%s)", man->title, man->msec);
 
 	PAIR_SUMMARY_INIT(&tag[0], "Document Header");
 	PAIR_CLASS_INIT(&tag[1], "head");
@@ -363,16 +369,16 @@ man_root_post(MAN_ARGS)
 	PAIR_CLASS_INIT(&tag[0], "foot-date");
 	print_otag(h, TAG_TD, 1, tag);
 
-	assert(m->date);
-	print_text(h, m->date);
+	assert(man->date);
+	print_text(h, man->date);
 	print_stagq(h, tt);
 
 	PAIR_CLASS_INIT(&tag[0], "foot-os");
 	PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
 	print_otag(h, TAG_TD, 2, tag);
 
-	if (m->source)
-		print_text(h, m->source);
+	if (man->source)
+		print_text(h, man->source);
 	print_tagq(h, t);
 }
 
@@ -468,7 +474,7 @@ man_alt_pre(MAN_ARGS)
 		if (TAG_MAX != fp)
 			t = print_otag(h, fp, 0, NULL);
 
-		print_man_node(m, nn, mh, h);
+		print_man_node(man, nn, mh, h);
 
 		if (t)
 			print_tagq(h, t);
@@ -543,14 +549,14 @@ man_IP_pre(MAN_ARGS)
 	/* For IP, only print the first header element. */
 
 	if (MAN_IP == n->tok && n->child)
-		print_man_node(m, n->child, mh, h);
+		print_man_node(man, n->child, mh, h);
 
 	/* For TP, only print next-line header elements. */
 
 	if (MAN_TP == n->tok)
 		for (nn = n->child; nn; nn = nn->next)
 			if (nn->line > n->line)
-				print_man_node(m, nn, mh, h);
+				print_man_node(man, nn, mh, h);
 
 	return(0);
 }
@@ -638,7 +644,7 @@ static int
 man_literal_pre(MAN_ARGS)
 {
 
-	if (MAN_nf != n->tok) {
+	if (MAN_fi == n->tok || MAN_EE == n->tok) {
 		print_otag(h, TAG_BR, 0, NULL);
 		mh->fl &= ~MANH_LITERAL;
 	} else
@@ -686,3 +692,27 @@ man_RS_pre(MAN_ARGS)
 	print_otag(h, TAG_DIV, 1, &tag);
 	return(1);
 }
+
+/* ARGSUSED */
+static int
+man_UR_pre(MAN_ARGS)
+{
+	struct htmlpair		 tag[2];
+
+	n = n->child;
+	assert(MAN_HEAD == n->type);
+	if (n->nchild) {
+		assert(MAN_TEXT == n->child->type);
+		PAIR_CLASS_INIT(&tag[0], "link-ext");
+		PAIR_HREF_INIT(&tag[1], n->child->string);
+		print_otag(h, TAG_A, 2, tag);
+	}
+
+	assert(MAN_BODY == n->next->type);
+	if (n->next->nchild)
+		n = n->next;
+
+	print_man_nodelist(man, n->child, mh, h);
+
+	return(0);
+}
diff --git a/contrib/mdocml/man_macro.c b/contrib/mdocml/man_macro.c
index 4bbbc4fa7f1..479d0484c41 100644
--- a/contrib/mdocml/man_macro.c
+++ b/contrib/mdocml/man_macro.c
@@ -1,6 +1,8 @@
-/*	$Id: man_macro.c,v 1.71 2012/01/03 15:16:24 kristaps Exp $ */
+/*	$Id: man_macro.c,v 1.79 2013/12/25 00:50:05 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2012, 2013 Ingo Schwarze 
+ * Copyright (c) 2013 Franco Fichtner 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -77,7 +79,7 @@ const	struct man_macro __man_macros[MAN_MAX] = {
 	{ in_line_eoln, MAN_BSCOPE }, /* nf */
 	{ in_line_eoln, MAN_BSCOPE }, /* fi */
 	{ blk_close, 0 }, /* RE */
-	{ blk_exp, MAN_EXPLICIT }, /* RS */
+	{ blk_exp, MAN_BSCOPE | MAN_EXPLICIT }, /* RS */
 	{ in_line_eoln, 0 }, /* DT */
 	{ in_line_eoln, 0 }, /* UC */
 	{ in_line_eoln, 0 }, /* PD */
@@ -85,6 +87,10 @@ const	struct man_macro __man_macros[MAN_MAX] = {
 	{ in_line_eoln, 0 }, /* in */
 	{ in_line_eoln, 0 }, /* ft */
 	{ in_line_eoln, 0 }, /* OP */
+	{ in_line_eoln, MAN_BSCOPE }, /* EX */
+	{ in_line_eoln, MAN_BSCOPE }, /* EE */
+	{ blk_exp, MAN_BSCOPE | MAN_EXPLICIT }, /* UR */
+	{ blk_close, 0 }, /* UE */
 };
 
 const	struct man_macro * const man_macros = __man_macros;
@@ -94,7 +100,7 @@ const	struct man_macro * const man_macros = __man_macros;
  * Warn when "n" is an explicit non-roff macro.
  */
 static void
-rew_warn(struct man *m, struct man_node *n, enum mandocerr er)
+rew_warn(struct man *man, struct man_node *n, enum mandocerr er)
 {
 
 	if (er == MANDOCERR_MAX || MAN_BLOCK != n->type)
@@ -105,7 +111,7 @@ rew_warn(struct man *m, struct man_node *n, enum mandocerr er)
 		return;
 
 	assert(er < MANDOCERR_FATAL);
-	man_nmsg(m, n, er);
+	man_nmsg(man, n, er);
 }
 
 
@@ -114,33 +120,33 @@ rew_warn(struct man *m, struct man_node *n, enum mandocerr er)
  * will be used if an explicit block scope is being closed out.
  */
 int
-man_unscope(struct man *m, const struct man_node *to, 
+man_unscope(struct man *man, const struct man_node *to, 
 		enum mandocerr er)
 {
 	struct man_node	*n;
 
 	assert(to);
 
-	m->next = MAN_NEXT_SIBLING;
+	man->next = MAN_NEXT_SIBLING;
 
 	/* LINTED */
-	while (m->last != to) {
+	while (man->last != to) {
 		/*
 		 * Save the parent here, because we may delete the
-		 * m->last node in the post-validation phase and reset
-		 * it to m->last->parent, causing a step in the closing
+		 * man->last node in the post-validation phase and reset
+		 * it to man->last->parent, causing a step in the closing
 		 * out to be lost.
 		 */
-		n = m->last->parent;
-		rew_warn(m, m->last, er);
-		if ( ! man_valid_post(m))
+		n = man->last->parent;
+		rew_warn(man, man->last, er);
+		if ( ! man_valid_post(man))
 			return(0);
-		m->last = n;
-		assert(m->last);
+		man->last = n;
+		assert(man->last);
 	}
 
-	rew_warn(m, m->last, er);
-	if ( ! man_valid_post(m))
+	rew_warn(man, man->last, er);
+	if ( ! man_valid_post(man))
 		return(0);
 
 	return(1);
@@ -183,8 +189,12 @@ rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
 		return(REW_NOHALT);
 
 	/* First: rewind to ourselves. */
-	if (type == n->type && tok == n->tok)
-		return(REW_REWIND);
+	if (type == n->type && tok == n->tok) {
+		if (MAN_EXPLICIT & man_macros[n->tok].flags)
+			return(REW_HALT);
+		else
+			return(REW_REWIND);
+	}
 
 	/* 
 	 * Next follow the implicit scope-smashings as defined by man.7:
@@ -200,6 +210,10 @@ rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
 			return(c);
 		break;
 	case (MAN_RS):
+		/* Preserve empty paragraphs before RS. */
+		if (0 == n->nchild && (MAN_P == n->tok ||
+		    MAN_PP == n->tok || MAN_LP == n->tok))
+			return(REW_HALT);
 		/* Rewind to a subsection, if a block. */
 		if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
 			return(c);
@@ -230,13 +244,13 @@ rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
  * scopes.  When a scope is closed, it must be validated and actioned.
  */
 static int
-rew_scope(enum man_type type, struct man *m, enum mant tok)
+rew_scope(enum man_type type, struct man *man, enum mant tok)
 {
 	struct man_node	*n;
 	enum rew	 c;
 
 	/* LINTED */
-	for (n = m->last; n; n = n->parent) {
+	for (n = man->last; n; n = n->parent) {
 		/* 
 		 * Whether we should stop immediately (REW_HALT), stop
 		 * and rewind until this point (REW_REWIND), or keep
@@ -255,7 +269,7 @@ rew_scope(enum man_type type, struct man *m, enum mant tok)
 	 */
 	assert(n);
 
-	return(man_unscope(m, n, MANDOCERR_MAX));
+	return(man_unscope(man, n, MANDOCERR_MAX));
 }
 
 
@@ -273,22 +287,24 @@ blk_close(MACRO_PROT_ARGS)
 	case (MAN_RE):
 		ntok = MAN_RS;
 		break;
+	case (MAN_UE):
+		ntok = MAN_UR;
+		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
-	for (nn = m->last->parent; nn; nn = nn->parent)
-		if (ntok == nn->tok)
+	for (nn = man->last->parent; nn; nn = nn->parent)
+		if (ntok == nn->tok && MAN_BLOCK == nn->type)
 			break;
 
-	if (NULL == nn)
-		man_pmsg(m, line, ppos, MANDOCERR_NOSCOPE);
-
-	if ( ! rew_scope(MAN_BODY, m, ntok))
-		return(0);
-	if ( ! rew_scope(MAN_BLOCK, m, ntok))
-		return(0);
+	if (NULL == nn) {
+		man_pmsg(man, line, ppos, MANDOCERR_NOSCOPE);
+		if ( ! rew_scope(MAN_BLOCK, man, MAN_PP))
+			return(0);
+	} else 
+		man_unscope(man, nn, MANDOCERR_MAX);
 
 	return(1);
 }
@@ -298,34 +314,40 @@ blk_close(MACRO_PROT_ARGS)
 int
 blk_exp(MACRO_PROT_ARGS)
 {
+	struct man_node	*n;
 	int		 la;
 	char		*p;
 
-	/* 
-	 * Close out prior scopes.  "Regular" explicit macros cannot be
-	 * nested, but we allow roff macros to be placed just about
-	 * anywhere.
-	 */
+	/* Close out prior implicit scopes. */
 
-	if ( ! man_block_alloc(m, line, ppos, tok))
+	if ( ! rew_scope(MAN_BLOCK, man, tok))
 		return(0);
-	if ( ! man_head_alloc(m, line, ppos, tok))
+
+	if ( ! man_block_alloc(man, line, ppos, tok))
+		return(0);
+	if ( ! man_head_alloc(man, line, ppos, tok))
 		return(0);
 
 	for (;;) {
 		la = *pos;
-		if ( ! man_args(m, line, pos, buf, &p))
+		if ( ! man_args(man, line, pos, buf, &p))
 			break;
-		if ( ! man_word_alloc(m, line, la, p))
+		if ( ! man_word_alloc(man, line, la, p))
 			return(0);
 	}
 
-	assert(m);
+	assert(man);
 	assert(tok != MAN_MAX);
 
-	if ( ! rew_scope(MAN_HEAD, m, tok))
-		return(0);
-	return(man_body_alloc(m, line, ppos, tok));
+	for (n = man->last; n; n = n->parent) {
+		if (n->tok != tok)
+			continue;
+		assert(MAN_HEAD == n->type);
+		man_unscope(man, n, MANDOCERR_MAX);
+		break;
+	}
+
+	return(man_body_alloc(man, line, ppos, tok));
 }
 
 
@@ -346,27 +368,27 @@ blk_imp(MACRO_PROT_ARGS)
 
 	/* Close out prior scopes. */
 
-	if ( ! rew_scope(MAN_BODY, m, tok))
+	if ( ! rew_scope(MAN_BODY, man, tok))
 		return(0);
-	if ( ! rew_scope(MAN_BLOCK, m, tok))
+	if ( ! rew_scope(MAN_BLOCK, man, tok))
 		return(0);
 
 	/* Allocate new block & head scope. */
 
-	if ( ! man_block_alloc(m, line, ppos, tok))
+	if ( ! man_block_alloc(man, line, ppos, tok))
 		return(0);
-	if ( ! man_head_alloc(m, line, ppos, tok))
+	if ( ! man_head_alloc(man, line, ppos, tok))
 		return(0);
 
-	n = m->last;
+	n = man->last;
 
 	/* Add line arguments. */
 
 	for (;;) {
 		la = *pos;
-		if ( ! man_args(m, line, pos, buf, &p))
+		if ( ! man_args(man, line, pos, buf, &p))
 			break;
-		if ( ! man_word_alloc(m, line, la, p))
+		if ( ! man_word_alloc(man, line, la, p))
 			return(0);
 	}
 
@@ -375,17 +397,17 @@ blk_imp(MACRO_PROT_ARGS)
 	if (MAN_SCOPED & man_macros[tok].flags) {
 		/* If we're forcing scope (`TP'), keep it open. */
 		if (MAN_FSCOPED & man_macros[tok].flags) {
-			m->flags |= MAN_BLINE;
+			man->flags |= MAN_BLINE;
 			return(1);
-		} else if (n == m->last) {
-			m->flags |= MAN_BLINE;
+		} else if (n == man->last) {
+			man->flags |= MAN_BLINE;
 			return(1);
 		}
 	}
 
-	if ( ! rew_scope(MAN_HEAD, m, tok))
+	if ( ! rew_scope(MAN_HEAD, man, tok))
 		return(0);
-	return(man_body_alloc(m, line, ppos, tok));
+	return(man_body_alloc(man, line, ppos, tok));
 }
 
 
@@ -397,28 +419,37 @@ in_line_eoln(MACRO_PROT_ARGS)
 	char		*p;
 	struct man_node	*n;
 
-	if ( ! man_elem_alloc(m, line, ppos, tok))
+	if ( ! man_elem_alloc(man, line, ppos, tok))
 		return(0);
 
-	n = m->last;
+	n = man->last;
 
 	for (;;) {
 		la = *pos;
-		if ( ! man_args(m, line, pos, buf, &p))
+		if ( ! man_args(man, line, pos, buf, &p))
 			break;
-		if ( ! man_word_alloc(m, line, la, p))
+		if ( ! man_word_alloc(man, line, la, p))
 			return(0);
 	}
 
+	/*
+	 * Append MAN_EOS in case the last snipped argument
+	 * ends with a dot, e.g. `.IR syslog (3).'
+	 */
+
+	if (n != man->last &&
+	    mandoc_eos(man->last->string, strlen(man->last->string), 0))
+		man->last->flags |= MAN_EOS;
+
 	/*
 	 * If no arguments are specified and this is MAN_SCOPED (i.e.,
 	 * next-line scoped), then set our mode to indicate that we're
 	 * waiting for terms to load into our context.
 	 */
 
-	if (n == m->last && MAN_SCOPED & man_macros[tok].flags) {
+	if (n == man->last && MAN_SCOPED & man_macros[tok].flags) {
 		assert( ! (MAN_NSCOPED & man_macros[tok].flags));
-		m->flags |= MAN_ELINE;
+		man->flags |= MAN_ELINE;
 		return(1);
 	} 
 
@@ -426,11 +457,11 @@ in_line_eoln(MACRO_PROT_ARGS)
 
 	if (MAN_NSCOPED & man_macros[tok].flags) {
 		assert( ! (MAN_SCOPED & man_macros[tok].flags));
-		m->flags |= MAN_ILINE;
+		man->flags |= MAN_ILINE;
 	}
 
-	assert(MAN_ROOT != m->last->type);
-	m->next = MAN_NEXT_SIBLING;
+	assert(MAN_ROOT != man->last->type);
+	man->next = MAN_NEXT_SIBLING;
 	
 	/*
 	 * Rewind our element scope.  Note that when TH is pruned, we'll
@@ -438,22 +469,22 @@ in_line_eoln(MACRO_PROT_ARGS)
 	 * its sibling.
 	 */
 
-	for ( ; m->last; m->last = m->last->parent) {
-		if (m->last == n)
+	for ( ; man->last; man->last = man->last->parent) {
+		if (man->last == n)
 			break;
-		if (m->last->type == MAN_ROOT)
+		if (man->last->type == MAN_ROOT)
 			break;
-		if ( ! man_valid_post(m))
+		if ( ! man_valid_post(man))
 			return(0);
 	}
 
-	assert(m->last);
+	assert(man->last);
 
 	/*
 	 * Same here regarding whether we're back at the root. 
 	 */
 
-	if (m->last->type != MAN_ROOT && ! man_valid_post(m))
+	if (man->last->type != MAN_ROOT && ! man_valid_post(man))
 		return(0);
 
 	return(1);
@@ -461,14 +492,14 @@ in_line_eoln(MACRO_PROT_ARGS)
 
 
 int
-man_macroend(struct man *m)
+man_macroend(struct man *man)
 {
 
-	return(man_unscope(m, m->first, MANDOCERR_SCOPEEXIT));
+	return(man_unscope(man, man->first, MANDOCERR_SCOPEEXIT));
 }
 
 static int
-man_args(struct man *m, int line, int *pos, char *buf, char **v)
+man_args(struct man *man, int line, int *pos, char *buf, char **v)
 {
 	char	 *start;
 
@@ -479,6 +510,6 @@ man_args(struct man *m, int line, int *pos, char *buf, char **v)
 	if ('\0' == *start)
 		return(0);
 
-	*v = mandoc_getarg(m->parse, v, line, pos);
+	*v = mandoc_getarg(man->parse, v, line, pos);
 	return(1);
 }
diff --git a/contrib/mdocml/man_term.c b/contrib/mdocml/man_term.c
index 69c5c95e442..4bd62443b43 100644
--- a/contrib/mdocml/man_term.c
+++ b/contrib/mdocml/man_term.c
@@ -1,7 +1,7 @@
-/*	$Id: man_term.c,v 1.127 2012/01/03 15:16:24 kristaps Exp $ */
+/*	$Id: man_term.c,v 1.139 2013/12/22 23:34:13 schwarze Exp $ */
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010, 2011 Ingo Schwarze 
+ * Copyright (c) 2008-2012 Kristaps Dzonsons 
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -35,8 +35,6 @@
 
 #define	MAXMARGINS	  64 /* maximum number of indented scopes */
 
-/* FIXME: have PD set the default vspace width. */
-
 struct	mtermp {
 	int		  fl;
 #define	MANT_LITERAL	 (1 << 0)
@@ -44,12 +42,13 @@ struct	mtermp {
 	int		  lmargincur; /* index of current margin */
 	int		  lmarginsz; /* actual number of nested margins */
 	size_t		  offset; /* default offset to visible page */
+	int		  pardist; /* vert. space before par., unit: [v] */
 };
 
 #define	DECL_ARGS 	  struct termp *p, \
 			  struct mtermp *mt, \
 			  const struct man_node *n, \
-			  const struct man_meta *m
+			  const struct man_meta *meta
 
 struct	termact {
 	int		(*pre)(DECL_ARGS);
@@ -66,18 +65,20 @@ static	void		  print_man_node(DECL_ARGS);
 static	void		  print_man_head(struct termp *, const void *);
 static	void		  print_man_foot(struct termp *, const void *);
 static	void		  print_bvspace(struct termp *, 
-				const struct man_node *);
+				const struct man_node *, int);
 
 static	int		  pre_B(DECL_ARGS);
 static	int		  pre_HP(DECL_ARGS);
 static	int		  pre_I(DECL_ARGS);
 static	int		  pre_IP(DECL_ARGS);
 static	int		  pre_OP(DECL_ARGS);
+static	int		  pre_PD(DECL_ARGS);
 static	int		  pre_PP(DECL_ARGS);
 static	int		  pre_RS(DECL_ARGS);
 static	int		  pre_SH(DECL_ARGS);
 static	int		  pre_SS(DECL_ARGS);
 static	int		  pre_TP(DECL_ARGS);
+static	int		  pre_UR(DECL_ARGS);
 static	int		  pre_alternate(DECL_ARGS);
 static	int		  pre_ft(DECL_ARGS);
 static	int		  pre_ign(DECL_ARGS);
@@ -91,6 +92,7 @@ static	void		  post_RS(DECL_ARGS);
 static	void		  post_SH(DECL_ARGS);
 static	void		  post_SS(DECL_ARGS);
 static	void		  post_TP(DECL_ARGS);
+static	void		  post_UR(DECL_ARGS);
 
 static	const struct termact termacts[MAN_MAX] = {
 	{ pre_sp, NULL, MAN_NOTEXT }, /* br */
@@ -122,11 +124,15 @@ static	const struct termact termacts[MAN_MAX] = {
 	{ pre_RS, post_RS, 0 }, /* RS */
 	{ pre_ign, NULL, 0 }, /* DT */
 	{ pre_ign, NULL, 0 }, /* UC */
-	{ pre_ign, NULL, 0 }, /* PD */
+	{ pre_PD, NULL, MAN_NOTEXT }, /* PD */
 	{ pre_ign, NULL, 0 }, /* AT */
 	{ pre_in, NULL, MAN_NOTEXT }, /* in */
 	{ pre_ft, NULL, MAN_NOTEXT }, /* ft */
 	{ pre_OP, NULL, 0 }, /* OP */
+	{ pre_literal, NULL, 0 }, /* EX */
+	{ pre_literal, NULL, 0 }, /* EE */
+	{ pre_UR, post_UR, 0 }, /* UR */
+	{ NULL, NULL, 0 }, /* UE */
 };
 
 
@@ -136,7 +142,7 @@ terminal_man(void *arg, const struct man *man)
 {
 	struct termp		*p;
 	const struct man_node	*n;
-	const struct man_meta	*m;
+	const struct man_meta	*meta;
 	struct mtermp		 mt;
 
 	p = (struct termp *)arg;
@@ -152,18 +158,19 @@ terminal_man(void *arg, const struct man *man)
 		p->symtab = mchars_alloc();
 
 	n = man_node(man);
-	m = man_meta(man);
+	meta = man_meta(man);
 
-	term_begin(p, print_man_head, print_man_foot, m);
+	term_begin(p, print_man_head, print_man_foot, meta);
 	p->flags |= TERMP_NOSPACE;
 
 	memset(&mt, 0, sizeof(struct mtermp));
 
 	mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
 	mt.offset = term_len(p, p->defindent);
+	mt.pardist = 1;
 
 	if (n->child)
-		print_man_nodelist(p, &mt, n->child, m);
+		print_man_nodelist(p, &mt, n->child, meta);
 
 	term_end(p);
 }
@@ -201,8 +208,9 @@ a2width(const struct termp *p, const char *cp)
  * first, print it.
  */
 static void
-print_bvspace(struct termp *p, const struct man_node *n)
+print_bvspace(struct termp *p, const struct man_node *n, int pardist)
 {
+	int	 i;
 
 	term_newln(p);
 
@@ -214,7 +222,8 @@ print_bvspace(struct termp *p, const struct man_node *n)
 		if (NULL == n->prev)
 			return;
 
-	term_vspace(p);
+	for (i = 0; i < pardist; i++)
+		term_vspace(p);
 }
 
 /* ARGSUSED */
@@ -243,7 +252,7 @@ pre_literal(DECL_ARGS)
 
 	term_newln(p);
 
-	if (MAN_nf == n->tok)
+	if (MAN_nf == n->tok || MAN_EX == n->tok)
 		mt->fl |= MANT_LITERAL;
 	else
 		mt->fl &= ~MANT_LITERAL;
@@ -256,13 +265,29 @@ pre_literal(DECL_ARGS)
 	if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
 		p->offset = p->rmargin;
 		p->rmargin = p->maxrmargin;
-		p->flags &= ~(TERMP_NOBREAK | TERMP_TWOSPACE);
+		p->trailspace = 0;
+		p->flags &= ~TERMP_NOBREAK;
 		p->flags |= TERMP_NOSPACE;
 	}
 
 	return(0);
 }
 
+/* ARGSUSED */
+static int
+pre_PD(DECL_ARGS)
+{
+
+	n = n->child;
+	if (0 == n) {
+		mt->pardist = 1;
+		return(0);
+	}
+	assert(MAN_TEXT == n->type);
+	mt->pardist = atoi(n->string);
+	return(0);
+}
+
 /* ARGSUSED */
 static int
 pre_alternate(DECL_ARGS)
@@ -307,7 +332,7 @@ pre_alternate(DECL_ARGS)
 		term_fontrepl(p, font[i]);
 		if (savelit && NULL == nn->next)
 			mt->fl |= MANT_LITERAL;
-		print_man_node(p, mt, nn, m);
+		print_man_node(p, mt, nn, meta);
 		if (nn->next)
 			p->flags |= TERMP_NOSPACE;
 	}
@@ -438,28 +463,54 @@ pre_in(DECL_ARGS)
 static int
 pre_sp(DECL_ARGS)
 {
+	char		*s;
 	size_t		 i, len;
+	int		 neg;
 
 	if ((NULL == n->prev && n->parent)) {
-		if (MAN_SS == n->parent->tok)
-			return(0);
-		if (MAN_SH == n->parent->tok)
+		switch (n->parent->tok) {
+		case (MAN_SH):
+			/* FALLTHROUGH */
+		case (MAN_SS):
+			/* FALLTHROUGH */
+		case (MAN_PP):
+			/* FALLTHROUGH */
+		case (MAN_LP):
+			/* FALLTHROUGH */
+		case (MAN_P):
+			/* FALLTHROUGH */
 			return(0);
+		default:
+			break;
+		}
 	}
 
+	neg = 0;
 	switch (n->tok) {
 	case (MAN_br):
 		len = 0;
 		break;
 	default:
-		len = n->child ? a2height(p, n->child->string) : 1;
+		if (NULL == n->child) {
+			len = 1;
+			break;
+		}
+		s = n->child->string;
+		if ('-' == *s) {
+			neg = 1;
+			s++;
+		}
+		len = a2height(p, s);
 		break;
 	}
 
 	if (0 == len)
 		term_newln(p);
-	for (i = 0; i < len; i++)
-		term_vspace(p);
+	else if (neg)
+		p->skipvsp += len;
+	else
+		for (i = 0; i < len; i++)
+			term_vspace(p);
 
 	return(0);
 }
@@ -475,16 +526,19 @@ pre_HP(DECL_ARGS)
 
 	switch (n->type) {
 	case (MAN_BLOCK):
-		print_bvspace(p, n);
+		print_bvspace(p, n, mt->pardist);
 		return(1);
 	case (MAN_BODY):
-		p->flags |= TERMP_NOBREAK;
-		p->flags |= TERMP_TWOSPACE;
 		break;
 	default:
 		return(0);
 	}
 
+	if ( ! (MANT_LITERAL & mt->fl)) {
+		p->flags |= TERMP_NOBREAK;
+		p->trailspace = 2;
+	}
+
 	len = mt->lmargin[mt->lmargincur];
 	ival = -1;
 
@@ -514,13 +568,10 @@ post_HP(DECL_ARGS)
 {
 
 	switch (n->type) {
-	case (MAN_BLOCK):
-		term_flushln(p);
-		break;
 	case (MAN_BODY):
-		term_flushln(p);
+		term_newln(p);
 		p->flags &= ~TERMP_NOBREAK;
-		p->flags &= ~TERMP_TWOSPACE;
+		p->trailspace = 0;
 		p->offset = mt->offset;
 		p->rmargin = p->maxrmargin;
 		break;
@@ -538,7 +589,7 @@ pre_PP(DECL_ARGS)
 	switch (n->type) {
 	case (MAN_BLOCK):
 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
-		print_bvspace(p, n);
+		print_bvspace(p, n, mt->pardist);
 		break;
 	default:
 		p->offset = mt->offset;
@@ -563,9 +614,10 @@ pre_IP(DECL_ARGS)
 		break;
 	case (MAN_HEAD):
 		p->flags |= TERMP_NOBREAK;
+		p->trailspace = 1;
 		break;
 	case (MAN_BLOCK):
-		print_bvspace(p, n);
+		print_bvspace(p, n, mt->pardist);
 		/* FALLTHROUGH */
 	default:
 		return(1);
@@ -598,7 +650,7 @@ pre_IP(DECL_ARGS)
 		mt->fl &= ~MANT_LITERAL;
 
 		if (n->child)
-			print_man_node(p, mt, n->child, m);
+			print_man_node(p, mt, n->child, meta);
 
 		if (savelit)
 			mt->fl |= MANT_LITERAL;
@@ -625,10 +677,12 @@ post_IP(DECL_ARGS)
 	case (MAN_HEAD):
 		term_flushln(p);
 		p->flags &= ~TERMP_NOBREAK;
+		p->trailspace = 0;
 		p->rmargin = p->maxrmargin;
 		break;
 	case (MAN_BODY):
 		term_newln(p);
+		p->offset = mt->offset;
 		break;
 	default:
 		break;
@@ -647,12 +701,13 @@ pre_TP(DECL_ARGS)
 	switch (n->type) {
 	case (MAN_HEAD):
 		p->flags |= TERMP_NOBREAK;
+		p->trailspace = 1;
 		break;
 	case (MAN_BODY):
 		p->flags |= TERMP_NOSPACE;
 		break;
 	case (MAN_BLOCK):
-		print_bvspace(p, n);
+		print_bvspace(p, n, mt->pardist);
 		/* FALLTHROUGH */
 	default:
 		return(1);
@@ -683,7 +738,7 @@ pre_TP(DECL_ARGS)
 		/* Don't print same-line elements. */
 		for (nn = n->child; nn; nn = nn->next)
 			if (nn->line > n->line)
-				print_man_node(p, mt, nn, m);
+				print_man_node(p, mt, nn, meta);
 
 		if (savelit)
 			mt->fl |= MANT_LITERAL;
@@ -694,6 +749,8 @@ pre_TP(DECL_ARGS)
 	case (MAN_BODY):
 		p->offset = mt->offset + len;
 		p->rmargin = p->maxrmargin;
+		p->trailspace = 0;
+		p->flags &= ~TERMP_NOBREAK;
 		break;
 	default:
 		break;
@@ -711,12 +768,10 @@ post_TP(DECL_ARGS)
 	switch (n->type) {
 	case (MAN_HEAD):
 		term_flushln(p);
-		p->flags &= ~TERMP_NOBREAK;
-		p->flags &= ~TERMP_TWOSPACE;
-		p->rmargin = p->maxrmargin;
 		break;
 	case (MAN_BODY):
 		term_newln(p);
+		p->offset = mt->offset;
 		break;
 	default:
 		break;
@@ -728,6 +783,7 @@ post_TP(DECL_ARGS)
 static int
 pre_SS(DECL_ARGS)
 {
+	int	 i;
 
 	switch (n->type) {
 	case (MAN_BLOCK):
@@ -740,11 +796,12 @@ pre_SS(DECL_ARGS)
 				break;
 		if (NULL == n->prev)
 			break;
-		term_vspace(p);
+		for (i = 0; i < mt->pardist; i++)
+			term_vspace(p);
 		break;
 	case (MAN_HEAD):
 		term_fontrepl(p, TERMFONT_BOLD);
-		p->offset = term_len(p, p->defindent/2);
+		p->offset = term_len(p, 3);
 		break;
 	case (MAN_BODY):
 		p->offset = mt->offset;
@@ -779,6 +836,7 @@ post_SS(DECL_ARGS)
 static int
 pre_SH(DECL_ARGS)
 {
+	int	 i;
 
 	switch (n->type) {
 	case (MAN_BLOCK):
@@ -792,7 +850,8 @@ pre_SH(DECL_ARGS)
 		/* If the first macro, no vspae. */
 		if (NULL == n->prev)
 			break;
-		term_vspace(p);
+		for (i = 0; i < mt->pardist; i++)
+			term_vspace(p);
 		break;
 	case (MAN_HEAD):
 		term_fontrepl(p, TERMFONT_BOLD);
@@ -890,6 +949,32 @@ post_RS(DECL_ARGS)
 		mt->lmargincur = mt->lmarginsz;
 }
 
+/* ARGSUSED */
+static int
+pre_UR(DECL_ARGS)
+{
+
+	return (MAN_HEAD != n->type);
+}
+
+/* ARGSUSED */
+static void
+post_UR(DECL_ARGS)
+{
+
+	if (MAN_BLOCK != n->type)
+		return;
+
+	term_word(p, "<");
+	p->flags |= TERMP_NOSPACE;
+
+	if (NULL != n->child->child)
+		print_man_node(p, mt, n->child->child, meta);
+
+	p->flags |= TERMP_NOSPACE;
+	term_word(p, ">");
+}
+
 static void
 print_man_node(DECL_ARGS)
 {
@@ -910,29 +995,8 @@ print_man_node(DECL_ARGS)
 			term_newln(p);
 
 		term_word(p, n->string);
+		goto out;
 
-		/*
-		 * If we're in a literal context, make sure that words
-		 * togehter on the same line stay together.  This is a
-		 * POST-printing call, so we check the NEXT word.  Since
-		 * -man doesn't have nested macros, we don't need to be
-		 * more specific than this.
-		 */
-		if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
-				(NULL == n->next || 
-				 n->next->line > n->line)) {
-			rm = p->rmargin;
-			rmax = p->maxrmargin;
-			p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
-			p->flags |= TERMP_NOSPACE;
-			term_flushln(p);
-			p->rmargin = rm;
-			p->maxrmargin = rmax;
-		}
-
-		if (MAN_EOS & n->flags)
-			p->flags |= TERMP_SENTENCE;
-		return;
 	case (MAN_EQN):
 		term_eqn(p, n->eqn);
 		return;
@@ -954,16 +1018,41 @@ print_man_node(DECL_ARGS)
 
 	c = 1;
 	if (termacts[n->tok].pre)
-		c = (*termacts[n->tok].pre)(p, mt, n, m);
+		c = (*termacts[n->tok].pre)(p, mt, n, meta);
 
 	if (c && n->child)
-		print_man_nodelist(p, mt, n->child, m);
+		print_man_nodelist(p, mt, n->child, meta);
 
 	if (termacts[n->tok].post)
-		(*termacts[n->tok].post)(p, mt, n, m);
+		(*termacts[n->tok].post)(p, mt, n, meta);
 	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
 		term_fontrepl(p, TERMFONT_NONE);
 
+out:
+	/*
+	 * If we're in a literal context, make sure that words
+	 * together on the same line stay together.  This is a
+	 * POST-printing call, so we check the NEXT word.  Since
+	 * -man doesn't have nested macros, we don't need to be
+	 * more specific than this.
+	 */
+	if (MANT_LITERAL & mt->fl && ! (TERMP_NOBREAK & p->flags) &&
+	    (NULL == n->next || n->next->line > n->line)) {
+		rm = p->rmargin;
+		rmax = p->maxrmargin;
+		p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
+		p->flags |= TERMP_NOSPACE;
+		if (NULL != n->string && '\0' != *n->string)
+			term_flushln(p);
+		else
+			term_newln(p);
+		if (rm < rmax && n->parent->tok == MAN_HP) {
+			p->offset = rm;
+			p->rmargin = rmax;
+		} else
+			p->rmargin = rm;
+		p->maxrmargin = rmax;
+	}
 	if (MAN_EOS & n->flags)
 		p->flags |= TERMP_SENTENCE;
 }
@@ -973,10 +1062,10 @@ static void
 print_man_nodelist(DECL_ARGS)
 {
 
-	print_man_node(p, mt, n, m);
+	print_man_node(p, mt, n, meta);
 	if ( ! n->next)
 		return;
-	print_man_nodelist(p, mt, n->next, m);
+	print_man_nodelist(p, mt, n->next, meta);
 }
 
 
@@ -1016,6 +1105,7 @@ print_man_foot(struct termp *p, const void *arg)
 	/* Bottom left corner: manual source. */
 
 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
+	p->trailspace = 1;
 	p->offset = 0;
 	p->rmargin = (p->maxrmargin - datelen + term_len(p, 1)) / 2;
 
@@ -1038,6 +1128,7 @@ print_man_foot(struct termp *p, const void *arg)
 
 	p->flags &= ~TERMP_NOBREAK;
 	p->flags |= TERMP_NOSPACE;
+	p->trailspace = 0;
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin;
 
@@ -1051,24 +1142,25 @@ print_man_head(struct termp *p, const void *arg)
 {
 	char		buf[BUFSIZ], title[BUFSIZ];
 	size_t		buflen, titlen;
-	const struct man_meta *m;
+	const struct man_meta *meta;
 
-	m = (const struct man_meta *)arg;
-	assert(m->title);
-	assert(m->msec);
+	meta = (const struct man_meta *)arg;
+	assert(meta->title);
+	assert(meta->msec);
 
-	if (m->vol)
-		strlcpy(buf, m->vol, BUFSIZ);
+	if (meta->vol)
+		strlcpy(buf, meta->vol, BUFSIZ);
 	else
 		buf[0] = '\0';
 	buflen = term_strlen(p, buf);
 
 	/* Top left corner: manual title and section. */
 
-	snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
+	snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
 	titlen = term_strlen(p, title);
 
 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
+	p->trailspace = 1;
 	p->offset = 0;
 	p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
 	    (p->maxrmargin - 
@@ -1091,6 +1183,7 @@ print_man_head(struct termp *p, const void *arg)
 	/* Top right corner: title and section, again. */
 
 	p->flags &= ~TERMP_NOBREAK;
+	p->trailspace = 0;
 	if (p->rmargin + titlen <= p->maxrmargin) {
 		p->flags |= TERMP_NOSPACE;
 		p->offset = p->rmargin;
diff --git a/contrib/mdocml/man_validate.c b/contrib/mdocml/man_validate.c
index e40b089f53b..da2e557ebb5 100644
--- a/contrib/mdocml/man_validate.c
+++ b/contrib/mdocml/man_validate.c
@@ -1,7 +1,7 @@
-/*	$Id: man_validate.c,v 1.80 2012/01/03 15:16:24 kristaps Exp $ */
+/*	$Id: man_validate.c,v 1.86 2013/10/17 20:54:58 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010 Ingo Schwarze 
+ * Copyright (c) 2010, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -35,7 +35,7 @@
 #include "libman.h"
 #include "libmandoc.h"
 
-#define	CHKARGS	  struct man *m, struct man_node *n
+#define	CHKARGS	  struct man *man, struct man_node *n
 
 typedef	int	(*v_check)(CHKARGS);
 
@@ -49,12 +49,14 @@ static	int	  check_eq2(CHKARGS);
 static	int	  check_le1(CHKARGS);
 static	int	  check_ge2(CHKARGS);
 static	int	  check_le5(CHKARGS);
+static	int	  check_head1(CHKARGS);
 static	int	  check_par(CHKARGS);
 static	int	  check_part(CHKARGS);
 static	int	  check_root(CHKARGS);
 static	void	  check_text(CHKARGS);
 
 static	int	  post_AT(CHKARGS);
+static	int	  post_IP(CHKARGS);
 static	int	  post_vs(CHKARGS);
 static	int	  post_fi(CHKARGS);
 static	int	  post_ft(CHKARGS);
@@ -70,6 +72,8 @@ static	v_check	  posts_eq0[] = { check_eq0, NULL };
 static	v_check	  posts_eq2[] = { check_eq2, NULL };
 static	v_check	  posts_fi[] = { check_eq0, post_fi, NULL };
 static	v_check	  posts_ft[] = { post_ft, NULL };
+static	v_check	  posts_ip[] = { post_IP, NULL };
+static	v_check	  posts_le1[] = { check_le1, NULL };
 static	v_check	  posts_nf[] = { check_eq0, post_nf, NULL };
 static	v_check	  posts_par[] = { check_par, NULL };
 static	v_check	  posts_part[] = { check_part, NULL };
@@ -77,6 +81,7 @@ static	v_check	  posts_sec[] = { post_sec, NULL };
 static	v_check	  posts_sp[] = { post_vs, check_le1, NULL };
 static	v_check	  posts_th[] = { check_ge2, check_le5, post_TH, NULL };
 static	v_check	  posts_uc[] = { post_UC, NULL };
+static	v_check	  posts_ur[] = { check_head1, check_part, NULL };
 static	v_check	  pres_sec[] = { pre_sec, NULL };
 
 static	const struct man_valid man_valids[MAN_MAX] = {
@@ -88,7 +93,7 @@ static	const struct man_valid man_valids[MAN_MAX] = {
 	{ NULL, posts_par }, /* LP */
 	{ NULL, posts_par }, /* PP */
 	{ NULL, posts_par }, /* P */
-	{ NULL, NULL }, /* IP */
+	{ NULL, posts_ip }, /* IP */
 	{ NULL, NULL }, /* HP */
 	{ NULL, NULL }, /* SM */
 	{ NULL, NULL }, /* SB */
@@ -109,16 +114,20 @@ static	const struct man_valid man_valids[MAN_MAX] = {
 	{ NULL, posts_part }, /* RS */
 	{ NULL, NULL }, /* DT */
 	{ NULL, posts_uc }, /* UC */
-	{ NULL, NULL }, /* PD */
+	{ NULL, posts_le1 }, /* PD */
 	{ NULL, posts_at }, /* AT */
 	{ NULL, NULL }, /* in */
 	{ NULL, posts_ft }, /* ft */
 	{ NULL, posts_eq2 }, /* OP */
+	{ NULL, posts_nf }, /* EX */
+	{ NULL, posts_fi }, /* EE */
+	{ NULL, posts_ur }, /* UR */
+	{ NULL, NULL }, /* UE */
 };
 
 
 int
-man_valid_pre(struct man *m, struct man_node *n)
+man_valid_pre(struct man *man, struct man_node *n)
 {
 	v_check		*cp;
 
@@ -138,27 +147,27 @@ man_valid_pre(struct man *m, struct man_node *n)
 	if (NULL == (cp = man_valids[n->tok].pres))
 		return(1);
 	for ( ; *cp; cp++)
-		if ( ! (*cp)(m, n)) 
+		if ( ! (*cp)(man, n)) 
 			return(0);
 	return(1);
 }
 
 
 int
-man_valid_post(struct man *m)
+man_valid_post(struct man *man)
 {
 	v_check		*cp;
 
-	if (MAN_VALID & m->last->flags)
+	if (MAN_VALID & man->last->flags)
 		return(1);
-	m->last->flags |= MAN_VALID;
+	man->last->flags |= MAN_VALID;
 
-	switch (m->last->type) {
+	switch (man->last->type) {
 	case (MAN_TEXT): 
-		check_text(m, m->last);
+		check_text(man, man->last);
 		return(1);
 	case (MAN_ROOT):
-		return(check_root(m, m->last));
+		return(check_root(man, man->last));
 	case (MAN_EQN):
 		/* FALLTHROUGH */
 	case (MAN_TBL):
@@ -167,10 +176,10 @@ man_valid_post(struct man *m)
 		break;
 	}
 
-	if (NULL == (cp = man_valids[m->last->tok].posts))
+	if (NULL == (cp = man_valids[man->last->tok].posts))
 		return(1);
 	for ( ; *cp; cp++)
-		if ( ! (*cp)(m, m->last))
+		if ( ! (*cp)(man, man->last))
 			return(0);
 
 	return(1);
@@ -181,29 +190,29 @@ static int
 check_root(CHKARGS) 
 {
 
-	if (MAN_BLINE & m->flags)
-		man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
-	else if (MAN_ELINE & m->flags)
-		man_nmsg(m, n, MANDOCERR_SCOPEEXIT);
+	if (MAN_BLINE & man->flags)
+		man_nmsg(man, n, MANDOCERR_SCOPEEXIT);
+	else if (MAN_ELINE & man->flags)
+		man_nmsg(man, n, MANDOCERR_SCOPEEXIT);
 
-	m->flags &= ~MAN_BLINE;
-	m->flags &= ~MAN_ELINE;
+	man->flags &= ~MAN_BLINE;
+	man->flags &= ~MAN_ELINE;
 
-	if (NULL == m->first->child) {
-		man_nmsg(m, n, MANDOCERR_NODOCBODY);
+	if (NULL == man->first->child) {
+		man_nmsg(man, n, MANDOCERR_NODOCBODY);
 		return(0);
-	} else if (NULL == m->meta.title) {
-		man_nmsg(m, n, MANDOCERR_NOTITLE);
+	} else if (NULL == man->meta.title) {
+		man_nmsg(man, n, MANDOCERR_NOTITLE);
 
 		/*
 		 * If a title hasn't been set, do so now (by
 		 * implication, date and section also aren't set).
 		 */
 
-	        m->meta.title = mandoc_strdup("unknown");
-		m->meta.msec = mandoc_strdup("1");
-		m->meta.date = mandoc_normdate
-			(m->parse, NULL, n->line, n->pos);
+	        man->meta.title = mandoc_strdup("unknown");
+		man->meta.msec = mandoc_strdup("1");
+		man->meta.date = mandoc_normdate
+			(man->parse, NULL, n->line, n->pos);
 	}
 
 	return(1);
@@ -214,12 +223,12 @@ check_text(CHKARGS)
 {
 	char		*cp, *p;
 
-	if (MAN_LITERAL & m->flags)
+	if (MAN_LITERAL & man->flags)
 		return;
 
 	cp = n->string;
 	for (p = cp; NULL != (p = strchr(p, '\t')); p++)
-		man_pmsg(m, n->line, (int)(p - cp), MANDOCERR_BADTAB);
+		man_pmsg(man, n->line, (int)(p - cp), MANDOCERR_BADTAB);
 }
 
 #define	INEQ_DEFINE(x, ineq, name) \
@@ -228,7 +237,7 @@ check_##name(CHKARGS) \
 { \
 	if (n->nchild ineq (x)) \
 		return(1); \
-	mandoc_vmsg(MANDOCERR_ARGCOUNT, m->parse, n->line, n->pos, \
+	mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line, n->pos, \
 			"line arguments %s %d (have %d)", \
 			#ineq, (x), n->nchild); \
 	return(1); \
@@ -240,6 +249,17 @@ INEQ_DEFINE(1, <=, le1)
 INEQ_DEFINE(2, >=, ge2)
 INEQ_DEFINE(5, <=, le5)
 
+static int
+check_head1(CHKARGS)
+{
+
+	if (MAN_HEAD == n->type && 1 != n->nchild)
+		mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line,
+		    n->pos, "line arguments eq 1 (have %d)", n->nchild);
+
+	return(1);
+}
+
 static int
 post_ft(CHKARGS)
 {
@@ -282,14 +302,14 @@ post_ft(CHKARGS)
 
 	if (0 == ok) {
 		mandoc_vmsg
-			(MANDOCERR_BADFONT, m->parse,
+			(MANDOCERR_BADFONT, man->parse,
 			 n->line, n->pos, "%s", cp);
 		*cp = '\0';
 	}
 
 	if (1 < n->nchild)
 		mandoc_vmsg
-			(MANDOCERR_ARGCOUNT, m->parse, n->line, 
+			(MANDOCERR_ARGCOUNT, man->parse, n->line, 
 			 n->pos, "want one child (have %d)", 
 			 n->nchild);
 
@@ -301,7 +321,7 @@ pre_sec(CHKARGS)
 {
 
 	if (MAN_BLOCK == n->type)
-		m->flags &= ~MAN_LITERAL;
+		man->flags &= ~MAN_LITERAL;
 	return(1);
 }
 
@@ -312,7 +332,7 @@ post_sec(CHKARGS)
 	if ( ! (MAN_HEAD == n->type && 0 == n->nchild)) 
 		return(1);
 
-	man_nmsg(m, n, MANDOCERR_SYNTARGCOUNT);
+	man_nmsg(man, n, MANDOCERR_SYNTARGCOUNT);
 	return(0);
 }
 
@@ -321,7 +341,7 @@ check_part(CHKARGS)
 {
 
 	if (MAN_BODY == n->type && 0 == n->nchild)
-		mandoc_msg(MANDOCERR_ARGCWARN, m->parse, n->line, 
+		mandoc_msg(MANDOCERR_ARGCWARN, man->parse, n->line, 
 				n->pos, "want children (have none)");
 
 	return(1);
@@ -335,15 +355,15 @@ check_par(CHKARGS)
 	switch (n->type) {
 	case (MAN_BLOCK):
 		if (0 == n->body->nchild)
-			man_node_delete(m, n);
+			man_node_delete(man, n);
 		break;
 	case (MAN_BODY):
 		if (0 == n->nchild)
-			man_nmsg(m, n, MANDOCERR_IGNPAR);
+			man_nmsg(man, n, MANDOCERR_IGNPAR);
 		break;
 	case (MAN_HEAD):
 		if (n->nchild)
-			man_nmsg(m, n, MANDOCERR_ARGSLOST);
+			man_nmsg(man, n, MANDOCERR_ARGSLOST);
 		break;
 	default:
 		break;
@@ -352,6 +372,24 @@ check_par(CHKARGS)
 	return(1);
 }
 
+static int
+post_IP(CHKARGS)
+{
+
+	switch (n->type) {
+	case (MAN_BLOCK):
+		if (0 == n->head->nchild && 0 == n->body->nchild)
+			man_node_delete(man, n);
+		break;
+	case (MAN_BODY):
+		if (0 == n->parent->head->nchild && 0 == n->nchild)
+			man_nmsg(man, n, MANDOCERR_IGNPAR);
+		break;
+	default:
+		break;
+	}
+	return(1);
+}
 
 static int
 post_TH(CHKARGS)
@@ -359,21 +397,16 @@ post_TH(CHKARGS)
 	const char	*p;
 	int		 line, pos;
 
-	if (m->meta.title)
-		free(m->meta.title);
-	if (m->meta.vol)
-		free(m->meta.vol);
-	if (m->meta.source)
-		free(m->meta.source);
-	if (m->meta.msec)
-		free(m->meta.msec);
-	if (m->meta.date)
-		free(m->meta.date);
+	free(man->meta.title);
+	free(man->meta.vol);
+	free(man->meta.source);
+	free(man->meta.msec);
+	free(man->meta.date);
 
 	line = n->line;
 	pos = n->pos;
-	m->meta.title = m->meta.vol = m->meta.date =
-		m->meta.msec = m->meta.source = NULL;
+	man->meta.title = man->meta.vol = man->meta.date =
+		man->meta.msec = man->meta.source = NULL;
 
 	/* ->TITLE<- MSEC DATE SOURCE VOL */
 
@@ -383,22 +416,22 @@ post_TH(CHKARGS)
 			/* Only warn about this once... */
 			if (isalpha((unsigned char)*p) && 
 					! isupper((unsigned char)*p)) {
-				man_nmsg(m, n, MANDOCERR_UPPERCASE);
+				man_nmsg(man, n, MANDOCERR_UPPERCASE);
 				break;
 			}
 		}
-		m->meta.title = mandoc_strdup(n->string);
+		man->meta.title = mandoc_strdup(n->string);
 	} else
-		m->meta.title = mandoc_strdup("");
+		man->meta.title = mandoc_strdup("");
 
 	/* TITLE ->MSEC<- DATE SOURCE VOL */
 
 	if (n)
 		n = n->next;
 	if (n && n->string)
-		m->meta.msec = mandoc_strdup(n->string);
+		man->meta.msec = mandoc_strdup(n->string);
 	else
-		m->meta.msec = mandoc_strdup("");
+		man->meta.msec = mandoc_strdup("");
 
 	/* TITLE MSEC ->DATE<- SOURCE VOL */
 
@@ -406,30 +439,30 @@ post_TH(CHKARGS)
 		n = n->next;
 	if (n && n->string && '\0' != n->string[0]) {
 		pos = n->pos;
-		m->meta.date = mandoc_normdate
-		    (m->parse, n->string, line, pos);
+		man->meta.date = mandoc_normdate
+		    (man->parse, n->string, line, pos);
 	} else
-		m->meta.date = mandoc_strdup("");
+		man->meta.date = mandoc_strdup("");
 
 	/* TITLE MSEC DATE ->SOURCE<- VOL */
 
 	if (n && (n = n->next))
-		m->meta.source = mandoc_strdup(n->string);
+		man->meta.source = mandoc_strdup(n->string);
 
 	/* TITLE MSEC DATE SOURCE ->VOL<- */
 	/* If missing, use the default VOL name for MSEC. */
 
 	if (n && (n = n->next))
-		m->meta.vol = mandoc_strdup(n->string);
-	else if ('\0' != m->meta.msec[0] &&
-	    (NULL != (p = mandoc_a2msec(m->meta.msec))))
-		m->meta.vol = mandoc_strdup(p);
+		man->meta.vol = mandoc_strdup(n->string);
+	else if ('\0' != man->meta.msec[0] &&
+	    (NULL != (p = mandoc_a2msec(man->meta.msec))))
+		man->meta.vol = mandoc_strdup(p);
 
 	/*
 	 * Remove the `TH' node after we've processed it for our
 	 * meta-data.
 	 */
-	man_node_delete(m, m->last);
+	man_node_delete(man, man->last);
 	return(1);
 }
 
@@ -437,10 +470,10 @@ static int
 post_nf(CHKARGS)
 {
 
-	if (MAN_LITERAL & m->flags)
-		man_nmsg(m, n, MANDOCERR_SCOPEREP);
+	if (MAN_LITERAL & man->flags)
+		man_nmsg(man, n, MANDOCERR_SCOPEREP);
 
-	m->flags |= MAN_LITERAL;
+	man->flags |= MAN_LITERAL;
 	return(1);
 }
 
@@ -448,10 +481,10 @@ static int
 post_fi(CHKARGS)
 {
 
-	if ( ! (MAN_LITERAL & m->flags))
-		man_nmsg(m, n, MANDOCERR_WNOSCOPE);
+	if ( ! (MAN_LITERAL & man->flags))
+		man_nmsg(man, n, MANDOCERR_WNOSCOPE);
 
-	m->flags &= ~MAN_LITERAL;
+	man->flags &= ~MAN_LITERAL;
 	return(1);
 }
 
@@ -488,10 +521,8 @@ post_UC(CHKARGS)
 			p = bsd_versions[0];
 	}
 
-	if (m->meta.source)
-		free(m->meta.source);
-
-	m->meta.source = mandoc_strdup(p);
+	free(man->meta.source);
+	man->meta.source = mandoc_strdup(p);
 	return(1);
 }
 
@@ -528,10 +559,8 @@ post_AT(CHKARGS)
 			p = unix_versions[0];
 	}
 
-	if (m->meta.source)
-		free(m->meta.source);
-
-	m->meta.source = mandoc_strdup(p);
+	free(man->meta.source);
+	man->meta.source = mandoc_strdup(p);
 	return(1);
 }
 
@@ -539,12 +568,25 @@ static int
 post_vs(CHKARGS)
 {
 
-	/* 
-	 * Don't warn about this because it occurs in pod2man and would
-	 * cause considerable (unfixable) warnage.
-	 */
-	if (NULL == n->prev && MAN_ROOT == n->parent->type)
-		man_node_delete(m, n);
+	if (NULL != n->prev)
+		return(1);
+
+	switch (n->parent->tok) {
+	case (MAN_SH):
+		/* FALLTHROUGH */
+	case (MAN_SS):
+		man_nmsg(man, n, MANDOCERR_IGNPAR);
+		/* FALLTHROUGH */
+	case (MAN_MAX):
+		/* 
+		 * Don't warn about this because it occurs in pod2man
+		 * and would cause considerable (unfixable) warnage.
+		 */
+		man_node_delete(man, n);
+		break;
+	default:
+		break;
+	}
 
 	return(1);
 }
diff --git a/contrib/mdocml/mandoc.1 b/contrib/mdocml/mandoc.1
index dbff0e31caa..0657bc66c55 100644
--- a/contrib/mdocml/mandoc.1
+++ b/contrib/mdocml/mandoc.1
@@ -1,6 +1,7 @@
-.\"	$Id: mandoc.1,v 1.100 2011/12/25 19:35:44 kristaps Exp $
+.\"	$Id: mandoc.1,v 1.103 2013/07/13 19:41:16 schwarze Exp $
 .\"
 .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
+.\" Copyright (c) 2012 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: December 25 2011 $
+.Dd $Mdocdate: July 13 2013 $
 .Dt MANDOC 1
 .Os
 .Sh NAME
@@ -23,6 +24,9 @@
 .Sh SYNOPSIS
 .Nm mandoc
 .Op Fl V
+.Sm off
+.Op Fl I Cm os Li = Ar name
+.Sm on
 .Op Fl m Ns Ar format
 .Op Fl O Ns Ar option
 .Op Fl T Ns Ar output
@@ -49,6 +53,15 @@ output.
 .Pp
 The arguments are as follows:
 .Bl -tag -width Ds
+.Sm off
+.It Fl I Cm os Li = Ar name
+.Sm on
+Override the default operating system
+.Ar name
+for the
+.Xr mdoc 7
+.Sq \&Os
+macro.
 .It Fl m Ns Ar format
 Input format.
 See
@@ -334,7 +347,7 @@ for font style specification and available command-line arguments.
 Translate input format into
 .Xr man 7
 output format.
-This is useful for distributing manual sources to legancy systems
+This is useful for distributing manual sources to legacy systems
 lacking
 .Xr mdoc 7
 formatters.
@@ -637,8 +650,7 @@ lists render similarly.
 The
 .Nm
 utility was written by
-.An Kristaps Dzonsons ,
-.Mt kristaps@bsd.lv .
+.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
 .Sh CAVEATS
 In
 .Fl T Ns Cm html
diff --git a/contrib/mdocml/mandoc.3 b/contrib/mdocml/mandoc.3
index 4d0b20d6507..fe6503d5477 100644
--- a/contrib/mdocml/mandoc.3
+++ b/contrib/mdocml/mandoc.3
@@ -1,4 +1,4 @@
-.\"	$Id: mandoc.3,v 1.17 2012/01/13 15:27:14 joerg Exp $
+.\"	$Id: mandoc.3,v 1.22 2013/10/06 17:01:52 schwarze Exp $
 .\"
 .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
 .\" Copyright (c) 2010 Ingo Schwarze 
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: January 13 2012 $
+.Dd $Mdocdate: October 6 2013 $
 .Dt MANDOC 3
 .Os
 .Sh NAME
@@ -43,15 +43,15 @@
 .Nm mparse_strlevel
 .Nd mandoc macro compiler library
 .Sh LIBRARY
-.Lb mandoc
+.Lb libmandoc
 .Sh SYNOPSIS
 .In man.h
 .In mdoc.h
 .In mandoc.h
 .Ft "enum mandoc_esc"
 .Fo mandoc_escape
-.Fa "const char **end"
-.Fa "const char **start"
+.Fa "const char const **end"
+.Fa "const char const **start"
 .Fa "int *sz"
 .Fc
 .Ft "const struct man_meta *"
@@ -67,7 +67,7 @@
 .Fa "const struct man *man"
 .Fc
 .Ft "struct mchars *"
-.Fn mchars_alloc
+.Fn mchars_alloc "void"
 .Ft void
 .Fn mchars_free "struct mchars *p"
 .Ft char
@@ -86,7 +86,6 @@
 .Fa "const struct mchars *p"
 .Fa "const char *cp"
 .Fa "size_t sz"
-.Ft "const char *"
 .Fc
 .Ft "const struct mdoc_meta *"
 .Fo mdoc_meta
@@ -257,16 +256,32 @@ and
 .Va sz
 may be
 .Dv NULL .
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa mandoc.c .
 .It Fn man_meta
 Obtain the meta-data of a successful parse.
 This may only be used on a pointer returned by
 .Fn mparse_result .
+Declared in
+.In man.h ,
+implemented in
+.Pa man.c .
 .It Fn man_mparse
 Get the parser used for the current output.
+Declared in
+.In man.h ,
+implemented in
+.Pa man.c .
 .It Fn man_node
 Obtain the root node of a successful parse.
 This may only be used on a pointer returned by
 .Fn mparse_result .
+Declared in
+.In man.h ,
+implemented in
+.Pa man.c .
 .It Fn mchars_alloc
 Allocate an
 .Vt "struct mchars *"
@@ -276,33 +291,65 @@ See
 for an overview of special characters.
 The object must be freed with
 .Fn mchars_free .
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa chars.c .
 .It Fn mchars_free
 Free an object created with
 .Fn mchars_alloc .
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa chars.c .
 .It Fn mchars_num2char
 Convert a character index (e.g., the \eN\(aq\(aq escape) into a
 printable ASCII character.
 Returns \e0 (the nil character) if the input sequence is malformed.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa chars.c .
 .It Fn mchars_num2uc
 Convert a hexadecimal character index (e.g., the \e[uNNNN] escape) into
 a Unicode codepoint.
 Returns \e0 (the nil character) if the input sequence is malformed.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa chars.c .
 .It Fn mchars_spec2cp
 Convert a special character into a valid Unicode codepoint.
 Returns \-1 on failure or a non-zero Unicode codepoint on success.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa chars.c .
 .It Fn mchars_spec2str
 Convert a special character into an ASCII string.
 Returns
 .Dv NULL
 on failure.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa chars.c .
 .It Fn mdoc_meta
 Obtain the meta-data of a successful parse.
 This may only be used on a pointer returned by
 .Fn mparse_result .
+Declared in
+.In mdoc.h ,
+implemented in
+.Pa mdoc.c .
 .It Fn mdoc_node
 Obtain the root node of a successful parse.
 This may only be used on a pointer returned by
 .Fn mparse_result .
+Declared in
+.In mdoc.h ,
+implemented in
+.Pa mdoc.c .
 .It Fn mparse_alloc
 Allocate a parser.
 The same parser may be used for multiple files so long as
@@ -310,18 +357,34 @@ The same parser may be used for multiple files so long as
 is called between parses.
 .Fn mparse_free
 must be called to free the memory allocated by this function.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_free
 Free all memory allocated by
 .Fn mparse_alloc .
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_getkeep
 Acquire the keep buffer.
 Must follow a call of
 .Fn mparse_keep .
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_keep
 Instruct the parser to retain a copy of its parsed input.
 This can be acquired with subsequent
 .Fn mparse_getkeep
 calls.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_readfd
 Parse a file or file descriptor.
 If
@@ -336,10 +399,18 @@ is assumed to be the name associated with
 This may be called multiple times with different parameters; however,
 .Fn mparse_reset
 should be invoked between parses.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_reset
 Reset a parser so that
 .Fn mparse_readfd
 may be used again.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_result
 Obtain the result of a parse.
 Only successful parses
@@ -350,10 +421,22 @@ returned less than MANDOCLEVEL_FATAL
 .Pc
 should invoke this function, in which case one of the two pointers will
 be filled in.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_strerror
 Return a statically-allocated string representation of an error code.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_strlevel
 Return a statically-allocated string representation of a level code.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .El
 .Ss Variables
 .Bl -ohang
@@ -596,5 +679,4 @@ levels of badly-nested blocks.
 The
 .Nm
 library was written by
-.An Kristaps Dzonsons ,
-.Mt kristaps@bsd.lv .
+.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
diff --git a/contrib/mdocml/mandoc.c b/contrib/mdocml/mandoc.c
index 604bb67e6ae..da738f20fa7 100644
--- a/contrib/mdocml/mandoc.c
+++ b/contrib/mdocml/mandoc.c
@@ -1,7 +1,7 @@
-/*	$Id: mandoc.c,v 1.62 2011/12/03 16:08:51 schwarze Exp $ */
+/*	$Id: mandoc.c,v 1.74 2013/12/30 18:30:32 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2011 Ingo Schwarze 
+ * Copyright (c) 2011, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -37,83 +37,38 @@
 
 static	int	 a2time(time_t *, const char *, const char *);
 static	char	*time2a(time_t);
-static	int	 numescape(const char *);
 
-/*
- * Pass over recursive numerical expressions.  This context of this
- * function is important: it's only called within character-terminating
- * escapes (e.g., \s[xxxyyy]), so all we need to do is handle initial
- * recursion: we don't care about what's in these blocks. 
- * This returns the number of characters skipped or -1 if an error
- * occurs (the caller should bail).
- */
-static int
-numescape(const char *start)
-{
-	int		 i;
-	size_t		 sz;
-	const char	*cp;
-
-	i = 0;
-
-	/* The expression consists of a subexpression. */
-
-	if ('\\' == start[i]) {
-		cp = &start[++i];
-		/*
-		 * Read past the end of the subexpression.
-		 * Bail immediately on errors.
-		 */
-		if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
-			return(-1);
-		return(i + cp - &start[i]);
-	} 
-
-	if ('(' != start[i++])
-		return(0);
-
-	/*
-	 * A parenthesised subexpression.  Read until the closing
-	 * parenthesis, making sure to handle any nested subexpressions
-	 * that might ruin our parse.
-	 */
-
-	while (')' != start[i]) {
-		sz = strcspn(&start[i], ")\\");
-		i += (int)sz;
-
-		if ('\0' == start[i])
-			return(-1);
-		else if ('\\' != start[i])
-			continue;
-
-		cp = &start[++i];
-		if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
-			return(-1);
-		i += cp - &start[i];
-	}
-
-	/* Read past the terminating ')'. */
-	return(++i);
-}
 
 enum mandoc_esc
 mandoc_escape(const char **end, const char **start, int *sz)
 {
-	char		 c, term, numeric;
-	int		 i, lim, ssz, rlim;
-	const char	*cp, *rstart;
+	const char	*local_start;
+	int		 local_sz;
+	char		 term;
 	enum mandoc_esc	 gly; 
 
-	cp = *end;
-	rstart = cp;
-	if (start)
-		*start = rstart;
-	i = lim = 0;
-	gly = ESCAPE_ERROR;
-	term = numeric = '\0';
+	/*
+	 * When the caller doesn't provide return storage,
+	 * use local storage.
+	 */
 
-	switch ((c = cp[i++])) {
+	if (NULL == start)
+		start = &local_start;
+	if (NULL == sz)
+		sz = &local_sz;
+
+	/*
+	 * Beyond the backslash, at least one input character
+	 * is part of the escape sequence.  With one exception
+	 * (see below), that character won't be returned.
+	 */
+
+	gly = ESCAPE_ERROR;
+	*start = ++*end;
+	*sz = 0;
+	term = '\0';
+
+	switch ((*start)[-1]) {
 	/*
 	 * First the glyphs.  There are several different forms of
 	 * these, but each eventually returns a substring of the glyph
@@ -121,7 +76,7 @@ mandoc_escape(const char **end, const char **start, int *sz)
 	 */
 	case ('('):
 		gly = ESCAPE_SPECIAL;
-		lim = 2;
+		*sz = 2;
 		break;
 	case ('['):
 		gly = ESCAPE_SPECIAL;
@@ -131,17 +86,38 @@ mandoc_escape(const char **end, const char **start, int *sz)
 		 * Unicode codepoint.  Here, however, only check whether
 		 * it's not a zero-width escape.
 		 */
-		if ('u' == cp[i] && ']' != cp[i + 1])
+		if ('u' == (*start)[0] && ']' != (*start)[1])
 			gly = ESCAPE_UNICODE;
 		term = ']';
 		break;
 	case ('C'):
-		if ('\'' != cp[i])
+		if ('\'' != **start)
 			return(ESCAPE_ERROR);
-		gly = ESCAPE_SPECIAL;
+		*start = ++*end;
+		if ('u' == (*start)[0] && '\'' != (*start)[1])
+			gly = ESCAPE_UNICODE;
+		else
+			gly = ESCAPE_SPECIAL;
 		term = '\'';
 		break;
 
+	/*
+	 * Escapes taking no arguments at all.
+	 */
+	case ('d'):
+		/* FALLTHROUGH */
+	case ('u'):
+		return(ESCAPE_IGNORE);
+
+	/*
+	 * The \z escape is supposed to output the following
+	 * character without advancing the cursor position.  
+	 * Since we are mostly dealing with terminal mode,
+	 * let us just skip the next character.
+	 */
+	case ('z'):
+		return(ESCAPE_SKIPCHAR);
+
 	/*
 	 * Handle all triggers matching \X(xy, \Xx, and \X[xxxx], where
 	 * 'X' is the trigger.  These have opaque sub-strings.
@@ -166,21 +142,17 @@ mandoc_escape(const char **end, const char **start, int *sz)
 	case ('f'):
 		if (ESCAPE_ERROR == gly)
 			gly = ESCAPE_FONT;
-
-		rstart= &cp[i];
-		if (start) 
-			*start = rstart;
-
-		switch (cp[i++]) {
+		switch (**start) {
 		case ('('):
-			lim = 2;
+			*start = ++*end;
+			*sz = 2;
 			break;
 		case ('['):
+			*start = ++*end;
 			term = ']';
 			break;
 		default:
-			lim = 1;
-			i--;
+			*sz = 1;
 			break;
 		}
 		break;
@@ -193,18 +165,23 @@ mandoc_escape(const char **end, const char **start, int *sz)
 		/* FALLTHROUGH */
 	case ('b'):
 		/* FALLTHROUGH */
+	case ('B'):
+		/* FALLTHROUGH */
 	case ('D'):
 		/* FALLTHROUGH */
 	case ('o'):
 		/* FALLTHROUGH */
 	case ('R'):
 		/* FALLTHROUGH */
+	case ('w'):
+		/* FALLTHROUGH */
 	case ('X'):
 		/* FALLTHROUGH */
 	case ('Z'):
-		if ('\'' != cp[i++])
+		if ('\'' != **start)
 			return(ESCAPE_ERROR);
 		gly = ESCAPE_IGNORE;
+		*start = ++*end;
 		term = '\'';
 		break;
 
@@ -212,8 +189,6 @@ mandoc_escape(const char **end, const char **start, int *sz)
 	 * These escapes are of the form \X'N', where 'X' is the trigger
 	 * and 'N' resolves to a numerical expression.
 	 */
-	case ('B'):
-		/* FALLTHROUGH */
 	case ('h'):
 		/* FALLTHROUGH */
 	case ('H'):
@@ -221,20 +196,17 @@ mandoc_escape(const char **end, const char **start, int *sz)
 	case ('L'):
 		/* FALLTHROUGH */
 	case ('l'):
-		gly = ESCAPE_NUMBERED;
 		/* FALLTHROUGH */
 	case ('S'):
 		/* FALLTHROUGH */
 	case ('v'):
 		/* FALLTHROUGH */
-	case ('w'):
-		/* FALLTHROUGH */
 	case ('x'):
-		if (ESCAPE_ERROR == gly)
-			gly = ESCAPE_IGNORE;
-		if ('\'' != cp[i++])
+		if ('\'' != **start)
 			return(ESCAPE_ERROR);
-		term = numeric = '\'';
+		gly = ESCAPE_IGNORE;
+		*start = ++*end;
+		term = '\'';
 		break;
 
 	/*
@@ -242,17 +214,17 @@ mandoc_escape(const char **end, const char **start, int *sz)
 	 * XXX Do any other escapes need similar handling?
 	 */
 	case ('N'):
-		if ('\0' == cp[i])
+		if ('\0' == **start)
 			return(ESCAPE_ERROR);
-		*end = &cp[++i];
-		if (isdigit((unsigned char)cp[i-1]))
+		(*end)++;
+		if (isdigit((unsigned char)**start)) {
+			*sz = 1;
 			return(ESCAPE_IGNORE);
+		}
+		(*start)++;
 		while (isdigit((unsigned char)**end))
 			(*end)++;
-		if (start)
-			*start = &cp[i];
-		if (sz)
-			*sz = *end - &cp[i];
+		*sz = *end - *start;
 		if ('\0' != **end)
 			(*end)++;
 		return(ESCAPE_NUMBERED);
@@ -263,122 +235,93 @@ mandoc_escape(const char **end, const char **start, int *sz)
 	case ('s'):
 		gly = ESCAPE_IGNORE;
 
-		rstart = &cp[i];
-		if (start) 
-			*start = rstart;
-
 		/* See +/- counts as a sign. */
-		c = cp[i];
-		if ('+' == c || '-' == c || ASCII_HYPH == c)
-			++i;
+		if ('+' == **end || '-' == **end || ASCII_HYPH == **end)
+			(*end)++;
 
-		switch (cp[i++]) {
+		switch (**end) {
 		case ('('):
-			lim = 2;
+			*start = ++*end;
+			*sz = 2;
 			break;
 		case ('['):
-			term = numeric = ']';
+			*start = ++*end;
+			term = ']';
 			break;
 		case ('\''):
-			term = numeric = '\'';
+			*start = ++*end;
+			term = '\'';
 			break;
 		default:
-			lim = 1;
-			i--;
+			*sz = 1;
 			break;
 		}
 
-		/* See +/- counts as a sign. */
-		c = cp[i];
-		if ('+' == c || '-' == c || ASCII_HYPH == c)
-			++i;
-
 		break;
 
 	/*
 	 * Anything else is assumed to be a glyph.
+	 * In this case, pass back the character after the backslash.
 	 */
 	default:
 		gly = ESCAPE_SPECIAL;
-		lim = 1;
-		i--;
+		*start = --*end;
+		*sz = 1;
 		break;
 	}
 
 	assert(ESCAPE_ERROR != gly);
 
-	rstart = &cp[i];
-	if (start)
-		*start = rstart;
-
 	/*
-	 * If a terminating block has been specified, we need to
-	 * handle the case of recursion, which could have their
-	 * own terminating blocks that mess up our parse.  This, by the
-	 * way, means that the "start" and "size" values will be
-	 * effectively meaningless.
-	 */
-
-	ssz = 0;
-	if (numeric && -1 == (ssz = numescape(&cp[i])))
-		return(ESCAPE_ERROR);
-
-	i += ssz;
-	rlim = -1;
-
-	/*
-	 * We have a character terminator.  Try to read up to that
-	 * character.  If we can't (i.e., we hit the nil), then return
-	 * an error; if we can, calculate our length, read past the
-	 * terminating character, and exit.
+	 * Read up to the terminating character,
+	 * paying attention to nested escapes.
 	 */
 
 	if ('\0' != term) {
-		*end = strchr(&cp[i], term);
-		if ('\0' == *end)
+		while (**end != term) {
+			switch (**end) {
+			case ('\0'):
+				return(ESCAPE_ERROR);
+			case ('\\'):
+				(*end)++;
+				if (ESCAPE_ERROR ==
+				    mandoc_escape(end, NULL, NULL))
+					return(ESCAPE_ERROR);
+				break;
+			default:
+				(*end)++;
+				break;
+			}
+		}
+		*sz = (*end)++ - *start;
+	} else {
+		assert(*sz > 0);
+		if ((size_t)*sz > strlen(*start))
 			return(ESCAPE_ERROR);
-
-		rlim = *end - &cp[i];
-		if (sz)
-			*sz = rlim;
-		(*end)++;
-		goto out;
+		*end += *sz;
 	}
 
-	assert(lim > 0);
-
-	/*
-	 * We have a numeric limit.  If the string is shorter than that,
-	 * stop and return an error.  Else adjust our endpoint, length,
-	 * and return the current glyph.
-	 */
-
-	if ((size_t)lim > strlen(&cp[i]))
-		return(ESCAPE_ERROR);
-
-	rlim = lim;
-	if (sz)
-		*sz = rlim;
-
-	*end = &cp[i] + lim;
-
-out:
-	assert(rlim >= 0 && rstart);
-
 	/* Run post-processors. */
 
 	switch (gly) {
 	case (ESCAPE_FONT):
-		/*
-		 * Pretend that the constant-width font modes are the
-		 * same as the regular font modes.
-		 */
-		if (2 == rlim && 'C' == *rstart)
-			rstart++;
-		else if (1 != rlim)
+		if (2 == *sz) {
+			if ('C' == **start) {
+				/*
+				 * Treat constant-width font modes
+				 * just like regular font modes.
+				 */
+				(*start)++;
+				(*sz)--;
+			} else {
+				if ('B' == (*start)[0] && 'I' == (*start)[1])
+					gly = ESCAPE_FONTBI;
+				break;
+			}
+		} else if (1 != *sz)
 			break;
 
-		switch (*rstart) {
+		switch (**start) {
 		case ('3'):
 			/* FALLTHROUGH */
 		case ('B'):
@@ -400,9 +343,7 @@ out:
 		}
 		break;
 	case (ESCAPE_SPECIAL):
-		if (1 != rlim)
-			break;
-		if ('c' == *rstart)
+		if (1 == *sz && 'c' == **start)
 			gly = ESCAPE_NOSPACE;
 		break;
 	default:
@@ -484,10 +425,10 @@ mandoc_strdup(const char *ptr)
  * Parse a quoted or unquoted roff-style request or macro argument.
  * Return a pointer to the parsed argument, which is either the original
  * pointer or advanced by one byte in case the argument is quoted.
- * Null-terminate the argument in place.
+ * NUL-terminate the argument in place.
  * Collapse pairs of quotes inside quoted arguments.
  * Advance the argument pointer to the next argument,
- * or to the null byte terminating the argument line.
+ * or to the NUL byte terminating the argument line.
  */
 char *
 mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
@@ -506,17 +447,35 @@ mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
 	pairs = 0;
 	white = 0;
 	for (cp = start; '\0' != *cp; cp++) {
-		/* Move left after quoted quotes and escaped backslashes. */
+
+		/*
+		 * Move the following text left
+		 * after quoted quotes and after "\\" and "\t".
+		 */
 		if (pairs)
 			cp[-pairs] = cp[0];
+
 		if ('\\' == cp[0]) {
-			if ('\\' == cp[1]) {
-				/* Poor man's copy mode. */
+			/*
+			 * In copy mode, translate double to single
+			 * backslashes and backslash-t to literal tabs.
+			 */
+			switch (cp[1]) {
+			case ('t'):
+				cp[0] = '\t';
+				/* FALLTHROUGH */
+			case ('\\'):
 				pairs++;
 				cp++;
-			} else if (0 == quoted && ' ' == cp[1])
+				break;
+			case (' '):
 				/* Skip escaped blanks. */
-				cp++;
+				if (0 == quoted)
+					cp++;
+				break;
+			default:
+				break;
+			}
 		} else if (0 == quoted) {
 			if (' ' == cp[0]) {
 				/* Unescaped blanks end unquoted args. */
@@ -540,7 +499,7 @@ mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
 	if (1 == quoted)
 		mandoc_msg(MANDOCERR_BADQUOTE, parse, ln, *pos, NULL);
 
-	/* Null-terminate this argument and move to the next one. */
+	/* NUL-terminate this argument and move to the next one. */
 	if (pairs)
 		cp[-pairs] = '\0';
 	if ('\0' != *cp) {
@@ -677,32 +636,6 @@ mandoc_eos(const char *p, size_t sz, int enclosed)
 	return(found && !enclosed);
 }
 
-/*
- * Find out whether a line is a macro line or not.  If it is, adjust the
- * current position and return one; if it isn't, return zero and don't
- * change the current position.
- */
-int
-mandoc_getcontrol(const char *cp, int *ppos)
-{
-	int		pos;
-
-	pos = *ppos;
-
-	if ('\\' == cp[pos] && '.' == cp[pos + 1])
-		pos += 2;
-	else if ('.' == cp[pos] || '\'' == cp[pos])
-		pos++;
-	else
-		return(0);
-
-	while (' ' == cp[pos] || '\t' == cp[pos])
-		pos++;
-
-	*ppos = pos;
-	return(1);
-}
-
 /*
  * Convert a string to a long that may not be <0.
  * If the string is invalid, or is less than 0, return -1.
diff --git a/contrib/mdocml/mandoc.h b/contrib/mdocml/mandoc.h
index a37effc5f58..4c6a32f7a62 100644
--- a/contrib/mdocml/mandoc.h
+++ b/contrib/mdocml/mandoc.h
@@ -1,6 +1,7 @@
-/*	$Id: mandoc.h,v 1.99 2012/02/16 20:51:31 joerg Exp $ */
+/*	$Id: mandoc.h,v 1.112 2013/12/30 18:30:32 schwarze Exp $ */
 /*
  * Copyright (c) 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -50,6 +51,7 @@ enum	mandocerr {
 	MANDOCERR_NOTITLE, /* no title in document */
 	MANDOCERR_UPPERCASE, /* document title should be all caps */
 	MANDOCERR_BADMSEC, /* unknown manual section */
+	MANDOCERR_BADVOLARCH, /* unknown manual volume or arch */
 	MANDOCERR_NODATE, /* date missing, using today's date */
 	MANDOCERR_BADDATE, /* cannot parse date, using it verbatim */
 	MANDOCERR_PROLOGOOO, /* prologue macros out of order */
@@ -61,14 +63,14 @@ enum	mandocerr {
 	MANDOCERR_SO, /* .so is fragile, better use ln(1) */
 	MANDOCERR_NAMESECFIRST, /* NAME section must come first */
 	MANDOCERR_BADNAMESEC, /* bad NAME section contents */
-	MANDOCERR_NONAME, /* manual name not yet set */
 	MANDOCERR_SECOOO, /* sections out of conventional order */
 	MANDOCERR_SECREP, /* duplicate section name */
-	MANDOCERR_SECMSEC, /* section not in conventional manual section */
+	MANDOCERR_SECMSEC, /* section header suited to sections ... */
 
 	/* related to macros and nesting */
 	MANDOCERR_MACROOBS, /* skipping obsolete macro */
 	MANDOCERR_IGNPAR, /* skipping paragraph macro */
+	MANDOCERR_MOVEPAR, /* moving paragraph macro out of list */
 	MANDOCERR_IGNNS, /* skipping no-space macro */
 	MANDOCERR_SCOPENEST, /* blocks badly nested */
 	MANDOCERR_CHILD, /* child violates parent syntax */
@@ -129,10 +131,12 @@ enum	mandocerr {
 	MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */
 	MANDOCERR_BADCHAR, /* skipping bad character */
 	MANDOCERR_NAMESC, /* escaped character not allowed in a name */
+	MANDOCERR_NONAME, /* manual name not yet set */
 	MANDOCERR_NOTEXT, /* skipping text before the first section header */
 	MANDOCERR_MACRO, /* skipping unknown macro */
 	MANDOCERR_REQUEST, /* NOT IMPLEMENTED: skipping request */
 	MANDOCERR_ARGCOUNT, /* argument count wrong */
+	MANDOCERR_STRAYTA, /* skipping column outside column list */
 	MANDOCERR_NOSCOPE, /* skipping end of block that is not open */
 	MANDOCERR_SCOPEBROKEN, /* missing end of block */
 	MANDOCERR_SCOPEEXIT, /* scope open on exit */
@@ -141,6 +145,7 @@ enum	mandocerr {
 	MANDOCERR_NOARGS, /* macro requires line argument(s) */
 	MANDOCERR_NOBODY, /* macro requires body argument(s) */
 	MANDOCERR_NOARGV, /* macro requires argument(s) */
+	MANDOCERR_NUMERIC, /* request requires a numeric argument */
 	MANDOCERR_LISTTYPE, /* missing list type */
 	MANDOCERR_ARGSLOST, /* line argument(s) will be lost */
 	MANDOCERR_BODYLOST, /* body argument(s) will be lost */
@@ -160,7 +165,7 @@ enum	mandocerr {
 	MANDOCERR_MAX
 };
 
-struct	tbl {
+struct	tbl_opts {
 	char		  tab; /* cell-separator */
 	char		  decimal; /* decimal point */
 	int		  linesize;
@@ -175,20 +180,14 @@ struct	tbl {
 	int		  cols; /* number of columns */
 };
 
-enum	tbl_headt {
-	TBL_HEAD_DATA, /* plug in data from tbl_dat */
-	TBL_HEAD_VERT, /* vertical spacer */
-	TBL_HEAD_DVERT  /* double-vertical spacer */
-};
-
 /*
  * The head of a table specifies all of its columns.  When formatting a
  * tbl_span, iterate over these and plug in data from the tbl_span when
  * appropriate, using tbl_cell as a guide to placement.
  */
 struct	tbl_head {
-	enum tbl_headt	  pos;
 	int		  ident; /* 0 <= unique id < cols */
+	int		  vert; /* width of preceding vertical line */
 	struct tbl_head	 *next;
 	struct tbl_head	 *prev;
 };
@@ -203,8 +202,6 @@ enum	tbl_cellt {
 	TBL_CELL_DOWN, /* ^ */
 	TBL_CELL_HORIZ, /* _, - */
 	TBL_CELL_DHORIZ, /* = */
-	TBL_CELL_VERT, /* | */
-	TBL_CELL_DVERT, /* || */
 	TBL_CELL_MAX
 };
 
@@ -213,6 +210,7 @@ enum	tbl_cellt {
  */
 struct	tbl_cell {
 	struct tbl_cell	 *next;
+	int		  vert; /* width of preceding vertical line */
 	enum tbl_cellt	  pos;
 	size_t		  spacing;
 	int		  flags;
@@ -266,7 +264,7 @@ enum	tbl_spant {
  * A row of data in a table.
  */
 struct	tbl_span {
-	struct tbl	 *tbl;
+	struct tbl_opts	 *opts;
 	struct tbl_head	 *head;
 	struct tbl_row	 *layout; /* layout row */
 	struct tbl_dat	 *first;
@@ -382,11 +380,13 @@ enum	mandoc_esc {
 	ESCAPE_FONT, /* a generic font mode */
 	ESCAPE_FONTBOLD, /* bold font mode */
 	ESCAPE_FONTITALIC, /* italic font mode */
+	ESCAPE_FONTBI, /* bold italic font mode */
 	ESCAPE_FONTROMAN, /* roman font mode */
 	ESCAPE_FONTPREV, /* previous font mode */
 	ESCAPE_NUMBERED, /* a numbered glyph */
 	ESCAPE_UNICODE, /* a unicode codepoint */
-	ESCAPE_NOSPACE /* suppress space if the last on a line */
+	ESCAPE_NOSPACE, /* suppress space if the last on a line */
+	ESCAPE_SKIPCHAR /* skip the next character */
 };
 
 typedef	void	(*mandocmsg)(enum mandocerr, enum mandoclevel,
@@ -413,8 +413,8 @@ int		  mchars_spec2cp(const struct mchars *,
 			const char *, size_t);
 const char	 *mchars_spec2str(const struct mchars *, 
 			const char *, size_t, size_t *);
-struct mparse	 *mparse_alloc(enum mparset, 
-			enum mandoclevel, mandocmsg, void *);
+struct mparse	 *mparse_alloc(enum mparset, enum mandoclevel,
+			mandocmsg, void *, char *);
 void		  mparse_free(struct mparse *);
 void		  mparse_keep(struct mparse *);
 enum mandoclevel  mparse_readfd(struct mparse *, int, const char *);
diff --git a/contrib/mdocml/mandoc_char.7 b/contrib/mdocml/mandoc_char.7
index acc1b6100d4..8d8356652ef 100644
--- a/contrib/mdocml/mandoc_char.7
+++ b/contrib/mdocml/mandoc_char.7
@@ -1,4 +1,4 @@
-.\"	$Id: mandoc_char.7,v 1.51 2011/11/23 10:09:30 kristaps Exp $
+.\"	$Id: mandoc_char.7,v 1.56 2013/12/26 17:23:42 schwarze Exp $
 .\"
 .\" Copyright (c) 2003 Jason McIntyre 
 .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
@@ -16,7 +16,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: November 23 2011 $
+.Dd $Mdocdate: December 26 2013 $
 .Dt MANDOC_CHAR 7
 .Os
 .Sh NAME
@@ -98,26 +98,27 @@ in literal context, and when none of the following special cases apply,
 just use the normal space character
 .Pq Sq \  .
 .Pp
-When filling text, lines may be broken between words, i.e. at space
+When filling text, output lines may be broken between words, i.e. at space
 characters.
 To prevent a line break between two particular words,
-use the non-breaking space escape sequence
-.Pq Sq \e~
+use the unpaddable non-breaking space escape sequence
+.Pq Sq \e\ \&
 instead of the normal space character.
 For example, the input string
-.Dq number\e~1
+.Dq number\e\ 1
 will be kept together as
-.Dq number\~1
+.Dq number\ 1
 on the same output line.
 .Pp
 On request and macro lines, the normal space character serves as an
 argument delimiter.
-To include whitespace into arguments, quoting is usually the best choice.
-In some cases, using either the non-breaking
-.Pq Sq \e~
-or the breaking
+To include whitespace into arguments, quoting is usually the best choice;
+see the MACRO SYNTAX section in
+.Xr roff 7 .
+In some cases, using the non-breaking space escape sequence
 .Pq Sq \e\ \&
-space escape sequence may be preferable.
+may be preferable.
+.Pp
 To escape macro names and to protect whitespace at the end
 of input lines, the zero-width space
 .Pq Sq \e&
@@ -194,14 +195,13 @@ manual.
 Spacing:
 .Bl -column "Input" "Description" -offset indent -compact
 .It Em Input Ta Em Description
-.It \e~      Ta non-breaking, non-collapsing space
-.It \e       Ta breaking, non-collapsing n-width space
-.It \e^      Ta zero-width space
-.It \e%      Ta zero-width space
+.It Sq \e\ \& Ta unpaddable non-breaking space
+.It \e~      Ta paddable non-breaking space
+.It \e0      Ta unpaddable, breaking digit-width space
+.It \e|      Ta one-sixth \e(em narrow space, zero width in nroff mode
+.It \e^      Ta one-twelfth \e(em half-narrow space, zero width in nroff
 .It \e&      Ta zero-width space
-.It \e|      Ta zero-width space
-.It \e0      Ta breaking, non-collapsing digit-width space
-.It \ec      Ta removes any trailing space (if applicable)
+.It \e%      Ta zero-width space allowing hyphenation
 .El
 .Pp
 Lines:
@@ -655,13 +655,18 @@ manual.
 .It \e*(Ai   Ta \*(Ai       Ta ANSI standard name
 .El
 .Sh UNICODE CHARACTERS
-The escape sequence
+The escape sequences
 .Pp
-.Dl \e[uXXXX]
+.Dl \e[uXXXX] and \eC'uXXXX'
 .Pp
-is interpreted as a Unicode codepoint.
+are interpreted as Unicode codepoints.
 The codepoint must be in the range above U+0080 and less than U+10FFFF.
-For compatibility, points must be zero-padded to four characters; if
+For compatibility, the hexadecimal digits
+.Sq A
+to
+.Sq F
+must be given as uppercase characters,
+and points must be zero-padded to four characters; if
 greater than four characters, no zero padding is allowed.
 Unicode surrogates are not allowed.
 .\" .Pp
@@ -684,7 +689,7 @@ For example, do not use \eN'34', use \e(dq, or even the plain
 .Sq \(dq
 character where possible.
 .Sh COMPATIBILITY
-This section documents compatibility between mandoc and other other
+This section documents compatibility between mandoc and other
 troff implementations, at this time limited to GNU troff
 .Pq Qq groff .
 .Pp
@@ -728,12 +733,11 @@ known representation.
 The
 .Nm
 manual page was written by
-.An Kristaps Dzonsons ,
-.Mt kristaps@bsd.lv .
+.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
 .Sh CAVEATS
-The
+The predefined string
 .Sq \e*(Ba
-escape mimics the behaviour of the
+mimics the behaviour of the
 .Sq \&|
 character in
 .Xr mdoc 7 ;
diff --git a/contrib/mdocml/mdoc.7 b/contrib/mdocml/mdoc.7
index 44d927b56b8..165c76b9500 100644
--- a/contrib/mdocml/mdoc.7
+++ b/contrib/mdocml/mdoc.7
@@ -1,7 +1,7 @@
-.\"	$Id: mdoc.7,v 1.214 2012/01/03 10:18:05 kristaps Exp $
+.\"	$Id: mdoc.7,v 1.223 2013/12/25 14:09:32 schwarze Exp $
 .\"
 .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
-.\" Copyright (c) 2010, 2011 Ingo Schwarze 
+.\" Copyright (c) 2010, 2011, 2013 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: January 3 2012 $
+.Dd $Mdocdate: December 25 2013 $
 .Dt MDOC 7
 .Os
 .Sh NAME
@@ -477,6 +477,7 @@ in the alphabetical
 .Bl -column "Brq, Bro, Brc" description
 .It Sx \&Lb Ta function library (one argument)
 .It Sx \&In Ta include file (one argument)
+.It Sx \&Fd Ta other preprocessor directive (>0 arguments)
 .It Sx \&Ft Ta function type (>0 arguments)
 .It Sx \&Fo , \&Fc Ta function block: Ar funcname
 .It Sx \&Fn Ta function name:
@@ -657,7 +658,7 @@ for all other author listings.
 .Pp
 Examples:
 .Dl \&.An -nosplit
-.Dl \&.An Kristaps Dzonsons \&Aq kristaps@bsd.lv
+.Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv
 .Ss \&Ao
 Begin a block enclosed by angle brackets.
 Does not have any head arguments.
@@ -711,7 +712,9 @@ for fixed strings to be passed verbatim as arguments, use
 or
 .Sx \&Cm .
 .Ss \&At
-Formats an AT&T version.
+Formats an
+.At
+version.
 Accepts one optional argument:
 .Pp
 .Bl -tag -width "v[1-7] | 32vX" -offset indent -compact
@@ -820,8 +823,8 @@ The most popular is the imaginary macro
 which resolves to
 .Sy 6n .
 .It
-A width using the syntax described in
-.Sx Scaling Widths .
+A scaling width as described in
+.Xr roff 7 .
 .It
 An arbitrary string, which indents by the length of this string.
 .El
@@ -926,8 +929,8 @@ The
 .Fl width
 and
 .Fl offset
-arguments accept
-.Sx Scaling Widths
+arguments accept scaling widths as described in
+.Xr roff 7
 or use the length of the given string.
 The
 .Fl offset
@@ -956,9 +959,9 @@ A columnated list.
 The
 .Fl width
 argument has no effect; instead, each argument specifies the width
-of one column, using either the
-.Sx Scaling Widths
-syntax or the string length of the argument.
+of one column, using either the scaling width syntax described in
+.Xr roff 7
+or the string length of the argument.
 If the first line of the body of a
 .Fl column
 list is not an
@@ -1089,7 +1092,9 @@ Examples:
 See also
 .Sx \&Bro .
 .Ss \&Bsx
-Format the BSD/OS version provided as an argument, or a default value if
+Format the
+.Bsx
+version provided as an argument, or a default value if
 no argument is provided.
 .Pp
 Examples:
@@ -1109,7 +1114,9 @@ and
 Prints
 .Dq is currently in beta test.
 .Ss \&Bx
-Format the BSD version provided as an argument, or a default value if no
+Format the
+.Bx
+version provided as an argument, or a default value if no
 argument is provided.
 .Pp
 Examples:
@@ -1401,11 +1408,16 @@ See also
 .Sx \&Er
 and
 .Sx \&Ev
-for special-purpose constants and
+for special-purpose constants,
 .Sx \&Va
-for variable symbols.
+for variable symbols, and
+.Sx \&Fd
+for listing preprocessor variable definitions in the
+.Em SYNOPSIS .
 .Ss \&Dx
-Format the DragonFly BSD version provided as an argument, or a default
+Format the
+.Dx
+version provided as an argument, or a default
 value if no argument is provided.
 .Pp
 Examples:
@@ -1562,15 +1574,32 @@ See also
 End a function context started by
 .Sx \&Fo .
 .Ss \&Fd
-Historically used to document include files.
-This usage has been deprecated in favour of
+Preprocessor directive, in particular for listing it in the
+.Em SYNOPSIS .
+Historically, it was also used to document include files.
+The latter usage has been deprecated in favour of
 .Sx \&In .
-Do not use this macro.
+.Pp
+Its syntax is as follows:
+.Bd -ragged -offset indent
+.Pf \. Sx \&Fd
+.Li # Ns Ar directive
+.Op Ar argument ...
+.Ed
+.Pp
+Examples:
+.Dl \&.Fd #define sa_handler __sigaction_u.__sa_handler
+.Dl \&.Fd #define SIO_MAXNFDS
+.Dl \&.Fd #ifdef FS_DEBUG
+.Dl \&.Ft void
+.Dl \&.Fn dbg_open \(dqconst char *\(dq
+.Dl \&.Fd #endif
 .Pp
 See also
-.Sx MANUAL STRUCTURE
+.Sx MANUAL STRUCTURE ,
+.Sx \&In ,
 and
-.Sx \&In .
+.Sx \&Dv .
 .Ss \&Fl
 Command-line flag or option.
 Used when listing arguments to command-line utilities.
@@ -1851,7 +1880,7 @@ section as described in
 .Pp
 Examples:
 .Dl \&.Lb libz
-.Dl \&.Lb mdoc
+.Dl \&.Lb libmandoc
 .Ss \&Li
 Denotes text that should be in a
 .Li literal
@@ -1902,6 +1931,7 @@ Its syntax is as follows:
 .Pp
 Examples:
 .Dl \&.Mt discuss@manpages.bsd.lv
+.Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv
 .Ss \&Nd
 A one line description of the manual's content.
 This may only be invoked in the
@@ -2083,7 +2113,7 @@ This macro is obsolete and not implemented in
 .Xr mandoc 1 .
 .Pp
 Historical
-.Xr mdoc 7
+.Nm
 packages described it as
 .Dq "old function type (FORTRAN)" .
 .Ss \&Ox
@@ -2347,20 +2377,26 @@ The following standards are recognised:
 .St -p1003.1b-93
 .It \-p1003.1c-95
 .St -p1003.1c-95
+.It \-p1003.1d-99
+.St -p1003.1d-99
 .It \-p1003.1g-2000
 .St -p1003.1g-2000
 .It \-p1003.1i-95
 .St -p1003.1i-95
+.It \-p1003.1j-2000
+.St -p1003.1j-2000
+.It \-p1003.1q-2000
+.St -p1003.1q-2000
+.It \-p1003.2
+.St -p1003.2
 .It \-p1003.2-92
 .St -p1003.2-92
 .It \-p1003.2a-92
 .St -p1003.2a-92
-.It \-p1387.2-95
-.St -p1387.2-95
-.It \-p1003.2
-.St -p1003.2
 .It \-p1387.2
 .St -p1387.2
+.It \-p1387.2-95
+.St -p1387.2-95
 .It \-isoC
 .St -isoC
 .It \-isoC-90
@@ -2407,6 +2443,8 @@ The following standards are recognised:
 .St -xbd5
 .It \-xcu5
 .St -xcu5
+.It \-xsh4.2
+.St -xsh4.2
 .It \-xsh5
 .St -xsh5
 .It \-xns5
@@ -2467,7 +2505,9 @@ Examples:
 Prints out
 .Dq currently under development.
 .Ss \&Ux
-Format the UNIX name.
+Format the
+.Ux
+name.
 Accepts no argument.
 .Pp
 Examples:
@@ -2532,20 +2572,14 @@ Link to another manual
 .Pq Qq cross-reference .
 Its syntax is as follows:
 .Pp
-.D1 Pf \. Sx \&Xr Ar name section
+.D1 Pf \. Sx \&Xr Ar name Op section
 .Pp
-The
+Cross reference the
 .Ar name
 and
 .Ar section
-are the name and section of the linked manual.
-If
-.Ar section
-is followed by non-punctuation, an
-.Sx \&Ns
-is inserted into the token stream.
-This behaviour is for compatibility with
-GNU troff.
+number of another man page;
+omitting the section number is rarely useful.
 .Pp
 Examples:
 .Dl \&.Xr mandoc 1
@@ -2569,8 +2603,8 @@ Its syntax is as follows:
 .Pp
 The
 .Ar height
-argument must be formatted as described in
-.Sx Scaling Widths .
+argument is a scaling width as described in
+.Xr roff 7 .
 If unspecified,
 .Sx \&sp
 asserts a single vertical space.
@@ -2964,7 +2998,7 @@ Manually switching the font using the
 .Ql \ef
 font escape sequences is never required.
 .Sh COMPATIBILITY
-This section documents compatibility between mandoc and other other
+This section documents compatibility between mandoc and other
 troff implementations, at this time limited to GNU troff
 .Pq Qq groff .
 The term
@@ -3168,5 +3202,4 @@ utility written by Kristaps Dzonsons appeared in
 The
 .Nm
 reference was written by
-.An Kristaps Dzonsons ,
-.Mt kristaps@bsd.lv .
+.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
diff --git a/contrib/mdocml/mdoc.c b/contrib/mdocml/mdoc.c
index 81a4ffc96fd..87b358797b4 100644
--- a/contrib/mdocml/mdoc.c
+++ b/contrib/mdocml/mdoc.c
@@ -1,7 +1,7 @@
-/*	$Id: mdoc.c,v 1.196 2011/09/30 00:13:28 schwarze Exp $ */
+/*	$Id: mdoc.c,v 1.206 2013/12/24 19:11:46 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010 Ingo Schwarze 
+ * Copyright (c) 2010, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -104,20 +104,20 @@ static	int		  mdoc_ptext(struct mdoc *, int, char *, int);
 static	int		  mdoc_pmacro(struct mdoc *, int, char *, int);
 
 const struct mdoc_node *
-mdoc_node(const struct mdoc *m)
+mdoc_node(const struct mdoc *mdoc)
 {
 
-	assert( ! (MDOC_HALT & m->flags));
-	return(m->first);
+	assert( ! (MDOC_HALT & mdoc->flags));
+	return(mdoc->first);
 }
 
 
 const struct mdoc_meta *
-mdoc_meta(const struct mdoc *m)
+mdoc_meta(const struct mdoc *mdoc)
 {
 
-	assert( ! (MDOC_HALT & m->flags));
-	return(&m->meta);
+	assert( ! (MDOC_HALT & mdoc->flags));
+	return(&mdoc->meta);
 }
 
 
@@ -197,13 +197,14 @@ mdoc_free(struct mdoc *mdoc)
  * Allocate volatile and non-volatile parse resources.  
  */
 struct mdoc *
-mdoc_alloc(struct roff *roff, struct mparse *parse)
+mdoc_alloc(struct roff *roff, struct mparse *parse, char *defos)
 {
 	struct mdoc	*p;
 
 	p = mandoc_calloc(1, sizeof(struct mdoc));
 
 	p->parse = parse;
+	p->defos = defos;
 	p->roff = roff;
 
 	mdoc_hash_init();
@@ -217,61 +218,61 @@ mdoc_alloc(struct roff *roff, struct mparse *parse)
  * through to macro_end() in macro.c.
  */
 int
-mdoc_endparse(struct mdoc *m)
+mdoc_endparse(struct mdoc *mdoc)
 {
 
-	assert( ! (MDOC_HALT & m->flags));
-	if (mdoc_macroend(m))
+	assert( ! (MDOC_HALT & mdoc->flags));
+	if (mdoc_macroend(mdoc))
 		return(1);
-	m->flags |= MDOC_HALT;
+	mdoc->flags |= MDOC_HALT;
 	return(0);
 }
 
 int
-mdoc_addeqn(struct mdoc *m, const struct eqn *ep)
+mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep)
 {
 	struct mdoc_node *n;
 
-	assert( ! (MDOC_HALT & m->flags));
+	assert( ! (MDOC_HALT & mdoc->flags));
 
 	/* No text before an initial macro. */
 
-	if (SEC_NONE == m->lastnamed) {
-		mdoc_pmsg(m, ep->ln, ep->pos, MANDOCERR_NOTEXT);
+	if (SEC_NONE == mdoc->lastnamed) {
+		mdoc_pmsg(mdoc, ep->ln, ep->pos, MANDOCERR_NOTEXT);
 		return(1);
 	}
 
-	n = node_alloc(m, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
+	n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN);
 	n->eqn = ep;
 
-	if ( ! node_append(m, n))
+	if ( ! node_append(mdoc, n))
 		return(0);
 
-	m->next = MDOC_NEXT_SIBLING;
+	mdoc->next = MDOC_NEXT_SIBLING;
 	return(1);
 }
 
 int
-mdoc_addspan(struct mdoc *m, const struct tbl_span *sp)
+mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp)
 {
 	struct mdoc_node *n;
 
-	assert( ! (MDOC_HALT & m->flags));
+	assert( ! (MDOC_HALT & mdoc->flags));
 
 	/* No text before an initial macro. */
 
-	if (SEC_NONE == m->lastnamed) {
-		mdoc_pmsg(m, sp->line, 0, MANDOCERR_NOTEXT);
+	if (SEC_NONE == mdoc->lastnamed) {
+		mdoc_pmsg(mdoc, sp->line, 0, MANDOCERR_NOTEXT);
 		return(1);
 	}
 
-	n = node_alloc(m, sp->line, 0, MDOC_MAX, MDOC_TBL);
+	n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, MDOC_TBL);
 	n->span = sp;
 
-	if ( ! node_append(m, n))
+	if ( ! node_append(mdoc, n))
 		return(0);
 
-	m->next = MDOC_NEXT_SIBLING;
+	mdoc->next = MDOC_NEXT_SIBLING;
 	return(1);
 }
 
@@ -281,12 +282,12 @@ mdoc_addspan(struct mdoc *m, const struct tbl_span *sp)
  * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()).
  */
 int
-mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs)
+mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs)
 {
 
-	assert( ! (MDOC_HALT & m->flags));
+	assert( ! (MDOC_HALT & mdoc->flags));
 
-	m->flags |= MDOC_NEWLINE;
+	mdoc->flags |= MDOC_NEWLINE;
 
 	/*
 	 * Let the roff nS register switch SYNOPSIS mode early,
@@ -294,16 +295,14 @@ mdoc_parseln(struct mdoc *m, int ln, char *buf, int offs)
 	 * whether this mode is on or off.
 	 * Note that this mode is also switched by the Sh macro.
 	 */
-	if (roff_regisset(m->roff, REG_nS)) {
-		if (roff_regget(m->roff, REG_nS))
-			m->flags |= MDOC_SYNOPSIS;
-		else
-			m->flags &= ~MDOC_SYNOPSIS;
-	}
+	if (roff_getreg(mdoc->roff, "nS"))
+		mdoc->flags |= MDOC_SYNOPSIS;
+	else
+		mdoc->flags &= ~MDOC_SYNOPSIS;
 
-	return(mandoc_getcontrol(buf, &offs) ?
-			mdoc_pmacro(m, ln, buf, offs) :
-			mdoc_ptext(m, ln, buf, offs));
+	return(roff_getcontrol(mdoc->roff, buf, &offs) ?
+			mdoc_pmacro(mdoc, ln, buf, offs) :
+			mdoc_ptext(mdoc, ln, buf, offs));
 }
 
 int
@@ -314,31 +313,31 @@ mdoc_macro(MACRO_PROT_ARGS)
 	/* If we're in the body, deny prologue calls. */
 
 	if (MDOC_PROLOGUE & mdoc_macros[tok].flags && 
-			MDOC_PBODY & m->flags) {
-		mdoc_pmsg(m, line, ppos, MANDOCERR_BADBODY);
+			MDOC_PBODY & mdoc->flags) {
+		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_BADBODY);
 		return(1);
 	}
 
 	/* If we're in the prologue, deny "body" macros.  */
 
 	if ( ! (MDOC_PROLOGUE & mdoc_macros[tok].flags) && 
-			! (MDOC_PBODY & m->flags)) {
-		mdoc_pmsg(m, line, ppos, MANDOCERR_BADPROLOG);
-		if (NULL == m->meta.msec)
-			m->meta.msec = mandoc_strdup("1");
-		if (NULL == m->meta.title)
-			m->meta.title = mandoc_strdup("UNKNOWN");
-		if (NULL == m->meta.vol)
-			m->meta.vol = mandoc_strdup("LOCAL");
-		if (NULL == m->meta.os)
-			m->meta.os = mandoc_strdup("LOCAL");
-		if (NULL == m->meta.date)
-			m->meta.date = mandoc_normdate
-				(m->parse, NULL, line, ppos);
-		m->flags |= MDOC_PBODY;
+			! (MDOC_PBODY & mdoc->flags)) {
+		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_BADPROLOG);
+		if (NULL == mdoc->meta.msec)
+			mdoc->meta.msec = mandoc_strdup("1");
+		if (NULL == mdoc->meta.title)
+			mdoc->meta.title = mandoc_strdup("UNKNOWN");
+		if (NULL == mdoc->meta.vol)
+			mdoc->meta.vol = mandoc_strdup("LOCAL");
+		if (NULL == mdoc->meta.os)
+			mdoc->meta.os = mandoc_strdup("LOCAL");
+		if (NULL == mdoc->meta.date)
+			mdoc->meta.date = mandoc_normdate
+				(mdoc->parse, NULL, line, ppos);
+		mdoc->flags |= MDOC_PBODY;
 	}
 
-	return((*mdoc_macros[tok].fp)(m, tok, line, ppos, pos, buf));
+	return((*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf));
 }
 
 
@@ -374,6 +373,8 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
 
 	switch (p->type) {
 	case (MDOC_BODY):
+		if (ENDBODY_NOT != p->end)
+			break;
 		/* FALLTHROUGH */
 	case (MDOC_TAIL):
 		/* FALLTHROUGH */
@@ -424,97 +425,99 @@ node_append(struct mdoc *mdoc, struct mdoc_node *p)
 
 
 static struct mdoc_node *
-node_alloc(struct mdoc *m, int line, int pos, 
+node_alloc(struct mdoc *mdoc, int line, int pos, 
 		enum mdoct tok, enum mdoc_type type)
 {
 	struct mdoc_node *p;
 
 	p = mandoc_calloc(1, sizeof(struct mdoc_node));
-	p->sec = m->lastsec;
+	p->sec = mdoc->lastsec;
 	p->line = line;
 	p->pos = pos;
+	p->lastline = line;
 	p->tok = tok;
 	p->type = type;
 
 	/* Flag analysis. */
 
-	if (MDOC_SYNOPSIS & m->flags)
+	if (MDOC_SYNOPSIS & mdoc->flags)
 		p->flags |= MDOC_SYNPRETTY;
 	else
 		p->flags &= ~MDOC_SYNPRETTY;
-	if (MDOC_NEWLINE & m->flags)
+	if (MDOC_NEWLINE & mdoc->flags)
 		p->flags |= MDOC_LINE;
-	m->flags &= ~MDOC_NEWLINE;
+	mdoc->flags &= ~MDOC_NEWLINE;
 
 	return(p);
 }
 
 
 int
-mdoc_tail_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
+mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(m, line, pos, tok, MDOC_TAIL);
-	if ( ! node_append(m, p))
+	p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL);
+	if ( ! node_append(mdoc, p))
 		return(0);
-	m->next = MDOC_NEXT_CHILD;
+	mdoc->next = MDOC_NEXT_CHILD;
 	return(1);
 }
 
 
 int
-mdoc_head_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
+mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
 {
 	struct mdoc_node *p;
 
-	assert(m->first);
-	assert(m->last);
+	assert(mdoc->first);
+	assert(mdoc->last);
 
-	p = node_alloc(m, line, pos, tok, MDOC_HEAD);
-	if ( ! node_append(m, p))
+	p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD);
+	if ( ! node_append(mdoc, p))
 		return(0);
-	m->next = MDOC_NEXT_CHILD;
+	mdoc->next = MDOC_NEXT_CHILD;
 	return(1);
 }
 
 
 int
-mdoc_body_alloc(struct mdoc *m, int line, int pos, enum mdoct tok)
+mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(m, line, pos, tok, MDOC_BODY);
-	if ( ! node_append(m, p))
+	p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
+	if ( ! node_append(mdoc, p))
 		return(0);
-	m->next = MDOC_NEXT_CHILD;
+	mdoc->next = MDOC_NEXT_CHILD;
 	return(1);
 }
 
 
 int
-mdoc_endbody_alloc(struct mdoc *m, int line, int pos, enum mdoct tok,
+mdoc_endbody_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok,
 		struct mdoc_node *body, enum mdoc_endbody end)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(m, line, pos, tok, MDOC_BODY);
+	p = node_alloc(mdoc, line, pos, tok, MDOC_BODY);
 	p->pending = body;
+	p->norm = body->norm;
 	p->end = end;
-	if ( ! node_append(m, p))
+	if ( ! node_append(mdoc, p))
 		return(0);
-	m->next = MDOC_NEXT_SIBLING;
+	mdoc->next = MDOC_NEXT_SIBLING;
 	return(1);
 }
 
 
 int
-mdoc_block_alloc(struct mdoc *m, int line, int pos, 
+mdoc_block_alloc(struct mdoc *mdoc, int line, int pos, 
 		enum mdoct tok, struct mdoc_arg *args)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(m, line, pos, tok, MDOC_BLOCK);
+	p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK);
 	p->args = args;
 	if (p->args)
 		(args->refcnt)++;
@@ -533,20 +536,20 @@ mdoc_block_alloc(struct mdoc *m, int line, int pos,
 		break;
 	}
 
-	if ( ! node_append(m, p))
+	if ( ! node_append(mdoc, p))
 		return(0);
-	m->next = MDOC_NEXT_CHILD;
+	mdoc->next = MDOC_NEXT_CHILD;
 	return(1);
 }
 
 
 int
-mdoc_elem_alloc(struct mdoc *m, int line, int pos, 
+mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos, 
 		enum mdoct tok, struct mdoc_arg *args)
 {
 	struct mdoc_node *p;
 
-	p = node_alloc(m, line, pos, tok, MDOC_ELEM);
+	p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM);
 	p->args = args;
 	if (p->args)
 		(args->refcnt)++;
@@ -559,27 +562,44 @@ mdoc_elem_alloc(struct mdoc *m, int line, int pos,
 		break;
 	}
 
-	if ( ! node_append(m, p))
+	if ( ! node_append(mdoc, p))
 		return(0);
-	m->next = MDOC_NEXT_CHILD;
+	mdoc->next = MDOC_NEXT_CHILD;
 	return(1);
 }
 
 int
-mdoc_word_alloc(struct mdoc *m, int line, int pos, const char *p)
+mdoc_word_alloc(struct mdoc *mdoc, int line, int pos, const char *p)
 {
 	struct mdoc_node *n;
 
-	n = node_alloc(m, line, pos, MDOC_MAX, MDOC_TEXT);
-	n->string = roff_strdup(m->roff, p);
+	n = node_alloc(mdoc, line, pos, MDOC_MAX, MDOC_TEXT);
+	n->string = roff_strdup(mdoc->roff, p);
 
-	if ( ! node_append(m, n))
+	if ( ! node_append(mdoc, n))
 		return(0);
 
-	m->next = MDOC_NEXT_SIBLING;
+	mdoc->next = MDOC_NEXT_SIBLING;
 	return(1);
 }
 
+void
+mdoc_word_append(struct mdoc *mdoc, const char *p)
+{
+	struct mdoc_node	*n;
+	char			*addstr, *newstr;
+
+	n = mdoc->last;
+	addstr = roff_strdup(mdoc->roff, p);
+	if (-1 == asprintf(&newstr, "%s %s", n->string, addstr)) {
+		perror(NULL);
+		exit((int)MANDOCLEVEL_SYSERR);
+	}
+	free(addstr);
+	free(n->string);
+	n->string = newstr;
+	mdoc->next = MDOC_NEXT_SIBLING;
+}
 
 static void
 mdoc_node_free(struct mdoc_node *p)
@@ -596,7 +616,7 @@ mdoc_node_free(struct mdoc_node *p)
 
 
 static void
-mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n)
+mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n)
 {
 
 	/* Adjust siblings. */
@@ -618,35 +638,43 @@ mdoc_node_unlink(struct mdoc *m, struct mdoc_node *n)
 
 	/* Adjust parse point, if applicable. */
 
-	if (m && m->last == n) {
+	if (mdoc && mdoc->last == n) {
 		if (n->prev) {
-			m->last = n->prev;
-			m->next = MDOC_NEXT_SIBLING;
+			mdoc->last = n->prev;
+			mdoc->next = MDOC_NEXT_SIBLING;
 		} else {
-			m->last = n->parent;
-			m->next = MDOC_NEXT_CHILD;
+			mdoc->last = n->parent;
+			mdoc->next = MDOC_NEXT_CHILD;
 		}
 	}
 
-	if (m && m->first == n)
-		m->first = NULL;
+	if (mdoc && mdoc->first == n)
+		mdoc->first = NULL;
 }
 
 
 void
-mdoc_node_delete(struct mdoc *m, struct mdoc_node *p)
+mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p)
 {
 
 	while (p->child) {
 		assert(p->nchild);
-		mdoc_node_delete(m, p->child);
+		mdoc_node_delete(mdoc, p->child);
 	}
 	assert(0 == p->nchild);
 
-	mdoc_node_unlink(m, p);
+	mdoc_node_unlink(mdoc, p);
 	mdoc_node_free(p);
 }
 
+int
+mdoc_node_relink(struct mdoc *mdoc, struct mdoc_node *p)
+{
+
+	mdoc_node_unlink(mdoc, p);
+	return(node_append(mdoc, p));
+}
+
 #if 0
 /*
  * Pre-treat a text line.
@@ -658,7 +686,7 @@ mdoc_node_delete(struct mdoc *m, struct mdoc_node *p)
  * the end-of-line, i.e., will re-enter in the next roff parse.
  */
 static int
-mdoc_preptext(struct mdoc *m, int line, char *buf, int offs)
+mdoc_preptext(struct mdoc *mdoc, int line, char *buf, int offs)
 {
 	char		*start, *end;
 	char		 delim;
@@ -666,12 +694,12 @@ mdoc_preptext(struct mdoc *m, int line, char *buf, int offs)
 	while ('\0' != buf[offs]) {
 		/* Mark starting position if eqn is set. */
 		start = NULL;
-		if ('\0' != (delim = roff_eqndelim(m->roff)))
+		if ('\0' != (delim = roff_eqndelim(mdoc->roff)))
 			if (NULL != (start = strchr(buf + offs, delim)))
 				*start++ = '\0';
 
 		/* Parse text as normal. */
-		if ( ! mdoc_ptext(m, line, buf, offs))
+		if ( ! mdoc_ptext(mdoc, line, buf, offs))
 			return(0);
 
 		/* Continue only if an equation exists. */
@@ -688,11 +716,11 @@ mdoc_preptext(struct mdoc *m, int line, char *buf, int offs)
 		}
 
 		/* Parse the equation itself. */
-		roff_openeqn(m->roff, NULL, line, offs, buf);
+		roff_openeqn(mdoc->roff, NULL, line, offs, buf);
 
 		/* Process a finished equation? */
-		if (roff_closeeqn(m->roff))
-			if ( ! mdoc_addeqn(m, roff_eqn(m->roff)))
+		if (roff_closeeqn(mdoc->roff))
+			if ( ! mdoc_addeqn(mdoc, roff_eqn(mdoc->roff)))
 				return(0);
 		offs += (end - (buf + offs));
 	} 
@@ -706,20 +734,20 @@ mdoc_preptext(struct mdoc *m, int line, char *buf, int offs)
  * control character.
  */
 static int
-mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
+mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs)
 {
 	char		 *c, *ws, *end;
 	struct mdoc_node *n;
 
 	/* No text before an initial macro. */
 
-	if (SEC_NONE == m->lastnamed) {
-		mdoc_pmsg(m, line, offs, MANDOCERR_NOTEXT);
+	if (SEC_NONE == mdoc->lastnamed) {
+		mdoc_pmsg(mdoc, line, offs, MANDOCERR_NOTEXT);
 		return(1);
 	}
 
-	assert(m->last);
-	n = m->last;
+	assert(mdoc->last);
+	n = mdoc->last;
 
 	/*
 	 * Divert directly to list processing if we're encountering a
@@ -731,8 +759,8 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
 	if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
 			LIST_column == n->norm->Bl.type) {
 		/* `Bl' is open without any children. */
-		m->flags |= MDOC_FREECOL;
-		return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
+		mdoc->flags |= MDOC_FREECOL;
+		return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
 	}
 
 	if (MDOC_It == n->tok && MDOC_BLOCK == n->type &&
@@ -740,8 +768,8 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
 			MDOC_Bl == n->parent->tok &&
 			LIST_column == n->parent->norm->Bl.type) {
 		/* `Bl' has block-level `It' children. */
-		m->flags |= MDOC_FREECOL;
-		return(mdoc_macro(m, MDOC_It, line, offs, &offs, buf));
+		mdoc->flags |= MDOC_FREECOL;
+		return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf));
 	}
 
 	/*
@@ -769,7 +797,7 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
 			 * Strip trailing tabs in literal context only;
 			 * outside, they affect the next line.
 			 */
-			if (MDOC_LITERAL & m->flags)
+			if (MDOC_LITERAL & mdoc->flags)
 				continue;
 			break;
 		case '\\':
@@ -786,27 +814,28 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
 	*end = '\0';
 
 	if (ws)
-		mdoc_pmsg(m, line, (int)(ws-buf), MANDOCERR_EOLNSPACE);
+		mdoc_pmsg(mdoc, line, (int)(ws-buf), MANDOCERR_EOLNSPACE);
 
-	if ('\0' == buf[offs] && ! (MDOC_LITERAL & m->flags)) {
-		mdoc_pmsg(m, line, (int)(c-buf), MANDOCERR_NOBLANKLN);
+	if ('\0' == buf[offs] && ! (MDOC_LITERAL & mdoc->flags)) {
+		mdoc_pmsg(mdoc, line, (int)(c-buf), MANDOCERR_NOBLANKLN);
 
 		/*
 		 * Insert a `sp' in the case of a blank line.  Technically,
 		 * blank lines aren't allowed, but enough manuals assume this
 		 * behaviour that we want to work around it.
 		 */
-		if ( ! mdoc_elem_alloc(m, line, offs, MDOC_sp, NULL))
+		if ( ! mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL))
 			return(0);
 
-		m->next = MDOC_NEXT_SIBLING;
-		return(1);
+		mdoc->next = MDOC_NEXT_SIBLING;
+
+		return(mdoc_valid_post(mdoc));
 	}
 
-	if ( ! mdoc_word_alloc(m, line, offs, buf+offs))
+	if ( ! mdoc_word_alloc(mdoc, line, offs, buf+offs))
 		return(0);
 
-	if (MDOC_LITERAL & m->flags)
+	if (MDOC_LITERAL & mdoc->flags)
 		return(1);
 
 	/*
@@ -818,7 +847,7 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
 	assert(buf < end);
 
 	if (mandoc_eos(buf+offs, (size_t)(end-buf-offs), 0))
-		m->last->flags |= MDOC_EOS;
+		mdoc->last->flags |= MDOC_EOS;
 
 	return(1);
 }
@@ -829,7 +858,7 @@ mdoc_ptext(struct mdoc *m, int line, char *buf, int offs)
  * character.
  */
 static int
-mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
+mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs)
 {
 	enum mdoct	  tok;
 	int		  i, sv;
@@ -839,7 +868,7 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
 	/* Empty post-control lines are ignored. */
 
 	if ('"' == buf[offs]) {
-		mdoc_pmsg(m, ln, offs, MANDOCERR_BADCOMMENT);
+		mdoc_pmsg(mdoc, ln, offs, MANDOCERR_BADCOMMENT);
 		return(1);
 	} else if ('\0' == buf[offs])
 		return(1);
@@ -861,7 +890,7 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
 	tok = (i > 1 || i < 4) ? mdoc_hash_find(mac) : MDOC_MAX;
 
 	if (MDOC_MAX == tok) {
-		mandoc_vmsg(MANDOCERR_MACRO, m->parse, 
+		mandoc_vmsg(MANDOCERR_MACRO, mdoc->parse, 
 				ln, sv, "%s", buf + sv - 1);
 		return(1);
 	}
@@ -882,21 +911,21 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
 	 */
 
 	if ('\0' == buf[offs] && ' ' == buf[offs - 1])
-		mdoc_pmsg(m, ln, offs - 1, MANDOCERR_EOLNSPACE);
+		mdoc_pmsg(mdoc, ln, offs - 1, MANDOCERR_EOLNSPACE);
 
 	/*
 	 * If an initial macro or a list invocation, divert directly
 	 * into macro processing.
 	 */
 
-	if (NULL == m->last || MDOC_It == tok || MDOC_El == tok) {
-		if ( ! mdoc_macro(m, tok, ln, sv, &offs, buf)) 
+	if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) {
+		if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf)) 
 			goto err;
 		return(1);
 	}
 
-	n = m->last;
-	assert(m->last);
+	n = mdoc->last;
+	assert(mdoc->last);
 
 	/*
 	 * If the first macro of a `Bl -column', open an `It' block
@@ -905,8 +934,8 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
 
 	if (MDOC_Bl == n->tok && MDOC_BODY == n->type &&
 			LIST_column == n->norm->Bl.type) {
-		m->flags |= MDOC_FREECOL;
-		if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf))
+		mdoc->flags |= MDOC_FREECOL;
+		if ( ! mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf))
 			goto err;
 		return(1);
 	}
@@ -921,22 +950,22 @@ mdoc_pmacro(struct mdoc *m, int ln, char *buf, int offs)
 			NULL != n->parent &&
 			MDOC_Bl == n->parent->tok &&
 			LIST_column == n->parent->norm->Bl.type) {
-		m->flags |= MDOC_FREECOL;
-		if ( ! mdoc_macro(m, MDOC_It, ln, sv, &sv, buf)) 
+		mdoc->flags |= MDOC_FREECOL;
+		if ( ! mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf)) 
 			goto err;
 		return(1);
 	}
 
 	/* Normal processing of a macro. */
 
-	if ( ! mdoc_macro(m, tok, ln, sv, &offs, buf)) 
+	if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf)) 
 		goto err;
 
 	return(1);
 
 err:	/* Error out. */
 
-	m->flags |= MDOC_HALT;
+	mdoc->flags |= MDOC_HALT;
 	return(0);
 }
 
@@ -980,7 +1009,7 @@ mdoc_isdelim(const char *p)
 
 	if (0 == strcmp(p + 1, "."))
 		return(DELIM_CLOSE);
-	if (0 == strcmp(p + 1, "*(Ba"))
+	if (0 == strcmp(p + 1, "fR|\\fP"))
 		return(DELIM_MIDDLE);
 
 	return(DELIM_NONE);
diff --git a/contrib/mdocml/mdoc.h b/contrib/mdocml/mdoc.h
index 9cee098e7fe..d0153b44806 100644
--- a/contrib/mdocml/mdoc.h
+++ b/contrib/mdocml/mdoc.h
@@ -1,4 +1,4 @@
-/*	$Id: mdoc.h,v 1.122 2011/03/22 14:05:45 kristaps Exp $ */
+/*	$Id: mdoc.h,v 1.125 2013/12/24 19:11:45 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  *
@@ -311,6 +311,7 @@ struct	mdoc_bl {
 	int		  comp; /* -compact */
 	size_t		  ncols; /* -column arg count */
 	const char	**cols; /* -column val ptr */
+	int		  count; /* -enum counter */
 };
 
 struct	mdoc_bf {
@@ -350,6 +351,7 @@ struct	mdoc_node {
 	int		  nchild; /* number children */
 	int		  line; /* parse line */
 	int		  pos; /* parse column */
+	int		  lastline; /* the node ends on this line */
 	enum mdoct	  tok; /* tok or MDOC__MAX if none */
 	int		  flags;
 #define	MDOC_VALID	 (1 << 0) /* has been validated */
@@ -362,6 +364,7 @@ struct	mdoc_node {
 	enum mdoc_type	  type; /* AST node type */
 	enum mdoc_sec	  sec; /* current named section */
 	union mdoc_data	 *norm; /* normalised args */
+	const void	 *prev_font; /* before entering this node */
 	/* FIXME: these can be union'd to shave a few bytes. */
 	struct mdoc_arg	 *args; /* BLOCK/ELEM */
 	struct mdoc_node *pending; /* BLOCK */
diff --git a/contrib/mdocml/mdoc_argv.c b/contrib/mdocml/mdoc_argv.c
index 08386e09b14..bb9bc6c339b 100644
--- a/contrib/mdocml/mdoc_argv.c
+++ b/contrib/mdocml/mdoc_argv.c
@@ -1,6 +1,7 @@
-/*	$Id: mdoc_argv.c,v 1.82 2012/03/23 05:50:24 kristaps Exp $ */
+/*	$Id: mdoc_argv.c,v 1.89 2013/12/25 00:50:05 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2012 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -42,8 +43,7 @@ enum	argsflag {
 enum	argvflag {
 	ARGV_NONE, /* no args to flag (e.g., -split) */
 	ARGV_SINGLE, /* one arg to flag (e.g., -file xxx)  */
-	ARGV_MULTI, /* multiple args (e.g., -column xxx yyy) */
-	ARGV_OPT_SINGLE /* optional arg (e.g., -offset [xxx]) */
+	ARGV_MULTI /* multiple args (e.g., -column xxx yyy) */
 };
 
 struct	mdocarg {
@@ -57,8 +57,6 @@ static	enum margserr	 args(struct mdoc *, int, int *,
 static	int		 args_checkpunct(const char *, int);
 static	int		 argv_multi(struct mdoc *, int, 
 				struct mdoc_argv *, int *, char *);
-static	int		 argv_opt_single(struct mdoc *, int, 
-				struct mdoc_argv *, int *, char *);
 static	int		 argv_single(struct mdoc *, int, 
 				struct mdoc_argv *, int *, char *);
 
@@ -69,7 +67,7 @@ static	const enum argvflag argvflags[MDOC_ARG_MAX] = {
 	ARGV_NONE,	/* MDOC_Unfilled */
 	ARGV_NONE,	/* MDOC_Literal */
 	ARGV_SINGLE,	/* MDOC_File */
-	ARGV_OPT_SINGLE, /* MDOC_Offset */
+	ARGV_SINGLE,	/* MDOC_Offset */
 	ARGV_NONE,	/* MDOC_Bullet */
 	ARGV_NONE,	/* MDOC_Dash */
 	ARGV_NONE,	/* MDOC_Hyphen */
@@ -81,7 +79,7 @@ static	const enum argvflag argvflags[MDOC_ARG_MAX] = {
 	ARGV_NONE,	/* MDOC_Ohang */
 	ARGV_NONE,	/* MDOC_Inset */
 	ARGV_MULTI,	/* MDOC_Column */
-	ARGV_OPT_SINGLE, /* MDOC_Width */
+	ARGV_SINGLE,	/* MDOC_Width */
 	ARGV_NONE,	/* MDOC_Compact */
 	ARGV_NONE,	/* MDOC_Std */
 	ARGV_NONE,	/* MDOC_Filled */
@@ -146,7 +144,7 @@ static	const enum mdocargt args_Bl[] = {
 };
 
 static	const struct mdocarg mdocargs[MDOC_MAX] = {
-	{ ARGSFL_NONE, NULL }, /* Ap */
+	{ ARGSFL_DELIM, NULL }, /* Ap */
 	{ ARGSFL_NONE, NULL }, /* Dd */
 	{ ARGSFL_NONE, NULL }, /* Dt */
 	{ ARGSFL_NONE, NULL }, /* Os */
@@ -163,7 +161,7 @@ static	const struct mdocarg mdocargs[MDOC_MAX] = {
 	{ ARGSFL_DELIM, NULL }, /* Ad */ 
 	{ ARGSFL_DELIM, args_An }, /* An */
 	{ ARGSFL_DELIM, NULL }, /* Ar */
-	{ ARGSFL_NONE, NULL }, /* Cd */
+	{ ARGSFL_DELIM, NULL }, /* Cd */
 	{ ARGSFL_DELIM, NULL }, /* Cm */
 	{ ARGSFL_DELIM, NULL }, /* Dv */ 
 	{ ARGSFL_DELIM, NULL }, /* Er */ 
@@ -175,7 +173,7 @@ static	const struct mdocarg mdocargs[MDOC_MAX] = {
 	{ ARGSFL_DELIM, NULL }, /* Fn */ 
 	{ ARGSFL_DELIM, NULL }, /* Ft */ 
 	{ ARGSFL_DELIM, NULL }, /* Ic */ 
-	{ ARGSFL_NONE, NULL }, /* In */ 
+	{ ARGSFL_DELIM, NULL }, /* In */ 
 	{ ARGSFL_DELIM, NULL }, /* Li */
 	{ ARGSFL_NONE, NULL }, /* Nd */ 
 	{ ARGSFL_DELIM, NULL }, /* Nm */ 
@@ -243,7 +241,7 @@ static	const struct mdocarg mdocargs[MDOC_MAX] = {
 	{ ARGSFL_DELIM, NULL }, /* Xc */
 	{ ARGSFL_NONE, NULL }, /* Xo */
 	{ ARGSFL_NONE, NULL }, /* Fo */ 
-	{ ARGSFL_NONE, NULL }, /* Fc */ 
+	{ ARGSFL_DELIM, NULL }, /* Fc */ 
 	{ ARGSFL_NONE, NULL }, /* Oo */
 	{ ARGSFL_DELIM, NULL }, /* Oc */
 	{ ARGSFL_NONE, args_Bk }, /* Bk */
@@ -252,7 +250,7 @@ static	const struct mdocarg mdocargs[MDOC_MAX] = {
 	{ ARGSFL_NONE, NULL }, /* Hf */
 	{ ARGSFL_NONE, NULL }, /* Fr */
 	{ ARGSFL_NONE, NULL }, /* Ud */
-	{ ARGSFL_NONE, NULL }, /* Lb */
+	{ ARGSFL_DELIM, NULL }, /* Lb */
 	{ ARGSFL_NONE, NULL }, /* Lp */
 	{ ARGSFL_DELIM, NULL }, /* Lk */
 	{ ARGSFL_DELIM, NULL }, /* Mt */
@@ -262,7 +260,7 @@ static	const struct mdocarg mdocargs[MDOC_MAX] = {
 	{ ARGSFL_NONE, NULL }, /* %C */
 	{ ARGSFL_NONE, NULL }, /* Es */
 	{ ARGSFL_NONE, NULL }, /* En */
-	{ ARGSFL_NONE, NULL }, /* Dx */
+	{ ARGSFL_DELIM, NULL }, /* Dx */
 	{ ARGSFL_NONE, NULL }, /* %Q */
 	{ ARGSFL_NONE, NULL }, /* br */
 	{ ARGSFL_NONE, NULL }, /* sp */
@@ -277,7 +275,7 @@ static	const struct mdocarg mdocargs[MDOC_MAX] = {
  * one mandatory value, an optional single value, or no value.
  */
 enum margverr
-mdoc_argv(struct mdoc *m, int line, enum mdoct tok,
+mdoc_argv(struct mdoc *mdoc, int line, enum mdoct tok,
 		struct mdoc_arg **v, int *pos, char *buf)
 {
 	char		 *p, sv;
@@ -344,15 +342,11 @@ mdoc_argv(struct mdoc *m, int line, enum mdoct tok,
 
 	switch (argvflags[tmp.arg]) {
 	case (ARGV_SINGLE):
-		if ( ! argv_single(m, line, &tmp, pos, buf))
+		if ( ! argv_single(mdoc, line, &tmp, pos, buf))
 			return(ARGV_ERROR);
 		break;
 	case (ARGV_MULTI):
-		if ( ! argv_multi(m, line, &tmp, pos, buf))
-			return(ARGV_ERROR);
-		break;
-	case (ARGV_OPT_SINGLE):
-		if ( ! argv_opt_single(m, line, &tmp, pos, buf))
+		if ( ! argv_multi(mdoc, line, &tmp, pos, buf))
 			return(ARGV_ERROR);
 		break;
 	case (ARGV_NONE):
@@ -413,14 +407,14 @@ argn_free(struct mdoc_arg *p, int iarg)
 }
 
 enum margserr
-mdoc_zargs(struct mdoc *m, int line, int *pos, char *buf, char **v)
+mdoc_zargs(struct mdoc *mdoc, int line, int *pos, char *buf, char **v)
 {
 
-	return(args(m, line, pos, buf, ARGSFL_NONE, v));
+	return(args(mdoc, line, pos, buf, ARGSFL_NONE, v));
 }
 
 enum margserr
-mdoc_args(struct mdoc *m, int line, int *pos, 
+mdoc_args(struct mdoc *mdoc, int line, int *pos, 
 		char *buf, enum mdoct tok, char **v)
 {
 	enum argsflag	  fl;
@@ -429,7 +423,7 @@ mdoc_args(struct mdoc *m, int line, int *pos,
 	fl = mdocargs[tok].flags;
 
 	if (MDOC_It != tok)
-		return(args(m, line, pos, buf, fl, v));
+		return(args(mdoc, line, pos, buf, fl, v));
 
 	/*
 	 * We know that we're in an `It', so it's reasonable to expect
@@ -438,35 +432,36 @@ mdoc_args(struct mdoc *m, int line, int *pos,
 	 * safe fall-back into the default behaviour.
 	 */
 
-	for (n = m->last; n; n = n->parent)
+	for (n = mdoc->last; n; n = n->parent)
 		if (MDOC_Bl == n->tok)
 			if (LIST_column == n->norm->Bl.type) {
 				fl = ARGSFL_TABSEP;
 				break;
 			}
 
-	return(args(m, line, pos, buf, fl, v));
+	return(args(mdoc, line, pos, buf, fl, v));
 }
 
 static enum margserr
-args(struct mdoc *m, int line, int *pos, 
+args(struct mdoc *mdoc, int line, int *pos, 
 		char *buf, enum argsflag fl, char **v)
 {
 	char		*p, *pp;
+	int		 pairs;
 	enum margserr	 rc;
 
 	if ('\0' == buf[*pos]) {
-		if (MDOC_PPHRASE & m->flags)
+		if (MDOC_PPHRASE & mdoc->flags)
 			return(ARGS_EOLN);
 		/*
 		 * If we're not in a partial phrase and the flag for
 		 * being a phrase literal is still set, the punctuation
 		 * is unterminated.
 		 */
-		if (MDOC_PHRASELIT & m->flags)
-			mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE);
+		if (MDOC_PHRASELIT & mdoc->flags)
+			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE);
 
-		m->flags &= ~MDOC_PHRASELIT;
+		mdoc->flags &= ~MDOC_PHRASELIT;
 		return(ARGS_EOLN);
 	}
 
@@ -489,7 +484,7 @@ args(struct mdoc *m, int line, int *pos,
 		pp = NULL;
 
 		/* Scan ahead to unescaped `Ta'. */
-		if ( ! (MDOC_PHRASELIT & m->flags)) 
+		if ( ! (MDOC_PHRASELIT & mdoc->flags)) 
 			for (pp = *v; ; pp++) {
 				if (NULL == (pp = strstr(pp, "Ta")))
 					break;
@@ -523,7 +518,7 @@ args(struct mdoc *m, int line, int *pos,
 
 		/* Whitespace check for eoln case... */
 		if ('\0' == *p && ' ' == *(p - 1))
-			mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE);
+			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE);
 
 		*pos += (int)(p - *v);
 
@@ -541,37 +536,48 @@ args(struct mdoc *m, int line, int *pos,
 			/* Skip ahead. */ ;
 
 		return(rc);
-	} 
+	}
 
-	/* 
+	/*
 	 * Process a quoted literal.  A quote begins with a double-quote
 	 * and ends with a double-quote NOT preceded by a double-quote.
+	 * NUL-terminate the literal in place.
+	 * Collapse pairs of quotes inside quoted literals.
 	 * Whitespace is NOT involved in literal termination.
 	 */
 
-	if (MDOC_PHRASELIT & m->flags || '\"' == buf[*pos]) {
-		if ( ! (MDOC_PHRASELIT & m->flags))
+	if (MDOC_PHRASELIT & mdoc->flags || '\"' == buf[*pos]) {
+		if ( ! (MDOC_PHRASELIT & mdoc->flags))
 			*v = &buf[++(*pos)];
 
-		if (MDOC_PPHRASE & m->flags)
-			m->flags |= MDOC_PHRASELIT;
+		if (MDOC_PPHRASE & mdoc->flags)
+			mdoc->flags |= MDOC_PHRASELIT;
 
+		pairs = 0;
 		for ( ; buf[*pos]; (*pos)++) {
+			/* Move following text left after quoted quotes. */
+			if (pairs)
+				buf[*pos - pairs] = buf[*pos];
 			if ('\"' != buf[*pos])
 				continue;
+			/* Unquoted quotes end quoted args. */
 			if ('\"' != buf[*pos + 1])
 				break;
+			/* Quoted quotes collapse. */
+			pairs++;
 			(*pos)++;
 		}
+		if (pairs)
+			buf[*pos - pairs] = '\0';
 
 		if ('\0' == buf[*pos]) {
-			if (MDOC_PPHRASE & m->flags)
+			if (MDOC_PPHRASE & mdoc->flags)
 				return(ARGS_QWORD);
-			mdoc_pmsg(m, line, *pos, MANDOCERR_BADQUOTE);
+			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_BADQUOTE);
 			return(ARGS_QWORD);
 		}
 
-		m->flags &= ~MDOC_PHRASELIT;
+		mdoc->flags &= ~MDOC_PHRASELIT;
 		buf[(*pos)++] = '\0';
 
 		if ('\0' == buf[*pos])
@@ -581,13 +587,13 @@ args(struct mdoc *m, int line, int *pos,
 			(*pos)++;
 
 		if ('\0' == buf[*pos])
-			mdoc_pmsg(m, line, *pos, MANDOCERR_EOLNSPACE);
+			mdoc_pmsg(mdoc, line, *pos, MANDOCERR_EOLNSPACE);
 
 		return(ARGS_QWORD);
 	}
 
 	p = &buf[*pos];
-	*v = mandoc_getarg(m->parse, &p, line, pos);
+	*v = mandoc_getarg(mdoc->parse, &p, line, pos);
 
 	return(ARGS_WORD);
 }
@@ -643,7 +649,7 @@ args_checkpunct(const char *buf, int i)
 }
 
 static int
-argv_multi(struct mdoc *m, int line, 
+argv_multi(struct mdoc *mdoc, int line, 
 		struct mdoc_argv *v, int *pos, char *buf)
 {
 	enum margserr	 ac;
@@ -652,7 +658,7 @@ argv_multi(struct mdoc *m, int line,
 	for (v->sz = 0; ; v->sz++) {
 		if ('-' == buf[*pos])
 			break;
-		ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
+		ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p);
 		if (ARGS_ERROR == ac)
 			return(0);
 		else if (ARGS_EOLN == ac)
@@ -669,16 +675,13 @@ argv_multi(struct mdoc *m, int line,
 }
 
 static int
-argv_opt_single(struct mdoc *m, int line, 
+argv_single(struct mdoc *mdoc, int line, 
 		struct mdoc_argv *v, int *pos, char *buf)
 {
 	enum margserr	 ac;
 	char		*p;
 
-	if ('-' == buf[*pos])
-		return(1);
-
-	ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
+	ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p);
 	if (ARGS_ERROR == ac)
 		return(0);
 	if (ARGS_EOLN == ac)
@@ -690,27 +693,3 @@ argv_opt_single(struct mdoc *m, int line,
 
 	return(1);
 }
-
-static int
-argv_single(struct mdoc *m, int line, 
-		struct mdoc_argv *v, int *pos, char *buf)
-{
-	int		 ppos;
-	enum margserr	 ac;
-	char		*p;
-
-	ppos = *pos;
-
-	ac = args(m, line, pos, buf, ARGSFL_NONE, &p);
-	if (ARGS_EOLN == ac) {
-		mdoc_pmsg(m, line, ppos, MANDOCERR_SYNTARGVCOUNT);
-		return(0);
-	} else if (ARGS_ERROR == ac)
-		return(0);
-
-	v->sz = 1;
-	v->value = mandoc_malloc(sizeof(char *));
-	v->value[0] = mandoc_strdup(p);
-
-	return(1);
-}
diff --git a/contrib/mdocml/mdoc_html.c b/contrib/mdocml/mdoc_html.c
index 60ea6dc7383..a7aa722d94c 100644
--- a/contrib/mdocml/mdoc_html.c
+++ b/contrib/mdocml/mdoc_html.c
@@ -1,4 +1,4 @@
-/*	$Id: mdoc_html.c,v 1.182 2011/11/03 20:37:00 schwarze Exp $ */
+/*	$Id: mdoc_html.c,v 1.186 2013/12/24 20:45:27 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  *
@@ -35,7 +35,7 @@
 
 #define	INDENT		 5
 
-#define	MDOC_ARGS	  const struct mdoc_meta *m, \
+#define	MDOC_ARGS	  const struct mdoc_meta *meta, \
 			  const struct mdoc_node *n, \
 			  struct html *h
 
@@ -260,10 +260,11 @@ static	const char * const lists[LIST_MAX] = {
 };
 
 void
-html_mdoc(void *arg, const struct mdoc *m)
+html_mdoc(void *arg, const struct mdoc *mdoc)
 {
 
-	print_mdoc(mdoc_meta(m), mdoc_node(m), (struct html *)arg);
+	print_mdoc(mdoc_meta(mdoc), mdoc_node(mdoc),
+			(struct html *)arg);
 	putchar('\n');
 }
 
@@ -361,14 +362,14 @@ print_mdoc(MDOC_ARGS)
 		print_gen_decls(h);
 		t = print_otag(h, TAG_HTML, 0, NULL);
 		tt = print_otag(h, TAG_HEAD, 0, NULL);
-		print_mdoc_head(m, n, h);
+		print_mdoc_head(meta, n, h);
 		print_tagq(h, tt);
 		print_otag(h, TAG_BODY, 0, NULL);
 		print_otag(h, TAG_DIV, 1, &tag);
 	} else 
 		t = print_otag(h, TAG_DIV, 1, &tag);
 
-	print_mdoc_nodelist(m, n, h);
+	print_mdoc_nodelist(meta, n, h);
 	print_tagq(h, t);
 }
 
@@ -380,10 +381,10 @@ print_mdoc_head(MDOC_ARGS)
 
 	print_gen_head(h);
 	bufinit(h);
-	bufcat_fmt(h, "%s(%s)", m->title, m->msec);
+	bufcat_fmt(h, "%s(%s)", meta->title, meta->msec);
 
-	if (m->arch)
-		bufcat_fmt(h, " (%s)", m->arch);
+	if (meta->arch)
+		bufcat_fmt(h, " (%s)", meta->arch);
 
 	print_otag(h, TAG_TITLE, 0, NULL);
 	print_text(h, h->buf);
@@ -394,9 +395,9 @@ static void
 print_mdoc_nodelist(MDOC_ARGS)
 {
 
-	print_mdoc_node(m, n, h);
+	print_mdoc_node(meta, n, h);
 	if (n->next)
-		print_mdoc_nodelist(m, n->next, h);
+		print_mdoc_nodelist(meta, n->next, h);
 }
 
 
@@ -411,7 +412,7 @@ print_mdoc_node(MDOC_ARGS)
 
 	switch (n->type) {
 	case (MDOC_ROOT):
-		child = mdoc_root_pre(m, n, h);
+		child = mdoc_root_pre(meta, n, h);
 		break;
 	case (MDOC_TEXT):
 		/* No tables in this mode... */
@@ -454,36 +455,32 @@ print_mdoc_node(MDOC_ARGS)
 
 		assert(NULL == h->tblt);
 		if (mdocs[n->tok].pre && ENDBODY_NOT == n->end)
-			child = (*mdocs[n->tok].pre)(m, n, h);
+			child = (*mdocs[n->tok].pre)(meta, n, h);
 		break;
 	}
 
 	if (HTML_KEEP & h->flags) {
-		if (n->prev && n->prev->line != n->line) {
+		if (n->prev ? (n->prev->lastline != n->line) :
+		    (n->parent && n->parent->line != n->line)) {
 			h->flags &= ~HTML_KEEP;
 			h->flags |= HTML_PREKEEP;
-		} else if (NULL == n->prev) {
-			if (n->parent && n->parent->line != n->line) {
-				h->flags &= ~HTML_KEEP;
-				h->flags |= HTML_PREKEEP;
-			}
 		}
 	}
 
 	if (child && n->child)
-		print_mdoc_nodelist(m, n->child, h);
+		print_mdoc_nodelist(meta, n->child, h);
 
 	print_stagq(h, t);
 
 	switch (n->type) {
 	case (MDOC_ROOT):
-		mdoc_root_post(m, n, h);
+		mdoc_root_post(meta, n, h);
 		break;
 	case (MDOC_EQN):
 		break;
 	default:
 		if (mdocs[n->tok].post && ENDBODY_NOT == n->end)
-			(*mdocs[n->tok].post)(m, n, h);
+			(*mdocs[n->tok].post)(meta, n, h);
 		break;
 	}
 }
@@ -509,13 +506,13 @@ mdoc_root_post(MDOC_ARGS)
 
 	PAIR_CLASS_INIT(&tag[0], "foot-date");
 	print_otag(h, TAG_TD, 1, tag);
-	print_text(h, m->date);
+	print_text(h, meta->date);
 	print_stagq(h, tt);
 
 	PAIR_CLASS_INIT(&tag[0], "foot-os");
 	PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
 	print_otag(h, TAG_TD, 2, tag);
-	print_text(h, m->os);
+	print_text(h, meta->os);
 	print_tagq(h, t);
 }
 
@@ -528,15 +525,15 @@ mdoc_root_pre(MDOC_ARGS)
 	struct tag	*t, *tt;
 	char		 b[BUFSIZ], title[BUFSIZ];
 
-	strlcpy(b, m->vol, BUFSIZ);
+	strlcpy(b, meta->vol, BUFSIZ);
 
-	if (m->arch) {
+	if (meta->arch) {
 		strlcat(b, " (", BUFSIZ);
-		strlcat(b, m->arch, BUFSIZ);
+		strlcat(b, meta->arch, BUFSIZ);
 		strlcat(b, ")", BUFSIZ);
 	}
 
-	snprintf(title, BUFSIZ - 1, "%s(%s)", m->title, m->msec);
+	snprintf(title, BUFSIZ - 1, "%s(%s)", meta->title, meta->msec);
 
 	PAIR_SUMMARY_INIT(&tag[0], "Document Header");
 	PAIR_CLASS_INIT(&tag[1], "head");
@@ -689,13 +686,13 @@ mdoc_nm_pre(MDOC_ARGS)
 		synopsis_pre(h, n);
 		PAIR_CLASS_INIT(&tag, "name");
 		print_otag(h, TAG_B, 1, &tag);
-		if (NULL == n->child && m->name)
-			print_text(h, m->name);
+		if (NULL == n->child && meta->name)
+			print_text(h, meta->name);
 		return(1);
 	case (MDOC_HEAD):
 		print_otag(h, TAG_TD, 0, NULL);
-		if (NULL == n->child && m->name)
-			print_text(h, m->name);
+		if (NULL == n->child && meta->name)
+			print_text(h, meta->name);
 		return(1);
 	case (MDOC_BODY):
 		print_otag(h, TAG_TD, 0, NULL);
@@ -712,8 +709,8 @@ mdoc_nm_pre(MDOC_ARGS)
 		if (MDOC_TEXT == n->type)
 			len += html_strlen(n->string);
 
-	if (0 == len && m->name)
-		len = html_strlen(m->name);
+	if (0 == len && meta->name)
+		len = html_strlen(meta->name);
 
 	SCALE_HS_INIT(&su, (double)len);
 	bufinit(h);
@@ -981,8 +978,6 @@ mdoc_bl_pre(MDOC_ARGS)
 	struct roffsu	 su;
 	char		 buf[BUFSIZ];
 
-	bufinit(h);
-
 	if (MDOC_BODY == n->type) {
 		if (LIST_column == n->norm->Bl.type)
 			print_otag(h, TAG_TBODY, 0, NULL);
@@ -1001,6 +996,7 @@ mdoc_bl_pre(MDOC_ARGS)
 		 */
 
 		for (i = 0; i < (int)n->norm->Bl.ncols; i++) {
+			bufinit(h);
 			a2width(n->norm->Bl.cols[i], &su);
 			if (i < (int)n->norm->Bl.ncols - 1)
 				bufcat_su(h, "width", &su);
@@ -1014,6 +1010,7 @@ mdoc_bl_pre(MDOC_ARGS)
 	}
 
 	SCALE_VS_INIT(&su, 0);
+	bufinit(h);
 	bufcat_su(h, "margin-top", &su);
 	bufcat_su(h, "margin-bottom", &su);
 	PAIR_STYLE_INIT(&tag[0], h);
@@ -1225,7 +1222,7 @@ mdoc_bd_pre(MDOC_ARGS)
 	h->flags |= HTML_LITERAL;
 
 	for (nn = n->child; nn; nn = nn->next) {
-		print_mdoc_node(m, nn, h);
+		print_mdoc_node(meta, nn, h);
 		/*
 		 * If the printed node flushes its own line, then we
 		 * needn't do it here as well.  This is hacky, but the
@@ -2273,7 +2270,7 @@ mdoc_quote_post(MDOC_ARGS)
 	case (MDOC_So):
 		/* FALLTHROUGH */
 	case (MDOC_Sq):
-		print_text(h, "\\(aq");
+		print_text(h, "\\(cq");
 		break;
 	default:
 		abort();
diff --git a/contrib/mdocml/mdoc_macro.c b/contrib/mdocml/mdoc_macro.c
index 11d147399ee..2a63ca92e31 100644
--- a/contrib/mdocml/mdoc_macro.c
+++ b/contrib/mdocml/mdoc_macro.c
@@ -1,7 +1,7 @@
-/*	$Id: mdoc_macro.c,v 1.115 2012/01/05 00:43:51 schwarze Exp $ */
+/*	$Id: mdoc_macro.c,v 1.125 2013/12/24 20:45:27 schwarze Exp $ */
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010 Ingo Schwarze 
+ * Copyright (c) 2008-2012 Kristaps Dzonsons 
+ * Copyright (c) 2010, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -51,8 +51,8 @@ static	int	  	in_line(MACRO_PROT_ARGS);
 static	int	  	obsolete(MACRO_PROT_ARGS);
 static	int	  	phrase_ta(MACRO_PROT_ARGS);
 
-static	int		dword(struct mdoc *, int, int, 
-				const char *, enum mdelim);
+static	int		dword(struct mdoc *, int, int, const char *,
+				 enum mdelim, int);
 static	int	  	append_delims(struct mdoc *, 
 				int, int *, char *);
 static	enum mdoct	lookup(enum mdoct, const char *);
@@ -70,128 +70,147 @@ static	int	  	rew_sub(enum mdoc_type, struct mdoc *,
 				enum mdoct, int, int);
 
 const	struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
-	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ap */
+	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ap */
 	{ in_line_eoln, MDOC_PROLOGUE }, /* Dd */
 	{ in_line_eoln, MDOC_PROLOGUE }, /* Dt */
 	{ in_line_eoln, MDOC_PROLOGUE }, /* Os */
-	{ blk_full, MDOC_PARSED }, /* Sh */
-	{ blk_full, MDOC_PARSED }, /* Ss */ 
-	{ in_line_eoln, 0 }, /* Pp */ 
-	{ blk_part_imp, MDOC_PARSED }, /* D1 */
-	{ blk_part_imp, MDOC_PARSED }, /* Dl */
+	{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* Sh */
+	{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* Ss */
+	{ in_line_eoln, 0 }, /* Pp */
+	{ blk_part_imp, MDOC_PARSED | MDOC_JOIN }, /* D1 */
+	{ blk_part_imp, MDOC_PARSED | MDOC_JOIN }, /* Dl */
 	{ blk_full, MDOC_EXPLICIT }, /* Bd */
-	{ blk_exp_close, MDOC_EXPLICIT }, /* Ed */
+	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ed */
 	{ blk_full, MDOC_EXPLICIT }, /* Bl */
-	{ blk_exp_close, MDOC_EXPLICIT }, /* El */
-	{ blk_full, MDOC_PARSED }, /* It */
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */ 
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* An */
+	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* El */
+	{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* It */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ad */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* An */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ar */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cd */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Cm */
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */ 
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */ 
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */ 
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Dv */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Er */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ev */
 	{ in_line_eoln, 0 }, /* Ex */
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */ 
-	{ in_line_eoln, 0 }, /* Fd */ 
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fa */
+	{ in_line_eoln, 0 }, /* Fd */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fl */
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */ 
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ft */ 
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */ 
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Fn */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ft */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ic */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Li */
-	{ blk_full, 0 }, /* Nd */ 
-	{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */ 
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Li */
+	{ blk_full, MDOC_JOIN }, /* Nd */
+	{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Nm */
 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Op */
 	{ obsolete, 0 }, /* Ot */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
 	{ in_line_eoln, 0 }, /* Rv */
-	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */ 
+	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
-	{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */ 
+	{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
-	{ in_line_eoln, 0 }, /* %A */
-	{ in_line_eoln, 0 }, /* %B */
-	{ in_line_eoln, 0 }, /* %D */
-	{ in_line_eoln, 0 }, /* %I */
-	{ in_line_eoln, 0 }, /* %J */
+	{ in_line_eoln, MDOC_JOIN }, /* %A */
+	{ in_line_eoln, MDOC_JOIN }, /* %B */
+	{ in_line_eoln, MDOC_JOIN }, /* %D */
+	{ in_line_eoln, MDOC_JOIN }, /* %I */
+	{ in_line_eoln, MDOC_JOIN }, /* %J */
 	{ in_line_eoln, 0 }, /* %N */
-	{ in_line_eoln, 0 }, /* %O */
+	{ in_line_eoln, MDOC_JOIN }, /* %O */
 	{ in_line_eoln, 0 }, /* %P */
-	{ in_line_eoln, 0 }, /* %R */
-	{ in_line_eoln, 0 }, /* %T */
+	{ in_line_eoln, MDOC_JOIN }, /* %R */
+	{ in_line_eoln, MDOC_JOIN }, /* %T */
 	{ in_line_eoln, 0 }, /* %V */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ac */
-	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ao */
-	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Aq */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Ac */
+	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_EXPLICIT | MDOC_JOIN }, /* Ao */
+	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Aq */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* At */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Bc */
-	{ blk_full, MDOC_EXPLICIT }, /* Bf */ 
-	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bo */
-	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Bq */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Bc */
+	{ blk_full, MDOC_EXPLICIT }, /* Bf */
+	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_EXPLICIT | MDOC_JOIN }, /* Bo */
+	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Bq */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bsx */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Bx */
 	{ in_line_eoln, 0 }, /* Db */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Dc */
-	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Do */
-	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Dq */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Ec */
-	{ blk_exp_close, MDOC_EXPLICIT }, /* Ef */
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Em */ 
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Dc */
+	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_EXPLICIT | MDOC_JOIN }, /* Do */
+	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Dq */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Ec */
+	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ef */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Em */
 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Eo */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Fx */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ms */
-	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* No */
-	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* Ns */
+	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_IGNDELIM | MDOC_JOIN }, /* No */
+	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_IGNDELIM | MDOC_JOIN }, /* Ns */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Nx */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ox */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Pc */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Pc */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_IGNDELIM }, /* Pf */
-	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Po */
-	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Pq */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Qc */
-	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Ql */
-	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Qo */
-	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Qq */
-	{ blk_exp_close, MDOC_EXPLICIT }, /* Re */
+	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_EXPLICIT | MDOC_JOIN }, /* Po */
+	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Pq */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Qc */
+	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ql */
+	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_EXPLICIT | MDOC_JOIN }, /* Qo */
+	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Qq */
+	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Re */
 	{ blk_full, MDOC_EXPLICIT }, /* Rs */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Sc */
-	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* So */
-	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Sq */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Sc */
+	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_EXPLICIT | MDOC_JOIN }, /* So */
+	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sq */
 	{ in_line_eoln, 0 }, /* Sm */
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sx */
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Sy */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sx */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Sy */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Tn */
-	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Ux */
+	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ux */
 	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Xc */
 	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Xo */
-	{ blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */ 
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Fc */ 
-	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Oo */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Oc */
+	{ blk_full, MDOC_EXPLICIT | MDOC_CALLABLE }, /* Fo */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Fc */
+	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_EXPLICIT | MDOC_JOIN }, /* Oo */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Oc */
 	{ blk_full, MDOC_EXPLICIT }, /* Bk */
-	{ blk_exp_close, MDOC_EXPLICIT }, /* Ek */
+	{ blk_exp_close, MDOC_EXPLICIT | MDOC_JOIN }, /* Ek */
 	{ in_line_eoln, 0 }, /* Bt */
 	{ in_line_eoln, 0 }, /* Hf */
 	{ obsolete, 0 }, /* Fr */
 	{ in_line_eoln, 0 }, /* Ud */
 	{ in_line, 0 }, /* Lb */
-	{ in_line_eoln, 0 }, /* Lp */ 
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Lk */ 
-	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Mt */ 
-	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED }, /* Brq */
-	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED | MDOC_EXPLICIT }, /* Bro */
-	{ blk_exp_close, MDOC_EXPLICIT | MDOC_CALLABLE | MDOC_PARSED }, /* Brc */
-	{ in_line_eoln, 0 }, /* %C */
+	{ in_line_eoln, 0 }, /* Lp */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Lk */
+	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Mt */
+	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Brq */
+	{ blk_part_exp, MDOC_CALLABLE | MDOC_PARSED |
+			MDOC_EXPLICIT | MDOC_JOIN }, /* Bro */
+	{ blk_exp_close, MDOC_CALLABLE | MDOC_PARSED |
+			 MDOC_EXPLICIT | MDOC_JOIN }, /* Brc */
+	{ in_line_eoln, MDOC_JOIN }, /* %C */
 	{ obsolete, 0 }, /* Es */
 	{ obsolete, 0 }, /* En */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */
-	{ in_line_eoln, 0 }, /* %Q */
+	{ in_line_eoln, MDOC_JOIN }, /* %Q */
 	{ in_line_eoln, 0 }, /* br */
 	{ in_line_eoln, 0 }, /* sp */
 	{ in_line_eoln, 0 }, /* %U */
-	{ phrase_ta, MDOC_CALLABLE | MDOC_PARSED }, /* Ta */
+	{ phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */
 };
 
 const	struct mdoc_macro * const mdoc_macros = __mdoc_macros;
@@ -203,22 +222,23 @@ const	struct mdoc_macro * const mdoc_macros = __mdoc_macros;
  * are errors.
  */
 int
-mdoc_macroend(struct mdoc *m)
+mdoc_macroend(struct mdoc *mdoc)
 {
 	struct mdoc_node *n;
 
 	/* Scan for open explicit scopes. */
 
-	n = MDOC_VALID & m->last->flags ?  m->last->parent : m->last;
+	n = MDOC_VALID & mdoc->last->flags ?
+			mdoc->last->parent : mdoc->last;
 
 	for ( ; n; n = n->parent)
 		if (MDOC_BLOCK == n->type &&
 		    MDOC_EXPLICIT & mdoc_macros[n->tok].flags)
-			mdoc_nmsg(m, n, MANDOCERR_SCOPEEXIT);
+			mdoc_nmsg(mdoc, n, MANDOCERR_SCOPEEXIT);
 
 	/* Rewind to the first. */
 
-	return(rew_last(m, m->first));
+	return(rew_last(mdoc, mdoc->first));
 }
 
 
@@ -263,8 +283,8 @@ rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
 	while (mdoc->last != to) {
 		/*
 		 * Save the parent here, because we may delete the
-		 * m->last node in the post-validation phase and reset
-		 * it to m->last->parent, causing a step in the closing
+		 * mdoc->last node in the post-validation phase and reset
+		 * it to mdoc->last->parent, causing a step in the closing
 		 * out to be lost.
 		 */
 		np = mdoc->last->parent;
@@ -460,7 +480,7 @@ rew_elem(struct mdoc *mdoc, enum mdoct tok)
  */
 static int
 make_pending(struct mdoc_node *broken, enum mdoct tok,
-		struct mdoc *m, int line, int ppos)
+		struct mdoc *mdoc, int line, int ppos)
 {
 	struct mdoc_node *breaker;
 
@@ -515,7 +535,7 @@ make_pending(struct mdoc_node *broken, enum mdoct tok,
 			taker->pending = broken->pending;
 		}
 		broken->pending = breaker;
-		mandoc_vmsg(MANDOCERR_SCOPENEST, m->parse, line, ppos,
+		mandoc_vmsg(MANDOCERR_SCOPENEST, mdoc->parse, line, ppos,
 				"%s breaks %s", mdoc_macronames[tok],
 				mdoc_macronames[broken->tok]);
 		return(1);
@@ -530,41 +550,46 @@ make_pending(struct mdoc_node *broken, enum mdoct tok,
 
 
 static int
-rew_sub(enum mdoc_type t, struct mdoc *m, 
+rew_sub(enum mdoc_type t, struct mdoc *mdoc, 
 		enum mdoct tok, int line, int ppos)
 {
 	struct mdoc_node *n;
 
-	n = m->last;
+	n = mdoc->last;
 	while (n) {
 		switch (rew_dohalt(tok, t, n)) {
 		case (REWIND_NONE):
 			return(1);
 		case (REWIND_THIS):
+			n->lastline = line -
+			    (MDOC_NEWLINE & mdoc->flags &&
+			     ! (MDOC_EXPLICIT & mdoc_macros[tok].flags));
 			break;
 		case (REWIND_FORCE):
-			mandoc_vmsg(MANDOCERR_SCOPEBROKEN, m->parse, 
+			mandoc_vmsg(MANDOCERR_SCOPEBROKEN, mdoc->parse, 
 					line, ppos, "%s breaks %s", 
 					mdoc_macronames[tok],
 					mdoc_macronames[n->tok]);
 			/* FALLTHROUGH */
 		case (REWIND_MORE):
+			n->lastline = line -
+			    (MDOC_NEWLINE & mdoc->flags ? 1 : 0);
 			n = n->parent;
 			continue;
 		case (REWIND_LATER):
-			if (make_pending(n, tok, m, line, ppos) ||
+			if (make_pending(n, tok, mdoc, line, ppos) ||
 			    MDOC_BLOCK != t)
 				return(1);
 			/* FALLTHROUGH */
 		case (REWIND_ERROR):
-			mdoc_pmsg(m, line, ppos, MANDOCERR_NOSCOPE);
+			mdoc_pmsg(mdoc, line, ppos, MANDOCERR_NOSCOPE);
 			return(1);
 		}
 		break;
 	}
 
 	assert(n);
-	if ( ! rew_last(m, n))
+	if ( ! rew_last(mdoc, n))
 		return(0);
 
 	/*
@@ -572,10 +597,10 @@ rew_sub(enum mdoc_type t, struct mdoc *m,
 	 * Now that the current block ends, close the enclosing block, too.
 	 */
 	while (NULL != (n = n->pending)) {
-		if ( ! rew_last(m, n))
+		if ( ! rew_last(mdoc, n))
 			return(0);
 		if (MDOC_HEAD == n->type &&
-		    ! mdoc_body_alloc(m, n->line, n->pos, n->tok))
+		    ! mdoc_body_alloc(mdoc, n->line, n->pos, n->tok))
 			return(0);
 	}
 
@@ -587,18 +612,26 @@ rew_sub(enum mdoc_type t, struct mdoc *m,
  * Punctuation consists of those tokens found in mdoc_isdelim().
  */
 static int
-dword(struct mdoc *m, int line, 
-		int col, const char *p, enum mdelim d)
+dword(struct mdoc *mdoc, int line, int col, const char *p,
+		enum mdelim d, int may_append)
 {
 	
 	if (DELIM_MAX == d)
 		d = mdoc_isdelim(p);
 
-	if ( ! mdoc_word_alloc(m, line, col, p))
+	if (may_append &&
+	    ! ((MDOC_SYNOPSIS | MDOC_KEEP | MDOC_SMOFF) & mdoc->flags) &&
+	    DELIM_NONE == d && MDOC_TEXT == mdoc->last->type &&
+	    DELIM_NONE == mdoc_isdelim(mdoc->last->string)) {
+		mdoc_word_append(mdoc, p);
+		return(1);
+	}
+
+	if ( ! mdoc_word_alloc(mdoc, line, col, p))
 		return(0);
 
 	if (DELIM_OPEN == d)
-		m->last->flags |= MDOC_DELIMO;
+		mdoc->last->flags |= MDOC_DELIMO;
 
 	/*
 	 * Closing delimiters only suppress the preceding space
@@ -610,15 +643,16 @@ dword(struct mdoc *m, int line,
 	 *	and solve this in the code related to `No'!
 	 */
 
-	else if (DELIM_CLOSE == d && m->last->prev &&
-			m->last->prev->tok != MDOC_No)
-		m->last->flags |= MDOC_DELIMC;
+	else if (DELIM_CLOSE == d && mdoc->last->prev &&
+			mdoc->last->prev->tok != MDOC_No &&
+			mdoc->last->parent->tok != MDOC_Fd)
+		mdoc->last->flags |= MDOC_DELIMC;
 
 	return(1);
 }
 
 static int
-append_delims(struct mdoc *m, int line, int *pos, char *buf)
+append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
 {
 	int		 la;
 	enum margserr	 ac;
@@ -629,14 +663,14 @@ append_delims(struct mdoc *m, int line, int *pos, char *buf)
 
 	for (;;) {
 		la = *pos;
-		ac = mdoc_zargs(m, line, pos, buf, &p);
+		ac = mdoc_zargs(mdoc, line, pos, buf, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
 		else if (ARGS_EOLN == ac)
 			break;
 
-		dword(m, line, la, p, DELIM_MAX);
+		dword(mdoc, line, la, p, DELIM_MAX, 1);
 
 		/*
 		 * If we encounter end-of-sentence symbols, then trigger
@@ -650,7 +684,7 @@ append_delims(struct mdoc *m, int line, int *pos, char *buf)
 		 * example, `.  ;' shouldn't propagate the double-space.
 		 */
 		if (mandoc_eos(p, strlen(p), 0))
-			m->last->flags |= MDOC_EOS;
+			mdoc->last->flags |= MDOC_EOS;
 	}
 
 	return(1);
@@ -672,12 +706,14 @@ blk_exp_close(MACRO_PROT_ARGS)
 	enum mdoct	 atok, ntok;
 	char		*p;
 
-	nl = MDOC_NEWLINE & m->flags;
+	nl = MDOC_NEWLINE & mdoc->flags;
 
 	switch (tok) {
 	case (MDOC_Ec):
 		maxargs = 1;
 		break;
+	case (MDOC_Ek):
+		mdoc->flags &= ~MDOC_KEEP;
 	default:
 		maxargs = 0;
 		break;
@@ -689,7 +725,7 @@ blk_exp_close(MACRO_PROT_ARGS)
 	 */
 	atok = rew_alt(tok);
 	body = later = NULL;
-	for (n = m->last; n; n = n->parent) {
+	for (n = mdoc->last; n; n = n->parent) {
 		if (MDOC_VALID & n->flags)
 			continue;
 
@@ -718,13 +754,13 @@ blk_exp_close(MACRO_PROT_ARGS)
 			 * postpone closing out the current block
 			 * until the rew_sub() closing out the sub-block.
 			 */
-			make_pending(later, tok, m, line, ppos);
+			make_pending(later, tok, mdoc, line, ppos);
 
 			/*
 			 * Mark the place where the formatting - but not
 			 * the scope - of the current block ends.
 			 */
-			if ( ! mdoc_endbody_alloc(m, line, ppos,
+			if ( ! mdoc_endbody_alloc(mdoc, line, ppos,
 			    atok, body, ENDBODY_SPACE))
 				return(0);
 			break;
@@ -738,37 +774,37 @@ blk_exp_close(MACRO_PROT_ARGS)
 		if (later &&
 		    MDOC_EXPLICIT & mdoc_macros[later->tok].flags)
 			continue;
-		if (MDOC_CALLABLE & mdoc_macros[n->tok].flags)
+		if (MDOC_It != n->tok)
 			later = n;
 	}
 
 	if ( ! (MDOC_CALLABLE & mdoc_macros[tok].flags)) {
 		/* FIXME: do this in validate */
 		if (buf[*pos]) 
-			mdoc_pmsg(m, line, ppos, MANDOCERR_ARGSLOST);
+			mdoc_pmsg(mdoc, line, ppos, MANDOCERR_ARGSLOST);
 
-		if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
+		if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
 			return(0);
-		return(rew_sub(MDOC_BLOCK, m, tok, line, ppos));
+		return(rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos));
 	}
 
-	if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
+	if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
 		return(0);
 
 	if (NULL == later && maxargs > 0) 
-		if ( ! mdoc_tail_alloc(m, line, ppos, rew_alt(tok)))
+		if ( ! mdoc_tail_alloc(mdoc, line, ppos, rew_alt(tok)))
 			return(0);
 
 	for (flushed = j = 0; ; j++) {
 		lastarg = *pos;
 
 		if (j == maxargs && ! flushed) {
-			if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
+			if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
 				return(0);
 			flushed = 1;
 		}
 
-		ac = mdoc_args(m, line, pos, buf, tok, &p);
+		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
@@ -780,27 +816,31 @@ blk_exp_close(MACRO_PROT_ARGS)
 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
 
 		if (MDOC_MAX == ntok) {
-			if ( ! dword(m, line, lastarg, p, DELIM_MAX))
+			if ( ! dword(mdoc, line, lastarg, p, DELIM_MAX,
+			    MDOC_JOIN & mdoc_macros[tok].flags))
 				return(0);
 			continue;
 		}
 
 		if ( ! flushed) {
-			if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
+			if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
 				return(0);
 			flushed = 1;
 		}
-		if ( ! mdoc_macro(m, ntok, line, lastarg, pos, buf))
+
+		mdoc->flags &= ~MDOC_NEWLINE;
+
+		if ( ! mdoc_macro(mdoc, ntok, line, lastarg, pos, buf))
 			return(0);
 		break;
 	}
 
-	if ( ! flushed && ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
+	if ( ! flushed && ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
 		return(0);
 
 	if ( ! nl)
 		return(1);
-	return(append_delims(m, line, pos, buf));
+	return(append_delims(mdoc, line, pos, buf));
 }
 
 
@@ -815,7 +855,7 @@ in_line(MACRO_PROT_ARGS)
 	struct mdoc_arg	*arg;
 	char		*p;
 
-	nl = MDOC_NEWLINE & m->flags;
+	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/*
 	 * Whether we allow ignored elements (those without content,
@@ -843,7 +883,7 @@ in_line(MACRO_PROT_ARGS)
 
 	for (arg = NULL;; ) {
 		la = *pos;
-		av = mdoc_argv(m, line, tok, &arg, pos, buf);
+		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
 
 		if (ARGV_WORD == av) {
 			*pos = la;
@@ -860,7 +900,7 @@ in_line(MACRO_PROT_ARGS)
 
 	for (cnt = scope = 0;; ) {
 		la = *pos;
-		ac = mdoc_args(m, line, pos, buf, tok, &p);
+		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
@@ -879,23 +919,25 @@ in_line(MACRO_PROT_ARGS)
 		 */
 
 		if (MDOC_MAX != ntok) {
-			if (scope && ! rew_elem(m, tok))
+			if (scope && ! rew_elem(mdoc, tok))
 				return(0);
 			if (nc && 0 == cnt) {
-				if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
+				if ( ! mdoc_elem_alloc(mdoc, line,
+						ppos, tok, arg))
 					return(0);
-				if ( ! rew_last(m, m->last))
+				if ( ! rew_last(mdoc, mdoc->last))
 					return(0);
 			} else if ( ! nc && 0 == cnt) {
 				mdoc_argv_free(arg);
-				mdoc_pmsg(m, line, ppos, MANDOCERR_MACROEMPTY);
+				mdoc_pmsg(mdoc, line, ppos,
+					MANDOCERR_MACROEMPTY);
 			}
 
-			if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
+			if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
 				return(0);
 			if ( ! nl)
 				return(1);
-			return(append_delims(m, line, pos, buf));
+			return(append_delims(mdoc, line, pos, buf));
 		} 
 
 		/* 
@@ -917,7 +959,8 @@ in_line(MACRO_PROT_ARGS)
 			 */
 			if (0 == cnt && (nc || MDOC_Li == tok) && 
 					DELIM_CLOSE == d && ! scope) {
-				if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
+				if ( ! mdoc_elem_alloc(mdoc, line,
+						ppos, tok, arg))
 					return(0);
 				if (MDOC_Ar == tok || MDOC_Li == tok || 
 						MDOC_Fl == tok)
@@ -928,11 +971,11 @@ in_line(MACRO_PROT_ARGS)
 			 * Close out our scope, if one is open, before
 			 * any punctuation.
 			 */
-			if (scope && ! rew_elem(m, tok))
+			if (scope && ! rew_elem(mdoc, tok))
 				return(0);
 			scope = 0;
 		} else if ( ! scope) {
-			if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
+			if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
 				return(0);
 			scope = 1;
 		}
@@ -940,7 +983,8 @@ in_line(MACRO_PROT_ARGS)
 		if (DELIM_NONE == d)
 			cnt++;
 
-		if ( ! dword(m, line, la, p, d))
+		if ( ! dword(mdoc, line, la, p, d,
+		    MDOC_JOIN & mdoc_macros[tok].flags))
 			return(0);
 
 		/*
@@ -949,13 +993,13 @@ in_line(MACRO_PROT_ARGS)
 		 * having to parse out spaces.
 		 */
 		if (scope && MDOC_Fl == tok) {
-			if ( ! rew_elem(m, tok))
+			if ( ! rew_elem(mdoc, tok))
 				return(0);
 			scope = 0;
 		}
 	}
 
-	if (scope && ! rew_elem(m, tok))
+	if (scope && ! rew_elem(mdoc, tok))
 		return(0);
 
 	/*
@@ -965,18 +1009,18 @@ in_line(MACRO_PROT_ARGS)
 	 */
 
 	if (nc && 0 == cnt) {
-		if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
+		if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
 			return(0);
-		if ( ! rew_last(m, m->last))
+		if ( ! rew_last(mdoc, mdoc->last))
 			return(0);
 	} else if ( ! nc && 0 == cnt) {
 		mdoc_argv_free(arg);
-		mdoc_pmsg(m, line, ppos, MANDOCERR_MACROEMPTY);
+		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_MACROEMPTY);
 	}
 
 	if ( ! nl)
 		return(1);
-	return(append_delims(m, line, pos, buf));
+	return(append_delims(mdoc, line, pos, buf));
 }
 
 
@@ -994,14 +1038,14 @@ blk_full(MACRO_PROT_ARGS)
 	enum margverr	  av;
 	char		 *p;
 
-	nl = MDOC_NEWLINE & m->flags;
+	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/* Close out prior implicit scope. */
 
 	if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
-		if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
+		if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
 			return(0);
-		if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
+		if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
 			return(0);
 	}
 
@@ -1016,7 +1060,7 @@ blk_full(MACRO_PROT_ARGS)
 
 	for (arg = NULL;; ) {
 		la = *pos;
-		av = mdoc_argv(m, line, tok, &arg, pos, buf);
+		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
 
 		if (ARGV_WORD == av) {
 			*pos = la;
@@ -1032,7 +1076,7 @@ blk_full(MACRO_PROT_ARGS)
 		return(0);
 	}
 
-	if ( ! mdoc_block_alloc(m, line, ppos, tok, arg))
+	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, arg))
 		return(0);
 
 	head = body = NULL;
@@ -1042,8 +1086,8 @@ blk_full(MACRO_PROT_ARGS)
 	 * parsed, even though `It' macros in general are parsed.
 	 */
 	nparsed = MDOC_It == tok &&
-		MDOC_Bl == m->last->parent->tok &&
-		LIST_diag == m->last->parent->norm->Bl.type;
+		MDOC_Bl == mdoc->last->parent->tok &&
+		LIST_diag == mdoc->last->parent->norm->Bl.type;
 
 	/*
 	 * The `Nd' macro has all arguments in its body: it's a hybrid
@@ -1051,15 +1095,18 @@ blk_full(MACRO_PROT_ARGS)
 	 */
 
 	if (MDOC_Nd == tok) {
-		if ( ! mdoc_head_alloc(m, line, ppos, tok))
+		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
 			return(0);
-		head = m->last;
-		if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
+		head = mdoc->last;
+		if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
 			return(0);
-		if ( ! mdoc_body_alloc(m, line, ppos, tok))
+		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
 			return(0);
-		body = m->last;
-	} 
+		body = mdoc->last;
+	}
+
+	if (MDOC_Bk == tok)
+		mdoc->flags |= MDOC_KEEP;
 
 	ac = ARGS_ERROR;
 
@@ -1067,7 +1114,7 @@ blk_full(MACRO_PROT_ARGS)
 		la = *pos;
 		/* Initialise last-phrase-type with ARGS_PEND. */
 		lac = ARGS_ERROR == ac ? ARGS_PEND : ac;
-		ac = mdoc_args(m, line, pos, buf, tok, &p);
+		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
 
 		if (ARGS_PUNCT == ac)
 			break;
@@ -1085,11 +1132,11 @@ blk_full(MACRO_PROT_ARGS)
 			 * reopen our scope if the last parse was a
 			 * phrase or partial phrase.
 			 */
-			if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
+			if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
 				return(0);
-			if ( ! mdoc_body_alloc(m, line, ppos, tok))
+			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
 				return(0);
-			body = m->last;
+			body = mdoc->last;
 			break;
 		}
 
@@ -1104,7 +1151,7 @@ blk_full(MACRO_PROT_ARGS)
 				ARGS_PPHRASE != ac &&
 				ARGS_QWORD != ac &&
 				DELIM_OPEN == mdoc_isdelim(p)) {
-			if ( ! dword(m, line, la, p, DELIM_OPEN))
+			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
 				return(0);
 			continue;
 		}
@@ -1112,9 +1159,9 @@ blk_full(MACRO_PROT_ARGS)
 		/* Open a head if one hasn't been opened. */
 
 		if (NULL == head) {
-			if ( ! mdoc_head_alloc(m, line, ppos, tok))
+			if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
 				return(0);
-			head = m->last;
+			head = mdoc->last;
 		}
 
 		if (ARGS_PHRASE == ac || 
@@ -1126,14 +1173,14 @@ blk_full(MACRO_PROT_ARGS)
 			 */
 
 			mtt = body ? MDOC_BODY : MDOC_HEAD;
-			if ( ! rew_sub(mtt, m, tok, line, ppos))
+			if ( ! rew_sub(mtt, mdoc, tok, line, ppos))
 				return(0);
 			
 			/* Then allocate our body context. */
 
-			if ( ! mdoc_body_alloc(m, line, ppos, tok))
+			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
 				return(0);
-			body = m->last;
+			body = mdoc->last;
 
 			/*
 			 * Process phrases: set whether we're in a
@@ -1142,14 +1189,14 @@ blk_full(MACRO_PROT_ARGS)
 			 */
 
 			if (ARGS_PPHRASE == ac)
-				m->flags |= MDOC_PPHRASE;
+				mdoc->flags |= MDOC_PPHRASE;
 			if (ARGS_PEND == ac && ARGS_PPHRASE == lac)
-				m->flags |= MDOC_PPHRASE;
+				mdoc->flags |= MDOC_PPHRASE;
 
-			if ( ! phrase(m, line, la, buf))
+			if ( ! phrase(mdoc, line, la, buf))
 				return(0);
 
-			m->flags &= ~MDOC_PPHRASE;
+			mdoc->flags &= ~MDOC_PPHRASE;
 			continue;
 		}
 
@@ -1157,23 +1204,24 @@ blk_full(MACRO_PROT_ARGS)
 			MDOC_MAX : lookup(tok, p);
 
 		if (MDOC_MAX == ntok) {
-			if ( ! dword(m, line, la, p, DELIM_MAX))
+			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
+			    MDOC_JOIN & mdoc_macros[tok].flags))
 				return(0);
 			continue;
 		}
 
-		if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
+		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
 			return(0);
 		break;
 	}
 
 	if (NULL == head) {
-		if ( ! mdoc_head_alloc(m, line, ppos, tok))
+		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
 			return(0);
-		head = m->last;
+		head = mdoc->last;
 	}
 	
-	if (nl && ! append_delims(m, line, pos, buf))
+	if (nl && ! append_delims(mdoc, line, pos, buf))
 		return(0);
 
 	/* If we've already opened our body, exit now. */
@@ -1187,7 +1235,7 @@ blk_full(MACRO_PROT_ARGS)
 	 * head to body until the rew_sub() call closing out that
 	 * sub-block.
 	 */
-	for (n = m->last; n && n != head; n = n->parent) {
+	for (n = mdoc->last; n && n != head; n = n->parent) {
 		if (MDOC_BLOCK == n->type && 
 				MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
 				! (MDOC_VALID & n->flags)) {
@@ -1198,21 +1246,21 @@ blk_full(MACRO_PROT_ARGS)
 
 	/* Close out scopes to remain in a consistent state. */
 
-	if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
+	if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
 		return(0);
-	if ( ! mdoc_body_alloc(m, line, ppos, tok))
+	if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
 		return(0);
 
 out:
-	if ( ! (MDOC_FREECOL & m->flags))
+	if ( ! (MDOC_FREECOL & mdoc->flags))
 		return(1);
 
-	if ( ! rew_sub(MDOC_BODY, m, tok, line, ppos))
+	if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
 		return(0);
-	if ( ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
+	if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
 		return(0);
 
-	m->flags &= ~MDOC_FREECOL;
+	mdoc->flags &= ~MDOC_FREECOL;
 	return(1);
 }
 
@@ -1228,7 +1276,7 @@ blk_part_imp(MACRO_PROT_ARGS)
 	struct mdoc_node *body; /* saved body context */
 	struct mdoc_node *n;
 
-	nl = MDOC_NEWLINE & m->flags;
+	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/*
 	 * A macro that spans to the end of the line.  This is generally
@@ -1239,14 +1287,14 @@ blk_part_imp(MACRO_PROT_ARGS)
 	 * or more closing punctuation nodes.
 	 */
 
-	if ( ! mdoc_block_alloc(m, line, ppos, tok, NULL))
+	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
 		return(0);
 
-	blk = m->last;
+	blk = mdoc->last;
 
-	if ( ! mdoc_head_alloc(m, line, ppos, tok))
+	if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
 		return(0);
-	if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
+	if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
 		return(0);
 
 	/*
@@ -1257,7 +1305,7 @@ blk_part_imp(MACRO_PROT_ARGS)
 
 	for (body = NULL; ; ) {
 		la = *pos;
-		ac = mdoc_args(m, line, pos, buf, tok, &p);
+		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
@@ -1268,26 +1316,27 @@ blk_part_imp(MACRO_PROT_ARGS)
 
 		if (NULL == body && ARGS_QWORD != ac &&
 				DELIM_OPEN == mdoc_isdelim(p)) {
-			if ( ! dword(m, line, la, p, DELIM_OPEN))
+			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
 				return(0);
 			continue;
-		} 
+		}
 
 		if (NULL == body) {
-		       if ( ! mdoc_body_alloc(m, line, ppos, tok))
+		       if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
 			       return(0);
-			body = m->last;
+			body = mdoc->last;
 		}
 
 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
 
 		if (MDOC_MAX == ntok) {
-			if ( ! dword(m, line, la, p, DELIM_MAX))
+			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
+			    MDOC_JOIN & mdoc_macros[tok].flags))
 				return(0);
 			continue;
 		}
 
-		if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
+		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
 			return(0);
 		break;
 	}
@@ -1295,9 +1344,9 @@ blk_part_imp(MACRO_PROT_ARGS)
 	/* Clean-ups to leave in a consistent state. */
 
 	if (NULL == body) {
-		if ( ! mdoc_body_alloc(m, line, ppos, tok))
+		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
 			return(0);
-		body = m->last;
+		body = mdoc->last;
 	}
 
 	for (n = body->child; n && n->next; n = n->next)
@@ -1324,12 +1373,13 @@ blk_part_imp(MACRO_PROT_ARGS)
 	 * postpone closing out the current block
 	 * until the rew_sub() call closing out the sub-block.
 	 */
-	for (n = m->last; n && n != body && n != blk->parent; n = n->parent) {
+	for (n = mdoc->last; n && n != body && n != blk->parent;
+			n = n->parent) {
 		if (MDOC_BLOCK == n->type &&
 		    MDOC_EXPLICIT & mdoc_macros[n->tok].flags &&
 		    ! (MDOC_VALID & n->flags)) {
-			make_pending(n, tok, m, line, ppos);
-			if ( ! mdoc_endbody_alloc(m, line, ppos,
+			make_pending(n, tok, mdoc, line, ppos);
+			if ( ! mdoc_endbody_alloc(mdoc, line, ppos,
 			    tok, body, ENDBODY_NOSPACE))
 				return(0);
 			return(1);
@@ -1343,22 +1393,29 @@ blk_part_imp(MACRO_PROT_ARGS)
 	 * crufty use of `Op' breakage.
 	 */
 	if (n != body)
-		mandoc_vmsg(MANDOCERR_SCOPENEST, m->parse, line, ppos, 
+		mandoc_vmsg(MANDOCERR_SCOPENEST, mdoc->parse, line, ppos, 
 				"%s broken", mdoc_macronames[tok]);
 
-	if (n && ! rew_sub(MDOC_BODY, m, tok, line, ppos))
+	if (n && ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
 		return(0);
 
 	/* Standard appending of delimiters. */
 
-	if (nl && ! append_delims(m, line, pos, buf))
+	if (nl && ! append_delims(mdoc, line, pos, buf))
 		return(0);
 
 	/* Rewind scope, if applicable. */
 
-	if (n && ! rew_sub(MDOC_BLOCK, m, tok, line, ppos))
+	if (n && ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
 		return(0);
 
+	/* Move trailing .Ns out of scope. */
+
+	for (n = body->child; n && n->next; n = n->next)
+		/* Do nothing. */ ;
+	if (n && MDOC_Ns == n->tok)
+		mdoc_node_relink(mdoc, n);
+
 	return(1);
 }
 
@@ -1373,7 +1430,7 @@ blk_part_exp(MACRO_PROT_ARGS)
 	char		 *p;
 	enum mdoct	  ntok;
 
-	nl = MDOC_NEWLINE & m->flags;
+	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/*
 	 * The opening of an explicit macro having zero or more leading
@@ -1381,12 +1438,12 @@ blk_part_exp(MACRO_PROT_ARGS)
 	 * case of `Eo'); and a body that may be empty.
 	 */
 
-	if ( ! mdoc_block_alloc(m, line, ppos, tok, NULL))
+	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
 		return(0); 
 
 	for (head = body = NULL; ; ) {
 		la = *pos;
-		ac = mdoc_args(m, line, pos, buf, tok, &p);
+		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
@@ -1400,16 +1457,16 @@ blk_part_exp(MACRO_PROT_ARGS)
 		if (NULL == head && ARGS_QWORD != ac &&
 				DELIM_OPEN == mdoc_isdelim(p)) {
 			assert(NULL == body);
-			if ( ! dword(m, line, la, p, DELIM_OPEN))
+			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
 				return(0);
 			continue;
-		} 
+		}
 
 		if (NULL == head) {
 			assert(NULL == body);
-			if ( ! mdoc_head_alloc(m, line, ppos, tok))
+			if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
 				return(0);
-			head = m->last;
+			head = mdoc->last;
 		}
 
 		/*
@@ -1421,14 +1478,14 @@ blk_part_exp(MACRO_PROT_ARGS)
 			assert(head);
 			/* No check whether it's a macro! */
 			if (MDOC_Eo == tok)
-				if ( ! dword(m, line, la, p, DELIM_MAX))
+				if ( ! dword(mdoc, line, la, p, DELIM_MAX, 0))
 					return(0);
 
-			if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
+			if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
 				return(0);
-			if ( ! mdoc_body_alloc(m, line, ppos, tok))
+			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
 				return(0);
-			body = m->last;
+			body = mdoc->last;
 
 			if (MDOC_Eo == tok)
 				continue;
@@ -1439,12 +1496,13 @@ blk_part_exp(MACRO_PROT_ARGS)
 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
 
 		if (MDOC_MAX == ntok) {
-			if ( ! dword(m, line, la, p, DELIM_MAX))
+			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
+			    MDOC_JOIN & mdoc_macros[tok].flags))
 				return(0);
 			continue;
 		}
 
-		if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
+		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
 			return(0);
 		break;
 	}
@@ -1452,13 +1510,13 @@ blk_part_exp(MACRO_PROT_ARGS)
 	/* Clean-up to leave in a consistent state. */
 
 	if (NULL == head)
-		if ( ! mdoc_head_alloc(m, line, ppos, tok))
+		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
 			return(0);
 
 	if (NULL == body) {
-		if ( ! rew_sub(MDOC_HEAD, m, tok, line, ppos))
+		if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
 			return(0);
-		if ( ! mdoc_body_alloc(m, line, ppos, tok))
+		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
 			return(0);
 	}
 
@@ -1466,7 +1524,7 @@ blk_part_exp(MACRO_PROT_ARGS)
 
 	if ( ! nl)
 		return(1);
-	return(append_delims(m, line, pos, buf));
+	return(append_delims(mdoc, line, pos, buf));
 }
 
 
@@ -1481,7 +1539,7 @@ in_line_argn(MACRO_PROT_ARGS)
 	char		*p;
 	enum mdoct	 ntok;
 
-	nl = MDOC_NEWLINE & m->flags;
+	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/*
 	 * A line macro that has a fixed number of arguments (maxargs).
@@ -1513,7 +1571,7 @@ in_line_argn(MACRO_PROT_ARGS)
 
 	for (arg = NULL; ; ) {
 		la = *pos;
-		av = mdoc_argv(m, line, tok, &arg, pos, buf);
+		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
 
 		if (ARGV_WORD == av) {
 			*pos = la;
@@ -1531,7 +1589,7 @@ in_line_argn(MACRO_PROT_ARGS)
 
 	for (flushed = j = 0; ; ) {
 		la = *pos;
-		ac = mdoc_args(m, line, pos, buf, tok, &p);
+		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
@@ -1543,15 +1601,15 @@ in_line_argn(MACRO_PROT_ARGS)
 		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) && 
 				ARGS_QWORD != ac && 0 == j && 
 				DELIM_OPEN == mdoc_isdelim(p)) {
-			if ( ! dword(m, line, la, p, DELIM_OPEN))
+			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
 				return(0);
 			continue;
 		} else if (0 == j)
-		       if ( ! mdoc_elem_alloc(m, line, la, tok, arg))
+		       if ( ! mdoc_elem_alloc(mdoc, line, la, tok, arg))
 			       return(0);
 
 		if (j == maxargs && ! flushed) {
-			if ( ! rew_elem(m, tok))
+			if ( ! rew_elem(mdoc, tok))
 				return(0);
 			flushed = 1;
 		}
@@ -1559,10 +1617,10 @@ in_line_argn(MACRO_PROT_ARGS)
 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
 
 		if (MDOC_MAX != ntok) {
-			if ( ! flushed && ! rew_elem(m, tok))
+			if ( ! flushed && ! rew_elem(mdoc, tok))
 				return(0);
 			flushed = 1;
-			if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
+			if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
 				return(0);
 			j++;
 			break;
@@ -1572,26 +1630,27 @@ in_line_argn(MACRO_PROT_ARGS)
 				ARGS_QWORD != ac &&
 				! flushed &&
 				DELIM_NONE != mdoc_isdelim(p)) {
-			if ( ! rew_elem(m, tok))
+			if ( ! rew_elem(mdoc, tok))
 				return(0);
 			flushed = 1;
 		}
 
-		if ( ! dword(m, line, la, p, DELIM_MAX))
+		if ( ! dword(mdoc, line, la, p, DELIM_MAX,
+		    MDOC_JOIN & mdoc_macros[tok].flags))
 			return(0);
 		j++;
 	}
 
-	if (0 == j && ! mdoc_elem_alloc(m, line, la, tok, arg))
+	if (0 == j && ! mdoc_elem_alloc(mdoc, line, la, tok, arg))
 	       return(0);
 
 	/* Close out in a consistent state. */
 
-	if ( ! flushed && ! rew_elem(m, tok))
+	if ( ! flushed && ! rew_elem(mdoc, tok))
 		return(0);
 	if ( ! nl)
 		return(1);
-	return(append_delims(m, line, pos, buf));
+	return(append_delims(mdoc, line, pos, buf));
 }
 
 
@@ -1608,13 +1667,13 @@ in_line_eoln(MACRO_PROT_ARGS)
 	assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));
 
 	if (tok == MDOC_Pp)
-		rew_sub(MDOC_BLOCK, m, MDOC_Nm, line, ppos);
+		rew_sub(MDOC_BLOCK, mdoc, MDOC_Nm, line, ppos);
 
 	/* Parse macro arguments. */
 
 	for (arg = NULL; ; ) {
 		la = *pos;
-		av = mdoc_argv(m, line, tok, &arg, pos, buf);
+		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
 
 		if (ARGV_WORD == av) {
 			*pos = la;
@@ -1631,14 +1690,14 @@ in_line_eoln(MACRO_PROT_ARGS)
 
 	/* Open element scope. */
 
-	if ( ! mdoc_elem_alloc(m, line, ppos, tok, arg))
+	if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
 		return(0);
 
 	/* Parse argument terms. */
 
 	for (;;) {
 		la = *pos;
-		ac = mdoc_args(m, line, pos, buf, tok, &p);
+		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
@@ -1648,19 +1707,20 @@ in_line_eoln(MACRO_PROT_ARGS)
 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
 
 		if (MDOC_MAX == ntok) {
-			if ( ! dword(m, line, la, p, DELIM_MAX))
+			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
+			    MDOC_JOIN & mdoc_macros[tok].flags))
 				return(0);
 			continue;
 		}
 
-		if ( ! rew_elem(m, tok))
+		if ( ! rew_elem(mdoc, tok))
 			return(0);
-		return(mdoc_macro(m, ntok, line, la, pos, buf));
+		return(mdoc_macro(mdoc, ntok, line, la, pos, buf));
 	}
 
 	/* Close out (no delimiters). */
 
-	return(rew_elem(m, tok));
+	return(rew_elem(mdoc, tok));
 }
 
 
@@ -1670,15 +1730,15 @@ ctx_synopsis(MACRO_PROT_ARGS)
 {
 	int		 nl;
 
-	nl = MDOC_NEWLINE & m->flags;
+	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/* If we're not in the SYNOPSIS, go straight to in-line. */
-	if ( ! (MDOC_SYNOPSIS & m->flags))
-		return(in_line(m, tok, line, ppos, pos, buf));
+	if ( ! (MDOC_SYNOPSIS & mdoc->flags))
+		return(in_line(mdoc, tok, line, ppos, pos, buf));
 
 	/* If we're a nested call, same place. */
 	if ( ! nl)
-		return(in_line(m, tok, line, ppos, pos, buf));
+		return(in_line(mdoc, tok, line, ppos, pos, buf));
 
 	/*
 	 * XXX: this will open a block scope; however, if later we end
@@ -1686,9 +1746,9 @@ ctx_synopsis(MACRO_PROT_ARGS)
 	 * the formatting.  Be careful.
 	 */
 	if (MDOC_Nm == tok)
-		return(blk_full(m, tok, line, ppos, pos, buf));
+		return(blk_full(mdoc, tok, line, ppos, pos, buf));
 	assert(MDOC_Vt == tok);
-	return(blk_part_imp(m, tok, line, ppos, pos, buf));
+	return(blk_part_imp(mdoc, tok, line, ppos, pos, buf));
 }
 
 
@@ -1697,7 +1757,7 @@ static int
 obsolete(MACRO_PROT_ARGS)
 {
 
-	mdoc_pmsg(m, line, ppos, MANDOCERR_MACROOBS);
+	mdoc_pmsg(mdoc, line, ppos, MANDOCERR_MACROOBS);
 	return(1);
 }
 
@@ -1708,7 +1768,7 @@ obsolete(MACRO_PROT_ARGS)
  * macro is encountered.
  */
 static int
-phrase(struct mdoc *m, int line, int ppos, char *buf)
+phrase(struct mdoc *mdoc, int line, int ppos, char *buf)
 {
 	int		 la, pos;
 	enum margserr	 ac;
@@ -1718,7 +1778,7 @@ phrase(struct mdoc *m, int line, int ppos, char *buf)
 	for (pos = ppos; ; ) {
 		la = pos;
 
-		ac = mdoc_zargs(m, line, &pos, buf, &p);
+		ac = mdoc_zargs(mdoc, line, &pos, buf, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
@@ -1728,14 +1788,14 @@ phrase(struct mdoc *m, int line, int ppos, char *buf)
 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);
 
 		if (MDOC_MAX == ntok) {
-			if ( ! dword(m, line, la, p, DELIM_MAX))
+			if ( ! dword(mdoc, line, la, p, DELIM_MAX, 1))
 				return(0);
 			continue;
 		}
 
-		if ( ! mdoc_macro(m, ntok, line, la, &pos, buf))
+		if ( ! mdoc_macro(mdoc, ntok, line, la, &pos, buf))
 			return(0);
-		return(append_delims(m, line, &pos, buf));
+		return(append_delims(mdoc, line, &pos, buf));
 	}
 
 	return(1);
@@ -1746,24 +1806,30 @@ phrase(struct mdoc *m, int line, int ppos, char *buf)
 static int
 phrase_ta(MACRO_PROT_ARGS)
 {
+	struct mdoc_node *n;
 	int		  la;
 	enum mdoct	  ntok;
 	enum margserr	  ac;
 	char		 *p;
 
-	/*
-	 * FIXME: this is overly restrictive: if the `Ta' is unexpected,
-	 * it should simply error out with ARGSLOST.
-	 */
+	/* Make sure we are in a column list or ignore this macro. */
+	n = mdoc->last;
+	while (NULL != n && MDOC_Bl != n->tok)
+		n = n->parent;
+	if (NULL == n || LIST_column != n->norm->Bl.type) {
+		mdoc_pmsg(mdoc, line, ppos, MANDOCERR_STRAYTA);
+		return(1);
+	}
 
-	if ( ! rew_sub(MDOC_BODY, m, MDOC_It, line, ppos))
+	/* Advance to the next column. */
+	if ( ! rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos))
 		return(0);
-	if ( ! mdoc_body_alloc(m, line, ppos, MDOC_It))
+	if ( ! mdoc_body_alloc(mdoc, line, ppos, MDOC_It))
 		return(0);
 
 	for (;;) {
 		la = *pos;
-		ac = mdoc_zargs(m, line, pos, buf, &p);
+		ac = mdoc_zargs(mdoc, line, pos, buf, &p);
 
 		if (ARGS_ERROR == ac)
 			return(0);
@@ -1773,14 +1839,15 @@ phrase_ta(MACRO_PROT_ARGS)
 		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);
 
 		if (MDOC_MAX == ntok) {
-			if ( ! dword(m, line, la, p, DELIM_MAX))
+			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
+			    MDOC_JOIN & mdoc_macros[tok].flags))
 				return(0);
 			continue;
 		}
 
-		if ( ! mdoc_macro(m, ntok, line, la, pos, buf))
+		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
 			return(0);
-		return(append_delims(m, line, pos, buf));
+		return(append_delims(mdoc, line, pos, buf));
 	}
 
 	return(1);
diff --git a/contrib/mdocml/mdoc_man.c b/contrib/mdocml/mdoc_man.c
index 9d7d2ca2386..6ee8b3abf45 100644
--- a/contrib/mdocml/mdoc_man.c
+++ b/contrib/mdocml/mdoc_man.c
@@ -1,6 +1,6 @@
-/*	$Id: mdoc_man.c,v 1.9 2011/10/24 21:47:59 schwarze Exp $ */
+/*	$Id: mdoc_man.c,v 1.57 2013/12/25 22:00:45 schwarze Exp $ */
 /*
- * Copyright (c) 2011 Ingo Schwarze 
+ * Copyright (c) 2011, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,22 +18,18 @@
 #include "config.h"
 #endif
 
+#include 
 #include 
 #include 
 
 #include "mandoc.h"
+#include "out.h"
 #include "man.h"
 #include "mdoc.h"
 #include "main.h"
 
-#define	DECL_ARGS const struct mdoc_meta *m, \
-		  const struct mdoc_node *n, \
-		  struct mman *mm
-
-struct	mman {
-	int		  need_space; /* next word needs prior ws */
-	int		  need_nl; /* next word needs prior nl */
-};
+#define	DECL_ARGS const struct mdoc_meta *meta, \
+		  const struct mdoc_node *n
 
 struct	manact {
 	int		(*cond)(DECL_ARGS); /* DON'T run actions */
@@ -45,29 +41,74 @@ struct	manact {
 
 static	int	  cond_body(DECL_ARGS);
 static	int	  cond_head(DECL_ARGS);
+static  void	  font_push(char);
+static	void	  font_pop(void);
+static	void	  mid_it(void);
+static	void	  post__t(DECL_ARGS);
 static	void	  post_bd(DECL_ARGS);
+static	void	  post_bf(DECL_ARGS);
+static	void	  post_bk(DECL_ARGS);
+static	void	  post_bl(DECL_ARGS);
 static	void	  post_dl(DECL_ARGS);
 static	void	  post_enc(DECL_ARGS);
+static	void	  post_eo(DECL_ARGS);
+static	void	  post_fa(DECL_ARGS);
+static	void	  post_fd(DECL_ARGS);
+static	void	  post_fl(DECL_ARGS);
+static	void	  post_fn(DECL_ARGS);
+static	void	  post_fo(DECL_ARGS);
+static	void	  post_font(DECL_ARGS);
+static	void	  post_in(DECL_ARGS);
+static	void	  post_it(DECL_ARGS);
+static	void	  post_lb(DECL_ARGS);
 static	void	  post_nm(DECL_ARGS);
 static	void	  post_percent(DECL_ARGS);
 static	void	  post_pf(DECL_ARGS);
 static	void	  post_sect(DECL_ARGS);
 static	void	  post_sp(DECL_ARGS);
+static	void	  post_vt(DECL_ARGS);
+static	int	  pre__t(DECL_ARGS);
+static	int	  pre_an(DECL_ARGS);
 static	int	  pre_ap(DECL_ARGS);
 static	int	  pre_bd(DECL_ARGS);
+static	int	  pre_bf(DECL_ARGS);
+static	int	  pre_bk(DECL_ARGS);
+static	int	  pre_bl(DECL_ARGS);
 static	int	  pre_br(DECL_ARGS);
 static	int	  pre_bx(DECL_ARGS);
 static	int	  pre_dl(DECL_ARGS);
 static	int	  pre_enc(DECL_ARGS);
+static	int	  pre_em(DECL_ARGS);
+static	int	  pre_fa(DECL_ARGS);
+static	int	  pre_fd(DECL_ARGS);
+static	int	  pre_fl(DECL_ARGS);
+static	int	  pre_fn(DECL_ARGS);
+static	int	  pre_fo(DECL_ARGS);
+static	int	  pre_ft(DECL_ARGS);
+static	int	  pre_in(DECL_ARGS);
 static	int	  pre_it(DECL_ARGS);
+static	int	  pre_lk(DECL_ARGS);
+static	int	  pre_li(DECL_ARGS);
 static	int	  pre_nm(DECL_ARGS);
+static	int	  pre_no(DECL_ARGS);
 static	int	  pre_ns(DECL_ARGS);
 static	int	  pre_pp(DECL_ARGS);
+static	int	  pre_rs(DECL_ARGS);
+static	int	  pre_sm(DECL_ARGS);
 static	int	  pre_sp(DECL_ARGS);
 static	int	  pre_sect(DECL_ARGS);
+static	int	  pre_sy(DECL_ARGS);
+static	void	  pre_syn(const struct mdoc_node *);
+static	int	  pre_vt(DECL_ARGS);
 static	int	  pre_ux(DECL_ARGS);
 static	int	  pre_xr(DECL_ARGS);
-static	void	  print_word(struct mman *, const char *);
+static	void	  print_word(const char *);
+static	void	  print_line(const char *, int);
+static	void	  print_block(const char *, int);
+static	void	  print_offs(const char *);
+static	void	  print_width(const char *,
+				const struct mdoc_node *, size_t);
+static	void	  print_count(int *);
 static	void	  print_node(DECL_ARGS);
 
 static	const struct manact manacts[MDOC_MAX + 1] = {
@@ -82,74 +123,74 @@ static	const struct manact manacts[MDOC_MAX + 1] = {
 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
 	{ cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
-	{ NULL, NULL, NULL, NULL, NULL }, /* Bl */
+	{ cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
 	{ NULL, NULL, NULL, NULL, NULL }, /* El */
-	{ NULL, pre_it, NULL, NULL, NULL }, /* _It */
-	{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Ad */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _An */
-	{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Ar */
-	{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Cd */
-	{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Cm */
-	{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Dv */
-	{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Er */
-	{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Ev */
+	{ NULL, pre_it, post_it, NULL, NULL }, /* It */
+	{ NULL, pre_em, post_font, NULL, NULL }, /* Ad */
+	{ NULL, pre_an, NULL, NULL, NULL }, /* An */
+	{ NULL, pre_em, post_font, NULL, NULL }, /* Ar */
+	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cd */
+	{ NULL, pre_sy, post_font, NULL, NULL }, /* Cm */
+	{ NULL, pre_li, post_font, NULL, NULL }, /* Dv */
+	{ NULL, pre_li, post_font, NULL, NULL }, /* Er */
+	{ NULL, pre_li, post_font, NULL, NULL }, /* Ev */
 	{ NULL, pre_enc, post_enc, "The \\fB",
 	    "\\fP\nutility exits 0 on success, and >0 if an error occurs."
 	    }, /* Ex */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Fa */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Fd */
-	{ NULL, pre_enc, post_enc, "\\fB-", "\\fP" }, /* Fl */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Fn */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Ft */
-	{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Ic */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _In */
-	{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Li */
+	{ NULL, pre_fa, post_fa, NULL, NULL }, /* Fa */
+	{ NULL, pre_fd, post_fd, NULL, NULL }, /* Fd */
+	{ NULL, pre_fl, post_fl, NULL, NULL }, /* Fl */
+	{ NULL, pre_fn, post_fn, NULL, NULL }, /* Fn */
+	{ NULL, pre_ft, post_font, NULL, NULL }, /* Ft */
+	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ic */
+	{ NULL, pre_in, post_in, NULL, NULL }, /* In */
+	{ NULL, pre_li, post_font, NULL, NULL }, /* Li */
 	{ cond_head, pre_enc, NULL, "\\- ", NULL }, /* Nd */
 	{ NULL, pre_nm, post_nm, NULL, NULL }, /* Nm */
 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Op */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Ot */
-	{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Pa */
+	{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
 	{ NULL, pre_enc, post_enc, "The \\fB",
 		"\\fP\nfunction returns the value 0 if successful;\n"
 		"otherwise the value -1 is returned and the global\n"
 		"variable \\fIerrno\\fP is set to indicate the error."
 		}, /* Rv */
 	{ NULL, NULL, NULL, NULL, NULL }, /* St */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Va */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Vt */
+	{ NULL, pre_em, post_font, NULL, NULL }, /* Va */
+	{ NULL, pre_vt, post_vt, NULL, NULL }, /* Vt */
 	{ NULL, pre_xr, NULL, NULL, NULL }, /* Xr */
-	{ NULL, NULL, post_percent, NULL, NULL }, /* _%A */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%B */
-	{ NULL, NULL, post_percent, NULL, NULL }, /* _%D */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%I */
-	{ NULL, pre_enc, post_percent, "\\fI", "\\fP" }, /* %J */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%N */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%O */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%P */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%R */
-	{ NULL, pre_enc, post_percent, "\"", "\"" }, /* %T */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%V */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %A */
+	{ NULL, pre_em, post_percent, NULL, NULL }, /* %B */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %D */
+	{ NULL, pre_em, post_percent, NULL, NULL }, /* %I */
+	{ NULL, pre_em, post_percent, NULL, NULL }, /* %J */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %N */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %O */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %P */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %R */
+	{ NULL, pre__t, post__t, NULL, NULL }, /* %T */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %V */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Ac */
 	{ cond_body, pre_enc, post_enc, "<", ">" }, /* Ao */
 	{ cond_body, pre_enc, post_enc, "<", ">" }, /* Aq */
 	{ NULL, NULL, NULL, NULL, NULL }, /* At */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Bf */
+	{ NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
 	{ NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
 	{ NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Db */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
-	{ cond_body, pre_enc, post_enc, "``", "''" }, /* Do */
-	{ cond_body, pre_enc, post_enc, "``", "''" }, /* Dq */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Ec */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Ef */
-	{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Em */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Eo */
+	{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Do */
+	{ cond_body, pre_enc, post_enc, "\\(lq", "\\(rq" }, /* Dq */
+	{ NULL, NULL, NULL, NULL, NULL }, /* Ec */
+	{ NULL, NULL, NULL, NULL, NULL }, /* Ef */
+	{ NULL, pre_em, post_font, NULL, NULL }, /* Em */
+	{ NULL, NULL, post_eo, NULL, NULL }, /* Eo */
 	{ NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
-	{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Ms */
-	{ NULL, NULL, NULL, NULL, NULL }, /* No */
+	{ NULL, pre_sy, post_font, NULL, NULL }, /* Ms */
+	{ NULL, pre_no, NULL, NULL, NULL }, /* No */
 	{ NULL, pre_ns, NULL, NULL, NULL }, /* Ns */
 	{ NULL, pre_ux, NULL, "NetBSD", NULL }, /* Nx */
 	{ NULL, pre_ux, NULL, "OpenBSD", NULL }, /* Ox */
@@ -158,91 +199,324 @@ static	const struct manact manacts[MDOC_MAX + 1] = {
 	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Po */
 	{ cond_body, pre_enc, post_enc, "(", ")" }, /* Pq */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Qc */
-	{ cond_body, pre_enc, post_enc, "`", "'" }, /* Ql */
+	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Ql */
 	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qo */
 	{ cond_body, pre_enc, post_enc, "\"", "\"" }, /* Qq */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Re */
-	{ cond_body, pre_pp, NULL, NULL, NULL }, /* Rs */
+	{ cond_body, pre_rs, NULL, NULL, NULL }, /* Rs */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
-	{ cond_body, pre_enc, post_enc, "`", "'" }, /* So */
-	{ cond_body, pre_enc, post_enc, "`", "'" }, /* Sq */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Sm */
-	{ NULL, pre_enc, post_enc, "\\fI", "\\fP" }, /* Sx */
-	{ NULL, pre_enc, post_enc, "\\fB", "\\fP" }, /* Sy */
-	{ NULL, pre_enc, post_enc, "\\fR", "\\fP" }, /* Tn */
+	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* So */
+	{ cond_body, pre_enc, post_enc, "\\(oq", "\\(cq" }, /* Sq */
+	{ NULL, pre_sm, NULL, NULL, NULL }, /* Sm */
+	{ NULL, pre_em, post_font, NULL, NULL }, /* Sx */
+	{ NULL, pre_sy, post_font, NULL, NULL }, /* Sy */
+	{ NULL, pre_li, post_font, NULL, NULL }, /* Tn */
 	{ NULL, pre_ux, NULL, "UNIX", NULL }, /* Ux */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Xc */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Xo */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Fo */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Fc */
+	{ NULL, NULL, NULL, NULL, NULL }, /* Xc */
+	{ NULL, NULL, NULL, NULL, NULL }, /* Xo */
+	{ NULL, pre_fo, post_fo, NULL, NULL }, /* Fo */
+	{ NULL, NULL, NULL, NULL, NULL }, /* Fc */
 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Oo */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Oc */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Bk */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Ek */
+	{ NULL, pre_bk, post_bk, NULL, NULL }, /* Bk */
+	{ NULL, NULL, NULL, NULL, NULL }, /* Ek */
 	{ NULL, pre_ux, NULL, "is currently in beta test.", NULL }, /* Bt */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Hf */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Fr */
 	{ NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Lb */
+	{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
 	{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Lk */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Mt */
+	{ NULL, pre_lk, NULL, NULL, NULL }, /* Lk */
+	{ NULL, pre_em, post_font, NULL, NULL }, /* Mt */
 	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Brq */
 	{ cond_body, pre_enc, post_enc, "{", "}" }, /* Bro */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Brc */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%C */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Es */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _En */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %C */
+	{ NULL, NULL, NULL, NULL, NULL }, /* Es */
+	{ NULL, NULL, NULL, NULL, NULL }, /* En */
 	{ NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%Q */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
 	{ NULL, pre_br, NULL, NULL, NULL }, /* br */
 	{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _%U */
-	{ NULL, NULL, NULL, NULL, NULL }, /* _Ta */
+	{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
+	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
 	{ NULL, NULL, NULL, NULL, NULL }, /* ROOT */
 };
 
+static	int		outflags;
+#define	MMAN_spc	(1 << 0)  /* blank character before next word */
+#define	MMAN_spc_force	(1 << 1)  /* even before trailing punctuation */
+#define	MMAN_nl		(1 << 2)  /* break man(7) code line */
+#define	MMAN_br		(1 << 3)  /* break output line */
+#define	MMAN_sp		(1 << 4)  /* insert a blank output line */
+#define	MMAN_PP		(1 << 5)  /* reset indentation etc. */
+#define	MMAN_Sm		(1 << 6)  /* horizontal spacing mode */
+#define	MMAN_Bk		(1 << 7)  /* word keep mode */
+#define	MMAN_Bk_susp	(1 << 8)  /* suspend this (after a macro) */
+#define	MMAN_An_split	(1 << 9)  /* author mode is "split" */
+#define	MMAN_An_nosplit	(1 << 10) /* author mode is "nosplit" */
+#define	MMAN_PD		(1 << 11) /* inter-paragraph spacing disabled */
+#define	MMAN_nbrword	(1 << 12) /* do not break the next word */
+
+#define	BL_STACK_MAX	32
+
+static	size_t		Bl_stack[BL_STACK_MAX];  /* offsets [chars] */
+static	int		Bl_stack_post[BL_STACK_MAX];  /* add final .RE */
+static	int		Bl_stack_len;  /* number of nested Bl blocks */
+static	int		TPremain;  /* characters before tag is full */
+
+static	struct {
+	char	*head;
+	char	*tail;
+	size_t	 size;
+}	fontqueue;
+
 static void
-print_word(struct mman *mm, const char *s)
+font_push(char newfont)
 {
 
-	if (mm->need_nl) {
+	if (fontqueue.head + fontqueue.size <= ++fontqueue.tail) {
+		fontqueue.size += 8;
+		fontqueue.head = mandoc_realloc(fontqueue.head,
+				fontqueue.size);
+	}
+	*fontqueue.tail = newfont;
+	print_word("");
+	printf("\\f");
+	putchar(newfont);
+	outflags &= ~MMAN_spc;
+}
+
+static void
+font_pop(void)
+{
+
+	if (fontqueue.tail > fontqueue.head)
+		fontqueue.tail--;
+	outflags &= ~MMAN_spc;
+	print_word("");
+	printf("\\f");
+	putchar(*fontqueue.tail);
+}
+
+static void
+print_word(const char *s)
+{
+
+	if ((MMAN_PP | MMAN_sp | MMAN_br | MMAN_nl) & outflags) {
 		/* 
 		 * If we need a newline, print it now and start afresh.
 		 */
-		putchar('\n');
-		mm->need_space = 0;
-		mm->need_nl = 0;
-	} else if (mm->need_space && '\0' != s[0])
+		if (MMAN_PP & outflags) {
+			if (MMAN_sp & outflags) {
+				if (MMAN_PD & outflags) {
+					printf("\n.PD");
+					outflags &= ~MMAN_PD;
+				}
+			} else if ( ! (MMAN_PD & outflags)) {
+				printf("\n.PD 0");
+				outflags |= MMAN_PD;
+			}
+			printf("\n.PP\n");
+		} else if (MMAN_sp & outflags)
+			printf("\n.sp\n");
+		else if (MMAN_br & outflags)
+			printf("\n.br\n");
+		else if (MMAN_nl & outflags)
+			putchar('\n');
+		outflags &= ~(MMAN_PP|MMAN_sp|MMAN_br|MMAN_nl|MMAN_spc);
+		if (1 == TPremain)
+			printf(".br\n");
+		TPremain = 0;
+	} else if (MMAN_spc & outflags) {
 		/*
-		 * If we need a space, only print it before
-		 * (1) a nonzero length word;
-		 * (2) a word that is non-punctuation; and
-		 * (3) if punctuation, non-terminating puncutation.
+		 * If we need a space, only print it if
+		 * (1) it is forced by `No' or
+		 * (2) what follows is not terminating punctuation or
+		 * (3) what follows is longer than one character.
 		 */
-		if (NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1])
+		if (MMAN_spc_force & outflags || '\0' == s[0] ||
+		    NULL == strchr(".,:;)]?!", s[0]) || '\0' != s[1]) {
+			if (MMAN_Bk & outflags &&
+			    ! (MMAN_Bk_susp & outflags))
+				putchar('\\');
 			putchar(' ');
+			if (TPremain)
+				TPremain--;
+		}
+	}
 
 	/*
 	 * Reassign needing space if we're not following opening
 	 * punctuation.
 	 */
-	mm->need_space = 
-		('(' != s[0] && '[' != s[0]) || '\0' != s[1];
+	if (MMAN_Sm & outflags && ('\0' == s[0] ||
+	    (('(' != s[0] && '[' != s[0]) || '\0' != s[1])))
+		outflags |= MMAN_spc;
+	else
+		outflags &= ~MMAN_spc;
+	outflags &= ~(MMAN_spc_force | MMAN_Bk_susp);
 
 	for ( ; *s; s++) {
 		switch (*s) {
 		case (ASCII_NBRSP):
-			printf("\\~");
+			printf("\\ ");
 			break;
 		case (ASCII_HYPH):
 			putchar('-');
 			break;
+		case (' '):
+			if (MMAN_nbrword & outflags) {
+				printf("\\ ");
+				break;
+			}
+			/* FALLTHROUGH */
 		default:
 			putchar((unsigned char)*s);
 			break;
 		}
+		if (TPremain)
+			TPremain--;
 	}
+	outflags &= ~MMAN_nbrword;
+}
+
+static void
+print_line(const char *s, int newflags)
+{
+
+	outflags &= ~MMAN_br;
+	outflags |= MMAN_nl;
+	print_word(s);
+	outflags |= newflags;
+}
+
+static void
+print_block(const char *s, int newflags)
+{
+
+	outflags &= ~MMAN_PP;
+	if (MMAN_sp & outflags) {
+		outflags &= ~(MMAN_sp | MMAN_br);
+		if (MMAN_PD & outflags) {
+			print_line(".PD", 0);
+			outflags &= ~MMAN_PD;
+		}
+	} else if (! (MMAN_PD & outflags))
+		print_line(".PD 0", MMAN_PD);
+	outflags |= MMAN_nl;
+	print_word(s);
+	outflags |= MMAN_Bk_susp | newflags;
+}
+
+static void
+print_offs(const char *v)
+{
+	char		  buf[24];
+	struct roffsu	  su;
+	size_t		  sz;
+
+	print_line(".RS", MMAN_Bk_susp);
+
+	/* Convert v into a number (of characters). */
+	if (NULL == v || '\0' == *v || 0 == strcmp(v, "left"))
+		sz = 0;
+	else if (0 == strcmp(v, "indent"))
+		sz = 6;
+	else if (0 == strcmp(v, "indent-two"))
+		sz = 12;
+	else if (a2roffsu(v, &su, SCALE_MAX)) {
+		if (SCALE_EN == su.unit)
+			sz = su.scale;
+		else {
+			/*
+			 * XXX
+			 * If we are inside an enclosing list,
+			 * there is no easy way to add the two
+			 * indentations because they are provided
+			 * in terms of different units.
+			 */
+			print_word(v);
+			outflags |= MMAN_nl;
+			return;
+		}
+	} else
+		sz = strlen(v);
+
+	/*
+	 * We are inside an enclosing list.
+	 * Add the two indentations.
+	 */
+	if (Bl_stack_len)
+		sz += Bl_stack[Bl_stack_len - 1];
+
+	snprintf(buf, sizeof(buf), "%zun", sz);
+	print_word(buf);
+	outflags |= MMAN_nl;
+}
+
+/*
+ * Set up the indentation for a list item; used from pre_it().
+ */
+void
+print_width(const char *v, const struct mdoc_node *child, size_t defsz)
+{
+	char		  buf[24];
+	struct roffsu	  su;
+	size_t		  sz, chsz;
+	int		  numeric, remain;
+
+	numeric = 1;
+	remain = 0;
+
+	/* Convert v into a number (of characters). */
+	if (NULL == v)
+		sz = defsz;
+	else if (a2roffsu(v, &su, SCALE_MAX)) {
+		if (SCALE_EN == su.unit)
+			sz = su.scale;
+		else {
+			sz = 0;
+			numeric = 0;
+		}
+	} else
+		sz = strlen(v);
+
+	/* XXX Rough estimation, might have multiple parts. */
+	chsz = (NULL != child && MDOC_TEXT == child->type) ?
+			strlen(child->string) : 0;
+
+	/* Maybe we are inside an enclosing list? */
+	mid_it();
+
+	/*
+	 * Save our own indentation,
+	 * such that child lists can use it.
+	 */
+	Bl_stack[Bl_stack_len++] = sz + 2;
+
+	/* Set up the current list. */
+	if (defsz && chsz > sz)
+		print_block(".HP", 0);
+	else {
+		print_block(".TP", 0);
+		remain = sz + 2;
+	}
+	if (numeric) {
+		snprintf(buf, sizeof(buf), "%zun", sz + 2);
+		print_word(buf);
+	} else
+		print_word(v);
+	TPremain = remain;
+}
+
+void
+print_count(int *count)
+{
+	char		  buf[12];
+
+	snprintf(buf, sizeof(buf), "%d.", ++*count);
+	print_word(buf);
 }
 
 void
@@ -261,37 +535,42 @@ man_man(void *arg, const struct man *man)
 void
 man_mdoc(void *arg, const struct mdoc *mdoc)
 {
-	const struct mdoc_meta *m;
+	const struct mdoc_meta *meta;
 	const struct mdoc_node *n;
-	struct mman	        mm;
 
-	m = mdoc_meta(mdoc);
+	meta = mdoc_meta(mdoc);
 	n = mdoc_node(mdoc);
 
-	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"",
-			m->title, m->msec, m->date, m->os, m->vol);
+	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
+			meta->title, meta->msec, meta->date,
+			meta->os, meta->vol);
 
-	memset(&mm, 0, sizeof(struct mman));
+	/* Disable hyphenation and if nroff, disable justification. */
+	printf(".nh\n.if n .ad l");
 
-	mm.need_nl = 1;
-	print_node(m, n, &mm);
+	outflags = MMAN_nl | MMAN_Sm;
+	if (0 == fontqueue.size) {
+		fontqueue.size = 8;
+		fontqueue.head = fontqueue.tail = mandoc_malloc(8);
+		*fontqueue.tail = 'R';
+	}
+	print_node(meta, n);
 	putchar('\n');
 }
 
 static void
 print_node(DECL_ARGS)
 {
-	const struct mdoc_node	*prev, *sub;
+	const struct mdoc_node	*sub;
 	const struct manact	*act;
 	int			 cond, do_sub;
-	
+
 	/*
 	 * Break the line if we were parsed subsequent the current node.
 	 * This makes the page structure be more consistent.
 	 */
-	prev = n->prev ? n->prev : n->parent;
-	if (prev && prev->line < n->line)
-		mm->need_nl = 1;
+	if (MMAN_spc & outflags && MDOC_LINE & n->flags)
+		outflags |= MMAN_nl;
 
 	act = NULL;
 	cond = 0;
@@ -302,21 +581,22 @@ print_node(DECL_ARGS)
 		 * Make sure that we don't happen to start with a
 		 * control character at the start of a line.
 		 */
-		if (mm->need_nl && ('.' == *n->string || 
+		if (MMAN_nl & outflags && ('.' == *n->string || 
 					'\'' == *n->string)) {
-			print_word(mm, "\\&");
-			mm->need_space = 0;
+			print_word("");
+			printf("\\&");
+			outflags &= ~MMAN_spc;
 		}
-		print_word(mm, n->string);
+		print_word(n->string);
 	} else {
 		/*
 		 * Conditionally run the pre-node action handler for a
 		 * node.
 		 */
 		act = manacts + n->tok;
-		cond = NULL == act->cond || (*act->cond)(m, n, mm);
+		cond = NULL == act->cond || (*act->cond)(meta, n);
 		if (cond && act->pre)
-			do_sub = (*act->pre)(m, n, mm);
+			do_sub = (*act->pre)(meta, n);
 	}
 
 	/* 
@@ -326,13 +606,13 @@ print_node(DECL_ARGS)
 	 */
 	if (do_sub)
 		for (sub = n->child; sub; sub = sub->next)
-			print_node(m, sub, mm);
+			print_node(meta, sub);
 
 	/*
 	 * Lastly, conditionally run the post-node handler.
 	 */
 	if (cond && act->post)
-		(*act->post)(m, n, mm);
+		(*act->post)(meta, n);
 }
 
 static int
@@ -349,10 +629,6 @@ cond_body(DECL_ARGS)
 	return(MDOC_BODY == n->type);
 }
 
-/*
- * Output a font encoding before a node, e.g., \fR.
- * This obviously has no trailing space.
- */
 static int
 pre_enc(DECL_ARGS)
 {
@@ -361,14 +637,11 @@ pre_enc(DECL_ARGS)
 	prefix = manacts[n->tok].prefix;
 	if (NULL == prefix)
 		return(1);
-	print_word(mm, prefix);
-	mm->need_space = 0;
+	print_word(prefix);
+	outflags &= ~MMAN_spc;
 	return(1);
 }
 
-/*
- * Output a font encoding subsequent a node, e.g., \fP.
- */
 static void
 post_enc(DECL_ARGS)
 {
@@ -377,28 +650,62 @@ post_enc(DECL_ARGS)
 	suffix = manacts[n->tok].suffix;
 	if (NULL == suffix)
 		return;
-	mm->need_space = 0;
-	print_word(mm, suffix);
+	outflags &= ~MMAN_spc;
+	print_word(suffix);
+}
+
+static void
+post_font(DECL_ARGS)
+{
+
+	font_pop();
 }
 
-/*
- * Used in listings (percent = %A, e.g.).
- * FIXME: this is incomplete. 
- * It doesn't print a nice ", and" for lists.
- */
 static void
 post_percent(DECL_ARGS)
 {
 
-	post_enc(m, n, mm);
-	if (n->next)
-		print_word(mm, ",");
-	else {
-		print_word(mm, ".");
-		mm->need_nl = 1;
+	if (pre_em == manacts[n->tok].pre)
+		font_pop();
+	if (n->next) {
+		print_word(",");
+		if (n->prev &&	n->prev->tok == n->tok &&
+				n->next->tok == n->tok)
+			print_word("and");
+	} else {
+		print_word(".");
+		outflags |= MMAN_nl;
 	}
 }
 
+static int
+pre__t(DECL_ARGS)
+{
+
+        if (n->parent && MDOC_Rs == n->parent->tok &&
+                        n->parent->norm->Rs.quote_T) {
+		print_word("");
+		putchar('\"');
+		outflags &= ~MMAN_spc;
+	} else
+		font_push('I');
+	return(1);
+}
+
+static void
+post__t(DECL_ARGS)
+{
+
+        if (n->parent && MDOC_Rs == n->parent->tok &&
+                        n->parent->norm->Rs.quote_T) {
+		outflags &= ~MMAN_spc;
+		print_word("");
+		putchar('\"');
+	} else
+		font_pop();
+	post_percent(meta, n);
+}
+
 /*
  * Print before a section header.
  */
@@ -406,12 +713,13 @@ static int
 pre_sect(DECL_ARGS)
 {
 
-	if (MDOC_HEAD != n->type)
-		return(1);
-	mm->need_nl = 1;
-	print_word(mm, manacts[n->tok].prefix);
-	print_word(mm, "\"");
-	mm->need_space = 0;
+	if (MDOC_HEAD == n->type) {
+		outflags |= MMAN_sp;
+		print_block(manacts[n->tok].prefix, 0);
+		print_word("");
+		putchar('\"');
+		outflags &= ~MMAN_spc;
+	}
 	return(1);
 }
 
@@ -424,18 +732,84 @@ post_sect(DECL_ARGS)
 
 	if (MDOC_HEAD != n->type)
 		return;
-	mm->need_space = 0;
-	print_word(mm, "\"");
-	mm->need_nl = 1;
+	outflags &= ~MMAN_spc;
+	print_word("");
+	putchar('\"');
+	outflags |= MMAN_nl;
+	if (MDOC_Sh == n->tok && SEC_AUTHORS == n->sec)
+		outflags &= ~(MMAN_An_split | MMAN_An_nosplit);
+}
+
+/* See mdoc_term.c, synopsis_pre() for comments. */
+static void
+pre_syn(const struct mdoc_node *n)
+{
+
+	if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
+		return;
+
+	if (n->prev->tok == n->tok &&
+			MDOC_Ft != n->tok &&
+			MDOC_Fo != n->tok &&
+			MDOC_Fn != n->tok) {
+		outflags |= MMAN_br;
+		return;
+	}
+
+	switch (n->prev->tok) {
+	case (MDOC_Fd):
+		/* FALLTHROUGH */
+	case (MDOC_Fn):
+		/* FALLTHROUGH */
+	case (MDOC_Fo):
+		/* FALLTHROUGH */
+	case (MDOC_In):
+		/* FALLTHROUGH */
+	case (MDOC_Vt):
+		outflags |= MMAN_sp;
+		break;
+	case (MDOC_Ft):
+		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
+			outflags |= MMAN_sp;
+			break;
+		}
+		/* FALLTHROUGH */
+	default:
+		outflags |= MMAN_br;
+		break;
+	}
+}
+
+static int
+pre_an(DECL_ARGS)
+{
+
+	switch (n->norm->An.auth) {
+	case (AUTH_split):
+		outflags &= ~MMAN_An_nosplit;
+		outflags |= MMAN_An_split;
+		return(0);
+	case (AUTH_nosplit):
+		outflags &= ~MMAN_An_split;
+		outflags |= MMAN_An_nosplit;
+		return(0);
+	default:
+		if (MMAN_An_split & outflags)
+			outflags |= MMAN_br;
+		else if (SEC_AUTHORS == n->sec &&
+		    ! (MMAN_An_nosplit & outflags))
+			outflags |= MMAN_An_split;
+		return(1);
+	}
 }
 
 static int
 pre_ap(DECL_ARGS)
 {
 
-	mm->need_space = 0;
-	print_word(mm, "'");
-	mm->need_space = 0;
+	outflags &= ~MMAN_spc;
+	print_word("'");
+	outflags &= ~MMAN_spc;
 	return(0);
 }
 
@@ -443,12 +817,14 @@ static int
 pre_bd(DECL_ARGS)
 {
 
+	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
+
 	if (DISP_unfilled == n->norm->Bd.type ||
-	    DISP_literal  == n->norm->Bd.type) {
-		mm->need_nl = 1;
-		print_word(mm, ".nf");
-	}
-	mm->need_nl = 1;
+	    DISP_literal  == n->norm->Bd.type)
+		print_line(".nf", 0);
+	if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
+		outflags |= MMAN_sp;
+	print_offs(n->norm->Bd.offs);
 	return(1);
 }
 
@@ -456,21 +832,143 @@ static void
 post_bd(DECL_ARGS)
 {
 
+	/* Close out this display. */
+	print_line(".RE", MMAN_nl);
 	if (DISP_unfilled == n->norm->Bd.type ||
-	    DISP_literal  == n->norm->Bd.type) {
-		mm->need_nl = 1;
-		print_word(mm, ".fi");
+	    DISP_literal  == n->norm->Bd.type)
+		print_line(".fi", MMAN_nl);
+
+	/* Maybe we are inside an enclosing list? */
+	if (NULL != n->parent->next)
+		mid_it();
+}
+
+static int
+pre_bf(DECL_ARGS)
+{
+
+	switch (n->type) {
+	case (MDOC_BLOCK):
+		return(1);
+	case (MDOC_BODY):
+		break;
+	default:
+		return(0);
 	}
-	mm->need_nl = 1;
+	switch (n->norm->Bf.font) {
+	case (FONT_Em):
+		font_push('I');
+		break;
+	case (FONT_Sy):
+		font_push('B');
+		break;
+	default:
+		font_push('R');
+		break;
+	}
+	return(1);
+}
+
+static void
+post_bf(DECL_ARGS)
+{
+
+	if (MDOC_BODY == n->type)
+		font_pop();
+}
+
+static int
+pre_bk(DECL_ARGS)
+{
+
+	switch (n->type) {
+	case (MDOC_BLOCK):
+		return(1);
+	case (MDOC_BODY):
+		outflags |= MMAN_Bk;
+		return(1);
+	default:
+		return(0);
+	}
+}
+
+static void
+post_bk(DECL_ARGS)
+{
+
+	if (MDOC_BODY == n->type)
+		outflags &= ~MMAN_Bk;
+}
+
+static int
+pre_bl(DECL_ARGS)
+{
+	size_t		 icol;
+
+	/*
+	 * print_offs() will increase the -offset to account for
+	 * a possible enclosing .It, but any enclosed .It blocks
+	 * just nest and do not add up their indentation.
+	 */
+	if (n->norm->Bl.offs) {
+		print_offs(n->norm->Bl.offs);
+		Bl_stack[Bl_stack_len++] = 0;
+	}
+
+	switch (n->norm->Bl.type) {
+	case (LIST_enum):
+		n->norm->Bl.count = 0;
+		return(1);
+	case (LIST_column):
+		break;
+	default:
+		return(1);
+	}
+
+	print_line(".TS", MMAN_nl);
+	for (icol = 0; icol < n->norm->Bl.ncols; icol++)
+		print_word("l");
+	print_word(".");
+	outflags |= MMAN_nl;
+	return(1);
+}
+
+static void
+post_bl(DECL_ARGS)
+{
+
+	switch (n->norm->Bl.type) {
+	case (LIST_column):
+		print_line(".TE", 0);
+		break;
+	case (LIST_enum):
+		n->norm->Bl.count = 0;
+		break;
+	default:
+		break;
+	}
+
+	if (n->norm->Bl.offs) {
+		print_line(".RE", MMAN_nl);
+		assert(Bl_stack_len);
+		Bl_stack_len--;
+		assert(0 == Bl_stack[Bl_stack_len]);
+	} else {
+		outflags |= MMAN_PP | MMAN_nl;
+		outflags &= ~(MMAN_sp | MMAN_br);
+	}
+
+	/* Maybe we are inside an enclosing list? */
+	if (NULL != n->parent->next)
+		mid_it();
+
 }
 
 static int
 pre_br(DECL_ARGS)
 {
 
-	mm->need_nl = 1;
-	print_word(mm, ".br");
-	mm->need_nl = 1;
+	outflags |= MMAN_br;
 	return(0);
 }
 
@@ -480,17 +978,17 @@ pre_bx(DECL_ARGS)
 
 	n = n->child;
 	if (n) {
-		print_word(mm, n->string);
-		mm->need_space = 0;
+		print_word(n->string);
+		outflags &= ~MMAN_spc;
 		n = n->next;
 	}
-	print_word(mm, "BSD");
+	print_word("BSD");
 	if (NULL == n)
 		return(0);
-	mm->need_space = 0;
-	print_word(mm, "-");
-	mm->need_space = 0;
-	print_word(mm, n->string);
+	outflags &= ~MMAN_spc;
+	print_word("-");
+	outflags &= ~MMAN_spc;
+	print_word(n->string);
 	return(0);
 }
 
@@ -498,9 +996,7 @@ static int
 pre_dl(DECL_ARGS)
 {
 
-	mm->need_nl = 1;
-	print_word(mm, ".RS 6n");
-	mm->need_nl = 1;
+	print_offs("6n");
 	return(1);
 }
 
@@ -508,9 +1004,213 @@ static void
 post_dl(DECL_ARGS)
 {
 
-	mm->need_nl = 1;
-	print_word(mm, ".RE");
-	mm->need_nl = 1;
+	print_line(".RE", MMAN_nl);
+
+	/* Maybe we are inside an enclosing list? */
+	if (NULL != n->parent->next)
+		mid_it();
+}
+
+static int
+pre_em(DECL_ARGS)
+{
+
+	font_push('I');
+	return(1);
+}
+
+static void
+post_eo(DECL_ARGS)
+{
+
+	if (MDOC_HEAD == n->type || MDOC_BODY == n->type)
+		outflags &= ~MMAN_spc;
+}
+
+static int
+pre_fa(DECL_ARGS)
+{
+	int	 am_Fa;
+
+	am_Fa = MDOC_Fa == n->tok;
+
+	if (am_Fa)
+		n = n->child;
+
+	while (NULL != n) {
+		font_push('I');
+		if (am_Fa || MDOC_SYNPRETTY & n->flags)
+			outflags |= MMAN_nbrword;
+		print_node(meta, n);
+		font_pop();
+		if (NULL != (n = n->next))
+			print_word(",");
+	}
+	return(0);
+}
+
+static void
+post_fa(DECL_ARGS)
+{
+
+	if (NULL != n->next && MDOC_Fa == n->next->tok)
+		print_word(",");
+}
+
+static int
+pre_fd(DECL_ARGS)
+{
+
+	pre_syn(n);
+	font_push('B');
+	return(1);
+}
+
+static void
+post_fd(DECL_ARGS)
+{
+
+	font_pop();
+	outflags |= MMAN_br;
+}
+
+static int
+pre_fl(DECL_ARGS)
+{
+
+	font_push('B');
+	print_word("\\-");
+	outflags &= ~MMAN_spc;
+	return(1);
+}
+
+static void
+post_fl(DECL_ARGS)
+{
+
+	font_pop();
+	if (0 == n->nchild && NULL != n->next &&
+			n->next->line == n->line)
+		outflags &= ~MMAN_spc;
+}
+
+static int
+pre_fn(DECL_ARGS)
+{
+
+	pre_syn(n);
+
+	n = n->child;
+	if (NULL == n)
+		return(0);
+
+	if (MDOC_SYNPRETTY & n->flags)
+		print_block(".HP 4n", MMAN_nl);
+
+	font_push('B');
+	print_node(meta, n);
+	font_pop();
+	outflags &= ~MMAN_spc;
+	print_word("(");
+	outflags &= ~MMAN_spc;
+
+	n = n->next;
+	if (NULL != n)
+		pre_fa(meta, n);
+	return(0);
+}
+
+static void
+post_fn(DECL_ARGS)
+{
+
+	print_word(")");
+	if (MDOC_SYNPRETTY & n->flags) {
+		print_word(";");
+		outflags |= MMAN_PP;
+	}
+}
+
+static int
+pre_fo(DECL_ARGS)
+{
+
+	switch (n->type) {
+	case (MDOC_BLOCK):
+		pre_syn(n);
+		break;
+	case (MDOC_HEAD):
+		if (MDOC_SYNPRETTY & n->flags)
+			print_block(".HP 4n", MMAN_nl);
+		font_push('B');
+		break;
+	case (MDOC_BODY):
+		outflags &= ~MMAN_spc;
+		print_word("(");
+		outflags &= ~MMAN_spc;
+		break;
+	default:
+		break;
+	}
+	return(1);
+}
+
+static void
+post_fo(DECL_ARGS)
+{
+
+	switch (n->type) {
+	case (MDOC_HEAD):
+		font_pop();
+		break;
+	case (MDOC_BODY):
+		post_fn(meta, n);
+		break;
+	default:
+		break;
+	}
+}
+
+static int
+pre_ft(DECL_ARGS)
+{
+
+	pre_syn(n);
+	font_push('I');
+	return(1);
+}
+
+static int
+pre_in(DECL_ARGS)
+{
+
+	if (MDOC_SYNPRETTY & n->flags) {
+		pre_syn(n);
+		font_push('B');
+		print_word("#include <");
+		outflags &= ~MMAN_spc;
+	} else {
+		print_word("<");
+		outflags &= ~MMAN_spc;
+		font_push('I');
+	}
+	return(1);
+}
+
+static void
+post_in(DECL_ARGS)
+{
+
+	if (MDOC_SYNPRETTY & n->flags) {
+		outflags &= ~MMAN_spc;
+		print_word(">");
+		font_pop();
+		outflags |= MMAN_br;
+	} else {
+		font_pop();
+		outflags &= ~MMAN_spc;
+		print_word(">");
+	}
 }
 
 static int
@@ -518,36 +1218,221 @@ pre_it(DECL_ARGS)
 {
 	const struct mdoc_node *bln;
 
-	if (MDOC_HEAD == n->type) {
-		mm->need_nl = 1;
-		print_word(mm, ".TP");
-		bln = n->parent->parent->prev;
+	switch (n->type) {
+	case (MDOC_HEAD):
+		outflags |= MMAN_PP | MMAN_nl;
+		bln = n->parent->parent;
+		if (0 == bln->norm->Bl.comp ||
+		    (NULL == n->parent->prev &&
+		     NULL == bln->parent->prev))
+			outflags |= MMAN_sp;
+		outflags &= ~MMAN_br;
 		switch (bln->norm->Bl.type) {
+		case (LIST_item):
+			return(0);
+		case (LIST_inset):
+			/* FALLTHROUGH */
+		case (LIST_diag):
+			/* FALLTHROUGH */
+		case (LIST_ohang):
+			if (bln->norm->Bl.type == LIST_diag)
+				print_line(".B \"", 0);
+			else
+				print_line(".R \"", 0);
+			outflags &= ~MMAN_spc;
+			return(1);
 		case (LIST_bullet):
-			print_word(mm, "4n");
-			mm->need_nl = 1;
-			print_word(mm, "\\fBo\\fP");
+			/* FALLTHROUGH */
+		case (LIST_dash):
+			/* FALLTHROUGH */
+		case (LIST_hyphen):
+			print_width(bln->norm->Bl.width, NULL, 0);
+			TPremain = 0;
+			outflags |= MMAN_nl;
+			font_push('B');
+			if (LIST_bullet == bln->norm->Bl.type)
+				print_word("o");
+			else
+				print_word("-");
+			font_pop();
+			break;
+		case (LIST_enum):
+			print_width(bln->norm->Bl.width, NULL, 0);
+			TPremain = 0;
+			outflags |= MMAN_nl;
+			print_count(&bln->norm->Bl.count);
+			break;
+		case (LIST_hang):
+			print_width(bln->norm->Bl.width, n->child, 6);
+			TPremain = 0;
+			break;
+		case (LIST_tag):
+			print_width(bln->norm->Bl.width, n->child, 0);
+			putchar('\n');
+			outflags &= ~MMAN_spc;
+			return(1);
+		default:
+			return(1);
+		}
+		outflags |= MMAN_nl;
+	default:
+		break;
+	}
+	return(1);
+}
+
+/*
+ * This function is called after closing out an indented block.
+ * If we are inside an enclosing list, restore its indentation.
+ */
+static void
+mid_it(void)
+{
+	char		 buf[24];
+
+	/* Nothing to do outside a list. */
+	if (0 == Bl_stack_len || 0 == Bl_stack[Bl_stack_len - 1])
+		return;
+
+	/* The indentation has already been set up. */
+	if (Bl_stack_post[Bl_stack_len - 1])
+		return;
+
+	/* Restore the indentation of the enclosing list. */
+	print_line(".RS", MMAN_Bk_susp);
+	snprintf(buf, sizeof(buf), "%zun", Bl_stack[Bl_stack_len - 1]);
+	print_word(buf);
+
+	/* Remeber to close out this .RS block later. */
+	Bl_stack_post[Bl_stack_len - 1] = 1;
+}
+
+static void
+post_it(DECL_ARGS)
+{
+	const struct mdoc_node *bln;
+
+	bln = n->parent->parent;
+
+	switch (n->type) {
+	case (MDOC_HEAD):
+		switch (bln->norm->Bl.type) {
+		case (LIST_diag):
+			outflags &= ~MMAN_spc;
+			print_word("\\ ");
+			break;
+		case (LIST_ohang):
+			outflags |= MMAN_br;
 			break;
 		default:
-			if (bln->norm->Bl.width)
-				print_word(mm, bln->norm->Bl.width);
 			break;
 		}
-		mm->need_nl = 1;
+		break;
+	case (MDOC_BODY):
+		switch (bln->norm->Bl.type) {
+		case (LIST_bullet):
+			/* FALLTHROUGH */
+		case (LIST_dash):
+			/* FALLTHROUGH */
+		case (LIST_hyphen):
+			/* FALLTHROUGH */
+		case (LIST_enum):
+			/* FALLTHROUGH */
+		case (LIST_hang):
+			/* FALLTHROUGH */
+		case (LIST_tag):
+			assert(Bl_stack_len);
+			Bl_stack[--Bl_stack_len] = 0;
+
+			/*
+			 * Our indentation had to be restored
+			 * after a child display or child list.
+			 * Close out that indentation block now.
+			 */
+			if (Bl_stack_post[Bl_stack_len]) {
+				print_line(".RE", MMAN_nl);
+				Bl_stack_post[Bl_stack_len] = 0;
+			}
+			break;
+		case (LIST_column):
+			if (NULL != n->next) {
+				putchar('\t');
+				outflags &= ~MMAN_spc;
+			}
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
 	}
+}
+
+static void
+post_lb(DECL_ARGS)
+{
+
+	if (SEC_LIBRARY == n->sec)
+		outflags |= MMAN_br;
+}
+
+static int
+pre_lk(DECL_ARGS)
+{
+	const struct mdoc_node *link, *descr;
+
+	if (NULL == (link = n->child))
+		return(0);
+
+	if (NULL != (descr = link->next)) {
+		font_push('I');
+		while (NULL != descr) {
+			print_word(descr->string);
+			descr = descr->next;
+		}
+		print_word(":");
+		font_pop();
+	}
+
+	font_push('B');
+	print_word(link->string);
+	font_pop();
+	return(0);
+}
+
+static int
+pre_li(DECL_ARGS)
+{
+
+	font_push('R');
 	return(1);
 }
 
 static int
 pre_nm(DECL_ARGS)
 {
+	char	*name;
 
+	if (MDOC_BLOCK == n->type) {
+		outflags |= MMAN_Bk;
+		pre_syn(n);
+	}
 	if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
 		return(1);
-	print_word(mm, "\\fB");
-	mm->need_space = 0;
+	name = n->child ? n->child->string : meta->name;
+	if (NULL == name)
+		return(0);
+	if (MDOC_HEAD == n->type) {
+		if (NULL == n->parent->prev)
+			outflags |= MMAN_sp;
+		print_block(".HP", 0);
+		printf(" %zun", strlen(name) + 1);
+		outflags |= MMAN_nl;
+	}
+	font_push('B');
 	if (NULL == n->child)
-		print_word(mm, m->name);
+		print_word(meta->name);
 	return(1);
 }
 
@@ -555,17 +1440,33 @@ static void
 post_nm(DECL_ARGS)
 {
 
-	if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
-		return;
-	mm->need_space = 0;
-	print_word(mm, "\\fP");
+	switch (n->type) {
+	case (MDOC_BLOCK):
+		outflags &= ~MMAN_Bk;
+		break;
+	case (MDOC_HEAD):
+		/* FALLTHROUGH */
+	case (MDOC_ELEM):
+		font_pop();
+		break;
+	default:
+		break;
+	}
+}
+
+static int
+pre_no(DECL_ARGS)
+{
+
+	outflags |= MMAN_spc_force;
+	return(1);
 }
 
 static int
 pre_ns(DECL_ARGS)
 {
 
-	mm->need_space = 0;
+	outflags &= ~MMAN_spc;
 	return(0);
 }
 
@@ -573,28 +1474,52 @@ static void
 post_pf(DECL_ARGS)
 {
 
-	mm->need_space = 0;
+	outflags &= ~MMAN_spc;
 }
 
 static int
 pre_pp(DECL_ARGS)
 {
 
-	mm->need_nl = 1;
-	if (MDOC_It == n->parent->tok)
-		print_word(mm, ".sp");
-	else
-		print_word(mm, ".PP");
-	mm->need_nl = 1;
+	if (MDOC_It != n->parent->tok)
+		outflags |= MMAN_PP;
+	outflags |= MMAN_sp | MMAN_nl;
+	outflags &= ~MMAN_br;
+	return(0);
+}
+
+static int
+pre_rs(DECL_ARGS)
+{
+
+	if (SEC_SEE_ALSO == n->sec) {
+		outflags |= MMAN_PP | MMAN_sp | MMAN_nl;
+		outflags &= ~MMAN_br;
+	}
 	return(1);
 }
 
+static int
+pre_sm(DECL_ARGS)
+{
+
+	assert(n->child && MDOC_TEXT == n->child->type);
+	if (0 == strcmp("on", n->child->string))
+		outflags |= MMAN_Sm | MMAN_spc;
+	else
+		outflags &= ~MMAN_Sm;
+	return(0);
+}
+
 static int
 pre_sp(DECL_ARGS)
 {
 
-	mm->need_nl = 1;
-	print_word(mm, ".sp");
+	if (MMAN_PP & outflags) {
+		outflags &= ~MMAN_PP;
+		print_line(".PP", 0);
+	} else
+		print_line(".sp", 0);
 	return(1);
 }
 
@@ -602,7 +1527,43 @@ static void
 post_sp(DECL_ARGS)
 {
 
-	mm->need_nl = 1;
+	outflags |= MMAN_nl;
+}
+
+static int
+pre_sy(DECL_ARGS)
+{
+
+	font_push('B');
+	return(1);
+}
+
+static int
+pre_vt(DECL_ARGS)
+{
+
+	if (MDOC_SYNPRETTY & n->flags) {
+		switch (n->type) {
+		case (MDOC_BLOCK):
+			pre_syn(n);
+			return(1);
+		case (MDOC_BODY):
+			break;
+		default:
+			return(0);
+		}
+	}
+	font_push('I');
+	return(1);
+}
+
+static void
+post_vt(DECL_ARGS)
+{
+
+	if (MDOC_SYNPRETTY & n->flags && MDOC_BODY != n->type)
+		return;
+	font_pop();
 }
 
 static int
@@ -612,14 +1573,14 @@ pre_xr(DECL_ARGS)
 	n = n->child;
 	if (NULL == n)
 		return(0);
-	print_node(m, n, mm);
+	print_node(meta, n);
 	n = n->next;
 	if (NULL == n)
 		return(0);
-	mm->need_space = 0;
-	print_word(mm, "(");
-	print_node(m, n, mm);
-	print_word(mm, ")");
+	outflags &= ~MMAN_spc;
+	print_word("(");
+	print_node(meta, n);
+	print_word(")");
 	return(0);
 }
 
@@ -627,11 +1588,11 @@ static int
 pre_ux(DECL_ARGS)
 {
 
-	print_word(mm, manacts[n->tok].prefix);
+	print_word(manacts[n->tok].prefix);
 	if (NULL == n->child)
 		return(0);
-	mm->need_space = 0;
-	print_word(mm, "\\~");
-	mm->need_space = 0;
+	outflags &= ~MMAN_spc;
+	print_word("\\ ");
+	outflags &= ~MMAN_spc;
 	return(1);
 }
diff --git a/contrib/mdocml/mdoc_term.c b/contrib/mdocml/mdoc_term.c
index 53335664444..268fcae0066 100644
--- a/contrib/mdocml/mdoc_term.c
+++ b/contrib/mdocml/mdoc_term.c
@@ -1,7 +1,8 @@
-/*	$Id: mdoc_term.c,v 1.238 2011/11/13 13:15:14 schwarze Exp $ */
+/*	$Id: mdoc_term.c,v 1.258 2013/12/25 21:24:12 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010 Ingo Schwarze 
+ * Copyright (c) 2010, 2012, 2013 Ingo Schwarze 
+ * Copyright (c) 2013 Franco Fichtner 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -41,8 +42,8 @@ struct	termpair {
 
 #define	DECL_ARGS struct termp *p, \
 		  struct termpair *pair, \
-	  	  const struct mdoc_meta *m, \
-		  const struct mdoc_node *n
+	  	  const struct mdoc_meta *meta, \
+		  struct mdoc_node *n
 
 struct	termact {
 	int	(*pre)(DECL_ARGS);
@@ -69,7 +70,7 @@ static	void	  termp_an_post(DECL_ARGS);
 static	void	  termp_bd_post(DECL_ARGS);
 static	void	  termp_bk_post(DECL_ARGS);
 static	void	  termp_bl_post(DECL_ARGS);
-static	void	  termp_d1_post(DECL_ARGS);
+static	void	  termp_fd_post(DECL_ARGS);
 static	void	  termp_fo_post(DECL_ARGS);
 static	void	  termp_in_post(DECL_ARGS);
 static	void	  termp_it_post(DECL_ARGS);
@@ -100,7 +101,6 @@ static	int	  termp_fl_pre(DECL_ARGS);
 static	int	  termp_fn_pre(DECL_ARGS);
 static	int	  termp_fo_pre(DECL_ARGS);
 static	int	  termp_ft_pre(DECL_ARGS);
-static	int	  termp_igndelim_pre(DECL_ARGS);
 static	int	  termp_in_pre(DECL_ARGS);
 static	int	  termp_it_pre(DECL_ARGS);
 static	int	  termp_li_pre(DECL_ARGS);
@@ -129,8 +129,8 @@ static	const struct termact termacts[MDOC_MAX] = {
 	{ termp_sh_pre, termp_sh_post }, /* Sh */
 	{ termp_ss_pre, termp_ss_post }, /* Ss */ 
 	{ termp_sp_pre, NULL }, /* Pp */ 
-	{ termp_d1_pre, termp_d1_post }, /* D1 */
-	{ termp_d1_pre, termp_d1_post }, /* Dl */
+	{ termp_d1_pre, termp_bl_post }, /* D1 */
+	{ termp_d1_pre, termp_bl_post }, /* Dl */
 	{ termp_bd_pre, termp_bd_post }, /* Bd */
 	{ NULL, NULL }, /* Ed */
 	{ termp_bl_pre, termp_bl_post }, /* Bl */
@@ -146,7 +146,7 @@ static	const struct termact termacts[MDOC_MAX] = {
 	{ NULL, NULL }, /* Ev */ 
 	{ termp_ex_pre, NULL }, /* Ex */
 	{ termp_fa_pre, NULL }, /* Fa */ 
-	{ termp_fd_pre, NULL }, /* Fd */ 
+	{ termp_fd_pre, termp_fd_post }, /* Fd */ 
 	{ termp_fl_pre, NULL }, /* Fl */
 	{ termp_fn_pre, NULL }, /* Fn */ 
 	{ termp_ft_pre, NULL }, /* Ft */ 
@@ -194,12 +194,12 @@ static	const struct termact termacts[MDOC_MAX] = {
 	{ termp_quote_pre, termp_quote_post }, /* Eo */
 	{ termp_xx_pre, NULL }, /* Fx */
 	{ termp_bold_pre, NULL }, /* Ms */
-	{ termp_igndelim_pre, NULL }, /* No */
+	{ NULL, NULL }, /* No */
 	{ termp_ns_pre, NULL }, /* Ns */
 	{ termp_xx_pre, NULL }, /* Nx */
 	{ termp_xx_pre, NULL }, /* Ox */
 	{ NULL, NULL }, /* Pc */
-	{ termp_igndelim_pre, termp_pf_post }, /* Pf */
+	{ NULL, termp_pf_post }, /* Pf */
 	{ termp_quote_pre, termp_quote_post }, /* Po */
 	{ termp_quote_pre, termp_quote_post }, /* Pq */
 	{ NULL, NULL }, /* Qc */
@@ -242,7 +242,7 @@ static	const struct termact termacts[MDOC_MAX] = {
 	{ NULL, termp____post }, /* %Q */ 
 	{ termp_sp_pre, NULL }, /* br */
 	{ termp_sp_pre, NULL }, /* sp */ 
-	{ termp_under_pre, termp____post }, /* %U */ 
+	{ NULL, termp____post }, /* %U */ 
 	{ NULL, NULL }, /* Ta */ 
 };
 
@@ -251,7 +251,7 @@ void
 terminal_mdoc(void *arg, const struct mdoc *mdoc)
 {
 	const struct mdoc_node	*n;
-	const struct mdoc_meta	*m;
+	const struct mdoc_meta	*meta;
 	struct termp		*p;
 
 	p = (struct termp *)arg;
@@ -267,12 +267,12 @@ terminal_mdoc(void *arg, const struct mdoc *mdoc)
 		p->symtab = mchars_alloc();
 
 	n = mdoc_node(mdoc);
-	m = mdoc_meta(mdoc);
+	meta = mdoc_meta(mdoc);
 
-	term_begin(p, print_mdoc_head, print_mdoc_foot, m);
+	term_begin(p, print_mdoc_head, print_mdoc_foot, meta);
 
 	if (n->child)
-		print_mdoc_nodelist(p, NULL, m, n->child);
+		print_mdoc_nodelist(p, NULL, meta, n->child);
 
 	term_end(p);
 }
@@ -282,9 +282,9 @@ static void
 print_mdoc_nodelist(DECL_ARGS)
 {
 
-	print_mdoc_node(p, pair, m, n);
+	print_mdoc_node(p, pair, meta, n);
 	if (n->next)
-		print_mdoc_nodelist(p, pair, m, n->next);
+		print_mdoc_nodelist(p, pair, meta, n->next);
 }
 
 
@@ -293,14 +293,13 @@ static void
 print_mdoc_node(DECL_ARGS)
 {
 	int		 chld;
-	const void	*font;
 	struct termpair	 npair;
 	size_t		 offset, rmargin;
 
 	chld = 1;
 	offset = p->offset;
 	rmargin = p->rmargin;
-	font = term_fontq(p);
+	n->prev_font = term_fontq(p);
 
 	memset(&npair, 0, sizeof(struct termpair));
 	npair.ppair = pair;
@@ -308,33 +307,16 @@ print_mdoc_node(DECL_ARGS)
 	/*
 	 * Keeps only work until the end of a line.  If a keep was
 	 * invoked in a prior line, revert it to PREKEEP.
-	 *
-	 * Also let SYNPRETTY sections behave as if they were wrapped
-	 * in a `Bk' block.
 	 */
 
-	if (TERMP_KEEP & p->flags || MDOC_SYNPRETTY & n->flags) {
-		if (n->prev && n->prev->line != n->line) {
+	if (TERMP_KEEP & p->flags) {
+		if (n->prev ? (n->prev->lastline != n->line) :
+		    (n->parent && n->parent->line != n->line)) {
 			p->flags &= ~TERMP_KEEP;
 			p->flags |= TERMP_PREKEEP;
-		} else if (NULL == n->prev) {
-			if (n->parent && n->parent->line != n->line) {
-				p->flags &= ~TERMP_KEEP;
-				p->flags |= TERMP_PREKEEP;
-			}
 		}
 	}
 
-	/*
-	 * Since SYNPRETTY sections aren't "turned off" with `Ek',
-	 * we have to intuit whether we should disable formatting.
-	 */
-
-	if ( ! (MDOC_SYNPRETTY & n->flags) &&
-	    ((n->prev   && MDOC_SYNPRETTY & n->prev->flags) ||
-	     (n->parent && MDOC_SYNPRETTY & n->parent->flags)))
-		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
-
 	/*
 	 * After the keep flags have been set up, we may now
 	 * produce output.  Note that some pre-handlers do so.
@@ -359,14 +341,15 @@ print_mdoc_node(DECL_ARGS)
 	default:
 		if (termacts[n->tok].pre && ENDBODY_NOT == n->end)
 			chld = (*termacts[n->tok].pre)
-				(p, &npair, m, n);
+				(p, &npair, meta, n);
 		break;
 	}
 
 	if (chld && n->child)
-		print_mdoc_nodelist(p, &npair, m, n->child);
+		print_mdoc_nodelist(p, &npair, meta, n->child);
 
-	term_fontpopq(p, font);
+	term_fontpopq(p,
+	    (ENDBODY_NOT == n->end ? n : n->pending)->prev_font);
 
 	switch (n->type) {
 	case (MDOC_TEXT):
@@ -378,7 +361,7 @@ print_mdoc_node(DECL_ARGS)
 	default:
 		if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags)
 			break;
-		(void)(*termacts[n->tok].post)(p, &npair, m, n);
+		(void)(*termacts[n->tok].post)(p, &npair, meta, n);
 
 		/*
 		 * Explicit end tokens not only call the post
@@ -409,9 +392,9 @@ print_mdoc_node(DECL_ARGS)
 static void
 print_mdoc_foot(struct termp *p, const void *arg)
 {
-	const struct mdoc_meta *m;
+	const struct mdoc_meta *meta;
 
-	m = (const struct mdoc_meta *)arg;
+	meta = (const struct mdoc_meta *)arg;
 
 	term_fontrepl(p, TERMFONT_NONE);
 
@@ -427,25 +410,27 @@ print_mdoc_foot(struct termp *p, const void *arg)
 
 	p->offset = 0;
 	p->rmargin = (p->maxrmargin - 
-			term_strlen(p, m->date) + term_len(p, 1)) / 2;
+			term_strlen(p, meta->date) + term_len(p, 1)) / 2;
+	p->trailspace = 1;
 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
 
-	term_word(p, m->os);
+	term_word(p, meta->os);
 	term_flushln(p);
 
 	p->offset = p->rmargin;
-	p->rmargin = p->maxrmargin - term_strlen(p, m->os);
+	p->rmargin = p->maxrmargin - term_strlen(p, meta->os);
 	p->flags |= TERMP_NOSPACE;
 
-	term_word(p, m->date);
+	term_word(p, meta->date);
 	term_flushln(p);
 
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin;
+	p->trailspace = 0;
 	p->flags &= ~TERMP_NOBREAK;
 	p->flags |= TERMP_NOSPACE;
 
-	term_word(p, m->os);
+	term_word(p, meta->os);
 	term_flushln(p);
 
 	p->offset = 0;
@@ -459,9 +444,9 @@ print_mdoc_head(struct termp *p, const void *arg)
 {
 	char		buf[BUFSIZ], title[BUFSIZ];
 	size_t		buflen, titlen;
-	const struct mdoc_meta *m;
+	const struct mdoc_meta *meta;
 
-	m = (const struct mdoc_meta *)arg;
+	meta = (const struct mdoc_meta *)arg;
 
 	/*
 	 * The header is strange.  It has three components, which are
@@ -479,20 +464,21 @@ print_mdoc_head(struct termp *p, const void *arg)
 	p->offset = 0;
 	p->rmargin = p->maxrmargin;
 
-	assert(m->vol);
-	strlcpy(buf, m->vol, BUFSIZ);
+	assert(meta->vol);
+	strlcpy(buf, meta->vol, BUFSIZ);
 	buflen = term_strlen(p, buf);
 
-	if (m->arch) {
+	if (meta->arch) {
 		strlcat(buf, " (", BUFSIZ);
-		strlcat(buf, m->arch, BUFSIZ);
+		strlcat(buf, meta->arch, BUFSIZ);
 		strlcat(buf, ")", BUFSIZ);
 	}
 
-	snprintf(title, BUFSIZ, "%s(%s)", m->title, m->msec);
+	snprintf(title, BUFSIZ, "%s(%s)", meta->title, meta->msec);
 	titlen = term_strlen(p, title);
 
 	p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
+	p->trailspace = 1;
 	p->offset = 0;
 	p->rmargin = 2 * (titlen+1) + buflen < p->maxrmargin ?
 	    (p->maxrmargin -
@@ -511,6 +497,7 @@ print_mdoc_head(struct termp *p, const void *arg)
 	term_flushln(p);
 
 	p->flags &= ~TERMP_NOBREAK;
+	p->trailspace = 0;
 	if (p->rmargin + titlen <= p->maxrmargin) {
 		p->flags |= TERMP_NOSPACE;
 		p->offset = p->rmargin;
@@ -727,12 +714,10 @@ termp_it_pre(DECL_ARGS)
 	case (LIST_dash):
 		/* FALLTHROUGH */
 	case (LIST_hyphen):
-		if (width < term_len(p, 4))
-			width = term_len(p, 4);
-		break;
+		/* FALLTHROUGH */
 	case (LIST_enum):
-		if (width < term_len(p, 5))
-			width = term_len(p, 5);
+		if (width < term_len(p, 2))
+			width = term_len(p, 2);
 		break;
 	case (LIST_hang):
 		if (0 == width)
@@ -787,20 +772,26 @@ termp_it_pre(DECL_ARGS)
 	 */
 
 	switch (type) {
+	case (LIST_enum):
+		/*
+		 * Weird special case.
+		 * Very narrow enum lists actually hang.
+		 */
+		if (width == term_len(p, 2))
+			p->flags |= TERMP_HANG;
+		/* FALLTHROUGH */
 	case (LIST_bullet):
 		/* FALLTHROUGH */
 	case (LIST_dash):
 		/* FALLTHROUGH */
-	case (LIST_enum):
-		/* FALLTHROUGH */
 	case (LIST_hyphen):
-		if (MDOC_HEAD == n->type)
-			p->flags |= TERMP_NOBREAK;
+		if (MDOC_HEAD != n->type)
+			break;
+		p->flags |= TERMP_NOBREAK;
+		p->trailspace = 1;
 		break;
 	case (LIST_hang):
-		if (MDOC_HEAD == n->type)
-			p->flags |= TERMP_NOBREAK;
-		else
+		if (MDOC_HEAD != n->type)
 			break;
 
 		/*
@@ -812,16 +803,18 @@ termp_it_pre(DECL_ARGS)
 		if (n->next->child && 
 				(MDOC_Bl == n->next->child->tok ||
 				 MDOC_Bd == n->next->child->tok))
-			p->flags &= ~TERMP_NOBREAK;
-		else
-			p->flags |= TERMP_HANG;
+			break;
+
+		p->flags |= TERMP_NOBREAK | TERMP_HANG;
+		p->trailspace = 1;
 		break;
 	case (LIST_tag):
-		if (MDOC_HEAD == n->type)
-			p->flags |= TERMP_NOBREAK | TERMP_TWOSPACE;
-
 		if (MDOC_HEAD != n->type)
 			break;
+
+		p->flags |= TERMP_NOBREAK;
+		p->trailspace = 2;
+
 		if (NULL == n->next || NULL == n->next->child)
 			p->flags |= TERMP_DANGLE;
 		break;
@@ -829,15 +822,20 @@ termp_it_pre(DECL_ARGS)
 		if (MDOC_HEAD == n->type)
 			break;
 
-		if (NULL == n->next)
+		if (NULL == n->next) {
 			p->flags &= ~TERMP_NOBREAK;
-		else
+			p->trailspace = 0;
+		} else {
 			p->flags |= TERMP_NOBREAK;
+			p->trailspace = 1;
+		}
 
 		break;
 	case (LIST_diag):
-		if (MDOC_HEAD == n->type)
-			p->flags |= TERMP_NOBREAK;
+		if (MDOC_HEAD != n->type)
+			break;
+		p->flags |= TERMP_NOBREAK;
+		p->trailspace = 1;
 		break;
 	default:
 		break;
@@ -989,8 +987,8 @@ termp_it_post(DECL_ARGS)
 
 	p->flags &= ~TERMP_DANGLE;
 	p->flags &= ~TERMP_NOBREAK;
-	p->flags &= ~TERMP_TWOSPACE;
 	p->flags &= ~TERMP_HANG;
+	p->trailspace = 0;
 }
 
 
@@ -999,22 +997,25 @@ static int
 termp_nm_pre(DECL_ARGS)
 {
 
-	if (MDOC_BLOCK == n->type)
+	if (MDOC_BLOCK == n->type) {
+		p->flags |= TERMP_PREKEEP;
 		return(1);
+	}
 
 	if (MDOC_BODY == n->type) {
 		if (NULL == n->child)
 			return(0);
 		p->flags |= TERMP_NOSPACE;
 		p->offset += term_len(p, 1) +
-		    (NULL == n->prev->child ? term_strlen(p, m->name) :
+		    (NULL == n->prev->child ?
+		     term_strlen(p, meta->name) :
 		     MDOC_TEXT == n->prev->child->type ?
-			term_strlen(p, n->prev->child->string) :
+		     term_strlen(p, n->prev->child->string) :
 		     term_len(p, 5));
 		return(1);
 	}
 
-	if (NULL == n->child && NULL == m->name)
+	if (NULL == n->child && NULL == meta->name)
 		return(0);
 
 	if (MDOC_HEAD == n->type)
@@ -1022,9 +1023,10 @@ termp_nm_pre(DECL_ARGS)
 
 	if (MDOC_HEAD == n->type && n->next->child) {
 		p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
+		p->trailspace = 1;
 		p->rmargin = p->offset + term_len(p, 1);
 		if (NULL == n->child) {
-			p->rmargin += term_strlen(p, m->name);
+			p->rmargin += term_strlen(p, meta->name);
 		} else if (MDOC_TEXT == n->child->type) {
 			p->rmargin += term_strlen(p, n->child->string);
 			if (n->child->next)
@@ -1037,7 +1039,7 @@ termp_nm_pre(DECL_ARGS)
 
 	term_fontpush(p, TERMFONT_BOLD);
 	if (NULL == n->child)
-		term_word(p, m->name);
+		term_word(p, meta->name);
 	return(1);
 }
 
@@ -1047,9 +1049,12 @@ static void
 termp_nm_post(DECL_ARGS)
 {
 
-	if (MDOC_HEAD == n->type && n->next->child) {
+	if (MDOC_BLOCK == n->type) {
+		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
+	} else if (MDOC_HEAD == n->type && n->next->child) {
 		term_flushln(p);
 		p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
+		p->trailspace = 0;
 	} else if (MDOC_BODY == n->type && n->child)
 		term_flushln(p);
 }
@@ -1375,14 +1380,14 @@ termp_vt_pre(DECL_ARGS)
 
 	if (MDOC_ELEM == n->type) {
 		synopsis_pre(p, n);
-		return(termp_under_pre(p, pair, m, n));
+		return(termp_under_pre(p, pair, meta, n));
 	} else if (MDOC_BLOCK == n->type) {
 		synopsis_pre(p, n);
 		return(1);
 	} else if (MDOC_HEAD == n->type)
 		return(0);
 
-	return(termp_under_pre(p, pair, m, n));
+	return(termp_under_pre(p, pair, meta, n));
 }
 
 
@@ -1402,7 +1407,16 @@ termp_fd_pre(DECL_ARGS)
 {
 
 	synopsis_pre(p, n);
-	return(termp_bold_pre(p, pair, m, n));
+	return(termp_bold_pre(p, pair, meta, n));
+}
+
+
+/* ARGSUSED */
+static void
+termp_fd_post(DECL_ARGS)
+{
+
+	term_newln(p);
 }
 
 
@@ -1425,6 +1439,8 @@ termp_sh_pre(DECL_ARGS)
 		break;
 	case (MDOC_BODY):
 		p->offset = term_len(p, p->defindent);
+		if (SEC_AUTHORS == n->sec)
+			p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
 		break;
 	default:
 		break;
@@ -1497,17 +1513,6 @@ termp_d1_pre(DECL_ARGS)
 }
 
 
-/* ARGSUSED */
-static void
-termp_d1_post(DECL_ARGS)
-{
-
-	if (MDOC_BLOCK != n->type) 
-		return;
-	term_newln(p);
-}
-
-
 /* ARGSUSED */
 static int
 termp_ft_pre(DECL_ARGS)
@@ -1524,6 +1529,7 @@ termp_ft_pre(DECL_ARGS)
 static int
 termp_fn_pre(DECL_ARGS)
 {
+	size_t		 rmargin = 0;
 	int		 pretty;
 
 	pretty = MDOC_SYNPRETTY & n->flags;
@@ -1533,11 +1539,24 @@ termp_fn_pre(DECL_ARGS)
 	if (NULL == (n = n->child))
 		return(0);
 
+	if (pretty) {
+		rmargin = p->rmargin;
+		p->rmargin = p->offset + term_len(p, 4);
+		p->flags |= TERMP_NOBREAK | TERMP_HANG;
+	}
+
 	assert(MDOC_TEXT == n->type);
 	term_fontpush(p, TERMFONT_BOLD);
 	term_word(p, n->string);
 	term_fontpop(p);
 
+	if (pretty) {
+		term_flushln(p);
+		p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
+		p->offset = p->rmargin;
+		p->rmargin = rmargin;
+	}
+
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, "(");
 	p->flags |= TERMP_NOSPACE;
@@ -1545,6 +1564,8 @@ termp_fn_pre(DECL_ARGS)
 	for (n = n->next; n; n = n->next) {
 		assert(MDOC_TEXT == n->type);
 		term_fontpush(p, TERMFONT_UNDER);
+		if (pretty)
+			p->flags |= TERMP_NBRWORD;
 		term_word(p, n->string);
 		term_fontpop(p);
 
@@ -1560,6 +1581,7 @@ termp_fn_pre(DECL_ARGS)
 	if (pretty) {
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, ";");
+		term_flushln(p);
 	}
 
 	return(0);
@@ -1579,20 +1601,16 @@ termp_fa_pre(DECL_ARGS)
 
 	for (nn = n->child; nn; nn = nn->next) {
 		term_fontpush(p, TERMFONT_UNDER);
+		p->flags |= TERMP_NBRWORD;
 		term_word(p, nn->string);
 		term_fontpop(p);
 
-		if (nn->next) {
+		if (nn->next || (n->next && n->next->tok == MDOC_Fa)) {
 			p->flags |= TERMP_NOSPACE;
 			term_word(p, ",");
 		}
 	}
 
-	if (n->child && n->next && n->next->tok == MDOC_Fa) {
-		p->flags |= TERMP_NOSPACE;
-		term_word(p, ",");
-	}
-
 	return(0);
 }
 
@@ -1602,7 +1620,7 @@ static int
 termp_bd_pre(DECL_ARGS)
 {
 	size_t			 tabwidth, rm, rmax;
-	const struct mdoc_node	*nn;
+	struct mdoc_node	*nn;
 
 	if (MDOC_BLOCK == n->type) {
 		print_bvspace(p, n, n);
@@ -1634,7 +1652,7 @@ termp_bd_pre(DECL_ARGS)
 	p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
 
 	for (nn = n->child; nn; nn = nn->next) {
-		print_mdoc_node(p, pair, m, nn);
+		print_mdoc_node(p, pair, meta, nn);
 		/*
 		 * If the printed node flushes its own line, then we
 		 * needn't do it here as well.  This is hacky, but the
@@ -1751,7 +1769,8 @@ termp_xx_pre(DECL_ARGS)
 		pp = "UNIX";
 		break;
 	default:
-		break;
+		abort();
+		/* NOTREACHED */
 	}
 
 	term_word(p, pp);
@@ -1765,16 +1784,6 @@ termp_xx_pre(DECL_ARGS)
 }
 
 
-/* ARGSUSED */
-static int
-termp_igndelim_pre(DECL_ARGS)
-{
-
-	p->flags |= TERMP_IGNDELIM;
-	return(1);
-}
-
-
 /* ARGSUSED */
 static void
 termp_pf_post(DECL_ARGS)
@@ -1923,7 +1932,7 @@ termp_quote_pre(DECL_ARGS)
 	case (MDOC_Do):
 		/* FALLTHROUGH */
 	case (MDOC_Dq):
-		term_word(p, "``");
+		term_word(p, "\\(lq");
 		break;
 	case (MDOC_Eo):
 		break;
@@ -1944,7 +1953,7 @@ termp_quote_pre(DECL_ARGS)
 	case (MDOC_So):
 		/* FALLTHROUGH */
 	case (MDOC_Sq):
-		term_word(p, "`");
+		term_word(p, "\\(oq");
 		break;
 	default:
 		abort();
@@ -1989,7 +1998,7 @@ termp_quote_post(DECL_ARGS)
 	case (MDOC_Do):
 		/* FALLTHROUGH */
 	case (MDOC_Dq):
-		term_word(p, "''");
+		term_word(p, "\\(rq");
 		break;
 	case (MDOC_Eo):
 		break;
@@ -2010,7 +2019,7 @@ termp_quote_post(DECL_ARGS)
 	case (MDOC_So):
 		/* FALLTHROUGH */
 	case (MDOC_Sq):
-		term_word(p, "'");
+		term_word(p, "\\(cq");
 		break;
 	default:
 		abort();
@@ -2023,16 +2032,31 @@ termp_quote_post(DECL_ARGS)
 static int
 termp_fo_pre(DECL_ARGS)
 {
+	size_t		 rmargin = 0;
+	int		 pretty;
+
+	pretty = MDOC_SYNPRETTY & n->flags;
 
 	if (MDOC_BLOCK == n->type) {
 		synopsis_pre(p, n);
 		return(1);
 	} else if (MDOC_BODY == n->type) {
+		if (pretty) {
+			rmargin = p->rmargin;
+			p->rmargin = p->offset + term_len(p, 4);
+			p->flags |= TERMP_NOBREAK | TERMP_HANG;
+		}
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, "(");
 		p->flags |= TERMP_NOSPACE;
+		if (pretty) {
+			term_flushln(p);
+			p->flags &= ~(TERMP_NOBREAK | TERMP_HANG);
+			p->offset = p->rmargin;
+			p->rmargin = rmargin;
+		}
 		return(1);
-	} 
+	}
 
 	if (NULL == n->child)
 		return(0);
@@ -2060,6 +2084,7 @@ termp_fo_post(DECL_ARGS)
 	if (MDOC_SYNPRETTY & n->flags) {
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, ";");
+		term_flushln(p);
 	}
 }
 
@@ -2071,7 +2096,7 @@ termp_bf_pre(DECL_ARGS)
 
 	if (MDOC_HEAD == n->type)
 		return(0);
-	else if (MDOC_BLOCK != n->type)
+	else if (MDOC_BODY != n->type)
 		return(1);
 
 	if (FONT_Em == n->norm->Bf.font) 
@@ -2157,25 +2182,24 @@ termp_li_pre(DECL_ARGS)
 static int
 termp_lk_pre(DECL_ARGS)
 {
-	const struct mdoc_node *nn, *sv;
+	const struct mdoc_node *link, *descr;
 
-	term_fontpush(p, TERMFONT_UNDER);
+	if (NULL == (link = n->child))
+		return(0);
 
-	nn = sv = n->child;
-
-	if (NULL == nn || NULL == nn->next)
-		return(1);
-
-	for (nn = nn->next; nn; nn = nn->next) 
-		term_word(p, nn->string);
-
-	term_fontpop(p);
-
-	p->flags |= TERMP_NOSPACE;
-	term_word(p, ":");
+	if (NULL != (descr = link->next)) {
+		term_fontpush(p, TERMFONT_UNDER);
+		while (NULL != descr) {
+			term_word(p, descr->string);
+			descr = descr->next;
+		}
+		p->flags |= TERMP_NOSPACE;
+		term_word(p, ":");
+		term_fontpop(p);
+	}
 
 	term_fontpush(p, TERMFONT_BOLD);
-	term_word(p, sv->string);
+	term_word(p, link->string);
 	term_fontpop(p);
 
 	return(0);
@@ -2225,9 +2249,9 @@ termp__t_post(DECL_ARGS)
 	 */
 	if (n->parent && MDOC_Rs == n->parent->tok &&
 			n->parent->norm->Rs.quote_T)
-		termp_quote_post(p, pair, m, n);
+		termp_quote_post(p, pair, meta, n);
 
-	termp____post(p, pair, m, n);
+	termp____post(p, pair, meta, n);
 }
 
 /* ARGSUSED */
@@ -2241,7 +2265,7 @@ termp__t_pre(DECL_ARGS)
 	 */
 	if (n->parent && MDOC_Rs == n->parent->tok &&
 			n->parent->norm->Rs.quote_T)
-		return(termp_quote_pre(p, pair, m, n));
+		return(termp_quote_pre(p, pair, meta, n));
 
 	term_fontpush(p, TERMFONT_UNDER);
 	return(1);
diff --git a/contrib/mdocml/mdoc_validate.c b/contrib/mdocml/mdoc_validate.c
index 0a1fce25029..588c070bc30 100644
--- a/contrib/mdocml/mdoc_validate.c
+++ b/contrib/mdocml/mdoc_validate.c
@@ -1,7 +1,7 @@
-/*	$Id: mdoc_validate.c,v 1.182 2012/03/23 05:50:25 kristaps Exp $ */
+/*	$Id: mdoc_validate.c,v 1.198 2013/12/15 21:23:52 schwarze Exp $ */
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010, 2011 Ingo Schwarze 
+ * Copyright (c) 2008-2012 Kristaps Dzonsons 
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -19,7 +19,7 @@
 #include "config.h"
 #endif
 
-#ifndef	OSNAME
+#ifndef OSNAME
 #include 
 #endif
 
@@ -97,17 +97,19 @@ static	int	 post_bl_block_width(POST_ARGS);
 static	int	 post_bl_block_tag(POST_ARGS);
 static	int	 post_bl_head(POST_ARGS);
 static	int	 post_bx(POST_ARGS);
+static	int	 post_defaults(POST_ARGS);
 static	int	 post_dd(POST_ARGS);
 static	int	 post_dt(POST_ARGS);
-static	int	 post_defaults(POST_ARGS);
-static	int	 post_literal(POST_ARGS);
 static	int	 post_eoln(POST_ARGS);
+static	int	 post_hyph(POST_ARGS);
+static	int	 post_ignpar(POST_ARGS);
 static	int	 post_it(POST_ARGS);
 static	int	 post_lb(POST_ARGS);
+static	int	 post_literal(POST_ARGS);
 static	int	 post_nm(POST_ARGS);
 static	int	 post_ns(POST_ARGS);
 static	int	 post_os(POST_ARGS);
-static	int	 post_ignpar(POST_ARGS);
+static	int	 post_par(POST_ARGS);
 static	int	 post_prol(POST_ARGS);
 static	int	 post_root(POST_ARGS);
 static	int	 post_rs(POST_ARGS);
@@ -141,27 +143,30 @@ static	v_post	 posts_bx[] = { post_bx, NULL };
 static	v_post	 posts_bool[] = { ebool, NULL };
 static	v_post	 posts_eoln[] = { post_eoln, NULL };
 static	v_post	 posts_defaults[] = { post_defaults, NULL };
+static	v_post	 posts_d1[] = { bwarn_ge1, post_hyph, NULL };
 static	v_post	 posts_dd[] = { post_dd, post_prol, NULL };
 static	v_post	 posts_dl[] = { post_literal, bwarn_ge1, NULL };
 static	v_post	 posts_dt[] = { post_dt, post_prol, NULL };
 static	v_post	 posts_fo[] = { hwarn_eq1, bwarn_ge1, NULL };
+static	v_post	 posts_hyph[] = { post_hyph, NULL };
+static	v_post	 posts_hyphtext[] = { ewarn_ge1, post_hyph, NULL };
 static	v_post	 posts_it[] = { post_it, NULL };
 static	v_post	 posts_lb[] = { post_lb, NULL };
-static	v_post	 posts_nd[] = { berr_ge1, NULL };
+static	v_post	 posts_nd[] = { berr_ge1, post_hyph, NULL };
 static	v_post	 posts_nm[] = { post_nm, NULL };
 static	v_post	 posts_notext[] = { ewarn_eq0, NULL };
 static	v_post	 posts_ns[] = { post_ns, NULL };
 static	v_post	 posts_os[] = { post_os, post_prol, NULL };
+static	v_post	 posts_pp[] = { post_par, ewarn_eq0, NULL };
 static	v_post	 posts_rs[] = { post_rs, NULL };
-static	v_post	 posts_sh[] = { post_ignpar, hwarn_ge1, post_sh, NULL };
-static	v_post	 posts_sp[] = { ewarn_le1, NULL };
-static	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, NULL };
+static	v_post	 posts_sh[] = { post_ignpar,hwarn_ge1,post_sh,post_hyph,NULL };
+static	v_post	 posts_sp[] = { post_par, ewarn_le1, NULL };
+static	v_post	 posts_ss[] = { post_ignpar, hwarn_ge1, post_hyph, NULL };
 static	v_post	 posts_st[] = { post_st, NULL };
 static	v_post	 posts_std[] = { post_std, NULL };
 static	v_post	 posts_text[] = { ewarn_ge1, NULL };
 static	v_post	 posts_text1[] = { ewarn_eq1, NULL };
 static	v_post	 posts_vt[] = { post_vt, NULL };
-static	v_post	 posts_wline[] = { bwarn_ge1, NULL };
 static	v_pre	 pres_an[] = { pre_an, NULL };
 static	v_pre	 pres_bd[] = { pre_display, pre_bd, pre_literal, pre_par, NULL };
 static	v_pre	 pres_bl[] = { pre_bl, pre_par, NULL };
@@ -169,8 +174,6 @@ static	v_pre	 pres_d1[] = { pre_display, NULL };
 static	v_pre	 pres_dl[] = { pre_literal, pre_display, NULL };
 static	v_pre	 pres_dd[] = { pre_dd, NULL };
 static	v_pre	 pres_dt[] = { pre_dt, NULL };
-static	v_pre	 pres_er[] = { NULL, NULL };
-static	v_pre	 pres_fd[] = { NULL, NULL };
 static	v_pre	 pres_it[] = { pre_it, pre_par, NULL };
 static	v_pre	 pres_os[] = { pre_os, NULL };
 static	v_pre	 pres_pp[] = { pre_par, NULL };
@@ -185,8 +188,8 @@ static	const struct valids mdoc_valids[MDOC_MAX] = {
 	{ pres_os, posts_os },			/* Os */
 	{ pres_sh, posts_sh },			/* Sh */ 
 	{ pres_ss, posts_ss },			/* Ss */ 
-	{ pres_pp, posts_notext },		/* Pp */ 
-	{ pres_d1, posts_wline },		/* D1 */
+	{ pres_pp, posts_pp },			/* Pp */ 
+	{ pres_d1, posts_d1 },			/* D1 */
 	{ pres_dl, posts_dl },			/* Dl */
 	{ pres_bd, posts_bd },			/* Bd */
 	{ NULL, NULL },				/* Ed */
@@ -199,11 +202,11 @@ static	const struct valids mdoc_valids[MDOC_MAX] = {
 	{ NULL, NULL },				/* Cd */ 
 	{ NULL, NULL },				/* Cm */
 	{ NULL, NULL },				/* Dv */ 
-	{ pres_er, NULL },			/* Er */ 
+	{ NULL, NULL },				/* Er */ 
 	{ NULL, NULL },				/* Ev */ 
 	{ pres_std, posts_std },		/* Ex */ 
 	{ NULL, NULL },				/* Fa */ 
-	{ pres_fd, posts_text },		/* Fd */
+	{ NULL, posts_text },			/* Fd */
 	{ NULL, NULL },				/* Fl */
 	{ NULL, NULL },				/* Fn */ 
 	{ NULL, NULL },				/* Ft */ 
@@ -221,15 +224,15 @@ static	const struct valids mdoc_valids[MDOC_MAX] = {
 	{ NULL, posts_vt },			/* Vt */ 
 	{ NULL, posts_text },			/* Xr */ 
 	{ NULL, posts_text },			/* %A */
-	{ NULL, posts_text },			/* %B */ /* FIXME: can be used outside Rs/Re. */
+	{ NULL, posts_hyphtext },		/* %B */ /* FIXME: can be used outside Rs/Re. */
 	{ NULL, posts_text },			/* %D */
 	{ NULL, posts_text },			/* %I */
 	{ NULL, posts_text },			/* %J */
-	{ NULL, posts_text },			/* %N */
-	{ NULL, posts_text },			/* %O */
+	{ NULL, posts_hyphtext },		/* %N */
+	{ NULL, posts_hyphtext },		/* %O */
 	{ NULL, posts_text },			/* %P */
-	{ NULL, posts_text },			/* %R */
-	{ NULL, posts_text },			/* %T */ /* FIXME: can be used outside Rs/Re. */
+	{ NULL, posts_hyphtext },		/* %R */
+	{ NULL, posts_hyphtext },		/* %T */ /* FIXME: can be used outside Rs/Re. */
 	{ NULL, posts_text },			/* %V */
 	{ NULL, NULL },				/* Ac */
 	{ NULL, NULL },				/* Ao */
@@ -269,7 +272,7 @@ static	const struct valids mdoc_valids[MDOC_MAX] = {
 	{ NULL, NULL },				/* So */
 	{ NULL, NULL },				/* Sq */
 	{ NULL, posts_bool },			/* Sm */ 
-	{ NULL, NULL },				/* Sx */
+	{ NULL, posts_hyph },			/* Sx */
 	{ NULL, NULL },				/* Sy */
 	{ NULL, NULL },				/* Tn */
 	{ NULL, NULL },				/* Ux */
@@ -286,7 +289,7 @@ static	const struct valids mdoc_valids[MDOC_MAX] = {
 	{ NULL, NULL },				/* Fr */
 	{ NULL, posts_eoln },			/* Ud */
 	{ NULL, posts_lb },			/* Lb */
-	{ NULL, posts_notext },			/* Lp */ 
+	{ pres_pp, posts_pp },			/* Lp */ 
 	{ NULL, NULL },				/* Lk */ 
 	{ NULL, posts_defaults },		/* Mt */ 
 	{ NULL, NULL },				/* Brq */ 
@@ -297,8 +300,8 @@ static	const struct valids mdoc_valids[MDOC_MAX] = {
 	{ NULL, NULL },				/* En */
 	{ NULL, NULL },				/* Dx */
 	{ NULL, posts_text },			/* %Q */
-	{ NULL, posts_notext },			/* br */
-	{ pres_pp, posts_sp },			/* sp */
+	{ NULL, posts_pp },			/* br */
+	{ NULL, posts_sp },			/* sp */
 	{ NULL, posts_text1 },			/* %U */
 	{ NULL, NULL },				/* Ta */
 };
@@ -314,12 +317,12 @@ static	const enum mdoct rsord[RSORD_MAX] = {
 	MDOC__R,
 	MDOC__N,
 	MDOC__V,
+	MDOC__U,
 	MDOC__P,
 	MDOC__Q,
-	MDOC__D,
-	MDOC__O,
 	MDOC__C,
-	MDOC__U
+	MDOC__D,
+	MDOC__O
 };
 
 static	const char * const secnames[SEC__MAX] = {
@@ -414,29 +417,29 @@ mdoc_valid_post(struct mdoc *mdoc)
 }
 
 static int
-check_count(struct mdoc *m, enum mdoc_type type, 
+check_count(struct mdoc *mdoc, enum mdoc_type type, 
 		enum check_lvl lvl, enum check_ineq ineq, int val)
 {
 	const char	*p;
 	enum mandocerr	 t;
 
-	if (m->last->type != type)
+	if (mdoc->last->type != type)
 		return(1);
 	
 	switch (ineq) {
 	case (CHECK_LT):
 		p = "less than ";
-		if (m->last->nchild < val)
+		if (mdoc->last->nchild < val)
 			return(1);
 		break;
 	case (CHECK_GT):
 		p = "more than ";
-		if (m->last->nchild > val)
+		if (mdoc->last->nchild > val)
 			return(1);
 		break;
 	case (CHECK_EQ):
 		p = "";
-		if (val == m->last->nchild)
+		if (val == mdoc->last->nchild)
 			return(1);
 		break;
 	default:
@@ -445,9 +448,9 @@ check_count(struct mdoc *m, enum mdoc_type type,
 	}
 
 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
-	mandoc_vmsg(t, m->parse, m->last->line, m->last->pos,
+	mandoc_vmsg(t, mdoc->parse, mdoc->last->line, mdoc->last->pos,
 			"want %s%d children (have %d)",
-			p, val, m->last->nchild);
+			p, val, mdoc->last->nchild);
 	return(1);
 }
 
@@ -513,7 +516,7 @@ hwarn_le1(POST_ARGS)
 }
 
 static void
-check_args(struct mdoc *m, struct mdoc_node *n)
+check_args(struct mdoc *mdoc, struct mdoc_node *n)
 {
 	int		 i;
 
@@ -522,34 +525,34 @@ check_args(struct mdoc *m, struct mdoc_node *n)
 
 	assert(n->args->argc);
 	for (i = 0; i < (int)n->args->argc; i++)
-		check_argv(m, n, &n->args->argv[i]);
+		check_argv(mdoc, n, &n->args->argv[i]);
 }
 
 static void
-check_argv(struct mdoc *m, struct mdoc_node *n, struct mdoc_argv *v)
+check_argv(struct mdoc *mdoc, struct mdoc_node *n, struct mdoc_argv *v)
 {
 	int		 i;
 
 	for (i = 0; i < (int)v->sz; i++)
-		check_text(m, v->line, v->pos, v->value[i]);
+		check_text(mdoc, v->line, v->pos, v->value[i]);
 
 	/* FIXME: move to post_std(). */
 
 	if (MDOC_Std == v->arg)
-		if ( ! (v->sz || m->meta.name))
-			mdoc_nmsg(m, n, MANDOCERR_NONAME);
+		if ( ! (v->sz || mdoc->meta.name))
+			mdoc_nmsg(mdoc, n, MANDOCERR_NONAME);
 }
 
 static void
-check_text(struct mdoc *m, int ln, int pos, char *p)
+check_text(struct mdoc *mdoc, int ln, int pos, char *p)
 {
 	char		*cp;
 
-	if (MDOC_LITERAL & m->flags)
+	if (MDOC_LITERAL & mdoc->flags)
 		return;
 
 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
-		mdoc_pmsg(m, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
+		mdoc_pmsg(mdoc, ln, pos + (int)(p - cp), MANDOCERR_BADTAB);
 }
 
 static int
@@ -733,14 +736,14 @@ pre_bl(PRE_ARGS)
 	/* 
 	 * Validate the width field.  Some list types don't need width
 	 * types and should be warned about them.  Others should have it
-	 * and must also be warned.
+	 * and must also be warned.  Yet others have a default and need
+	 * no warning.
 	 */
 
 	switch (n->norm->Bl.type) {
 	case (LIST_tag):
-		if (n->norm->Bl.width)
-			break;
-		mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
+		if (NULL == n->norm->Bl.width)
+			mdoc_nmsg(mdoc, n, MANDOCERR_NOWIDTHARG);
 		break;
 	case (LIST_column):
 		/* FALLTHROUGH */
@@ -754,6 +757,18 @@ pre_bl(PRE_ARGS)
 		if (n->norm->Bl.width)
 			mdoc_nmsg(mdoc, n, MANDOCERR_IGNARGV);
 		break;
+	case (LIST_bullet):
+		/* FALLTHROUGH */
+	case (LIST_dash):
+		/* FALLTHROUGH */
+	case (LIST_hyphen):
+		if (NULL == n->norm->Bl.width)
+			n->norm->Bl.width = "2n";
+		break;
+	case (LIST_enum):
+		if (NULL == n->norm->Bl.width)
+			n->norm->Bl.width = "3n";
+		break;
 	default:
 		break;
 	}
@@ -874,8 +889,6 @@ pre_sh(PRE_ARGS)
 
 	if (MDOC_BLOCK != n->type)
 		return(1);
-
-	roff_regunset(mdoc->roff, REG_nS);
 	return(check_parent(mdoc, n, MDOC_MAX, MDOC_ROOT));
 }
 
@@ -1111,24 +1124,29 @@ post_nm(POST_ARGS)
 	char		 buf[BUFSIZ];
 	int		 c;
 
-	/* If no child specified, make sure we have the meta name. */
-
-	if (NULL == mdoc->last->child && NULL == mdoc->meta.name) {
-		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
-		return(1);
-	} else if (mdoc->meta.name)
+	if (NULL != mdoc->meta.name)
 		return(1);
 
-	/* If no meta name, set it from the child. */
+	/* Try to use our children for setting the meta name. */
 
-	buf[0] = '\0';
-	if (-1 == (c = concat(buf, mdoc->last->child, BUFSIZ))) {
+	if (NULL != mdoc->last->child) {
+		buf[0] = '\0';
+		c = concat(buf, mdoc->last->child, BUFSIZ);
+	} else
+		c = 0;
+
+	switch (c) {
+	case (-1):
 		mdoc_nmsg(mdoc, mdoc->last->child, MANDOCERR_MEM);
 		return(0);
+	case (0):
+		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_NONAME);
+		mdoc->meta.name = mandoc_strdup("UNKNOWN");
+		break;
+	default:
+		mdoc->meta.name = mandoc_strdup(buf);
+		break;
 	}
-
-	assert(c);
-	mdoc->meta.name = mandoc_strdup(buf);
 	return(1);
 }
 
@@ -1334,7 +1352,7 @@ post_it(POST_ARGS)
 static int
 post_bl_block(POST_ARGS) 
 {
-	struct mdoc_node *n;
+	struct mdoc_node *n, *ni, *nc;
 
 	/*
 	 * These are fairly complicated, so we've broken them into two
@@ -1350,13 +1368,42 @@ post_bl_block(POST_ARGS)
 			NULL == n->norm->Bl.width) {
 		if ( ! post_bl_block_tag(mdoc))
 			return(0);
+		assert(n->norm->Bl.width);
 	} else if (NULL != n->norm->Bl.width) {
 		if ( ! post_bl_block_width(mdoc))
 			return(0);
-	} else 
-		return(1);
+		assert(n->norm->Bl.width);
+	}
 
-	assert(n->norm->Bl.width);
+	for (ni = n->body->child; ni; ni = ni->next) {
+		if (NULL == ni->body)
+			continue;
+		nc = ni->body->last;
+		while (NULL != nc) {
+			switch (nc->tok) {
+			case (MDOC_Pp):
+				/* FALLTHROUGH */
+			case (MDOC_Lp):
+				/* FALLTHROUGH */
+			case (MDOC_br):
+				break;
+			default:
+				nc = NULL;
+				continue;
+			}
+			if (NULL == ni->next) {
+				mdoc_nmsg(mdoc, nc, MANDOCERR_MOVEPAR);
+				if ( ! mdoc_node_relink(mdoc, nc))
+					return(0);
+			} else if (0 == n->norm->Bl.comp &&
+			    LIST_column != n->norm->Bl.type) {
+				mdoc_nmsg(mdoc, nc, MANDOCERR_IGNPAR);
+				mdoc_node_delete(mdoc, nc);
+			} else
+				break;
+			nc = ni->body->last;
+		}
+	}
 	return(1);
 }
 
@@ -1544,32 +1591,71 @@ post_bl_head(POST_ARGS)
 static int
 post_bl(POST_ARGS)
 {
-	struct mdoc_node	*n;
+	struct mdoc_node	*nparent, *nprev; /* of the Bl block */
+	struct mdoc_node	*nblock, *nbody;  /* of the Bl */
+	struct mdoc_node	*nchild, *nnext;  /* of the Bl body */
 
-	if (MDOC_HEAD == mdoc->last->type) 
-		return(post_bl_head(mdoc));
-	if (MDOC_BLOCK == mdoc->last->type)
+	nbody = mdoc->last;
+	switch (nbody->type) {
+	case (MDOC_BLOCK):
 		return(post_bl_block(mdoc));
-	if (MDOC_BODY != mdoc->last->type)
+	case (MDOC_HEAD):
+		return(post_bl_head(mdoc));
+	case (MDOC_BODY):
+		break;
+	default:
 		return(1);
+	}
 
-	for (n = mdoc->last->child; n; n = n->next) {
-		switch (n->tok) {
-		case (MDOC_Lp):
-			/* FALLTHROUGH */
-		case (MDOC_Pp):
-			mdoc_nmsg(mdoc, n, MANDOCERR_CHILD);
-			/* FALLTHROUGH */
-		case (MDOC_It):
-			/* FALLTHROUGH */
-		case (MDOC_Sm):
+	nchild = nbody->child;
+	while (NULL != nchild) {
+		if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
+			nchild = nchild->next;
 			continue;
-		default:
-			break;
 		}
 
-		mdoc_nmsg(mdoc, n, MANDOCERR_SYNTCHILD);
-		return(0);
+		mdoc_nmsg(mdoc, nchild, MANDOCERR_CHILD);
+
+		/*
+		 * Move the node out of the Bl block.
+		 * First, collect all required node pointers.
+		 */
+
+		nblock  = nbody->parent;
+		nprev   = nblock->prev;
+		nparent = nblock->parent;
+		nnext   = nchild->next;
+
+		/*
+		 * Unlink this child.
+		 */
+
+		assert(NULL == nchild->prev);
+		if (0 == --nbody->nchild) {
+			nbody->child = NULL;
+			nbody->last  = NULL;
+			assert(NULL == nnext);
+		} else {
+			nbody->child = nnext;
+			nnext->prev = NULL;
+		}
+
+		/*
+		 * Relink this child.
+		 */
+
+		nchild->parent = nparent;
+		nchild->prev   = nprev;
+		nchild->next   = nblock;
+
+		nblock->prev = nchild;
+		nparent->nchild++;
+		if (NULL == nprev)
+			nparent->child = nchild;
+		else
+			nprev->next = nchild;
+
+		nchild = nnext;
 	}
 
 	return(1);
@@ -1588,10 +1674,16 @@ ebool(struct mdoc *mdoc)
 
 	assert(MDOC_TEXT == mdoc->last->child->type);
 
-	if (0 == strcmp(mdoc->last->child->string, "on"))
+	if (0 == strcmp(mdoc->last->child->string, "on")) {
+		if (MDOC_Sm == mdoc->last->tok)
+			mdoc->flags &= ~MDOC_SMOFF;
 		return(1);
-	if (0 == strcmp(mdoc->last->child->string, "off"))
+	}
+	if (0 == strcmp(mdoc->last->child->string, "off")) {
+		if (MDOC_Sm == mdoc->last->tok)
+			mdoc->flags |= MDOC_SMOFF;
 		return(1);
+	}
 
 	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_BADBOOL);
 	return(1);
@@ -1771,6 +1863,47 @@ post_rs(POST_ARGS)
 	return(1);
 }
 
+/*
+ * For some arguments of some macros,
+ * convert all breakable hyphens into ASCII_HYPH.
+ */
+static int
+post_hyph(POST_ARGS)
+{
+	struct mdoc_node	*n, *nch;
+	char			*cp;
+
+	n = mdoc->last;
+	switch (n->type) {
+	case (MDOC_HEAD):
+		if (MDOC_Sh == n->tok || MDOC_Ss == n->tok)
+			break;
+		return(1);
+	case (MDOC_BODY):
+		if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
+			break;
+		return(1);
+	case (MDOC_ELEM):
+		break;
+	default:
+		return(1);
+	}
+
+	for (nch = n->child; nch; nch = nch->next) {
+		if (MDOC_TEXT != nch->type)
+			continue;
+		cp = nch->string;
+		if (3 > strnlen(cp, 3))
+			continue;
+		while ('\0' != *(++cp))
+			if ('-' == *cp &&
+			    isalpha((unsigned char)cp[-1]) &&
+			    isalpha((unsigned char)cp[1]))
+				*cp = ASCII_HYPH;
+	}
+	return(1);
+}
+
 static int
 post_ns(POST_ARGS)
 {
@@ -1857,10 +1990,13 @@ post_sh_head(POST_ARGS)
 
 	/* The SYNOPSIS gets special attention in other areas. */
 
-	if (SEC_SYNOPSIS == sec)
+	if (SEC_SYNOPSIS == sec) {
+		roff_setreg(mdoc->roff, "nS", 1, '=');
 		mdoc->flags |= MDOC_SYNOPSIS;
-	else
+	} else {
+		roff_setreg(mdoc->roff, "nS", 0, '=');
 		mdoc->flags &= ~MDOC_SYNOPSIS;
+	}
 
 	/* Mark our last section. */
 
@@ -1916,7 +2052,8 @@ post_sh_head(POST_ARGS)
 			break;
 		if (*mdoc->meta.msec == '9')
 			break;
-		mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_SECMSEC);
+		mandoc_msg(MANDOCERR_SECMSEC, mdoc->parse,
+				mdoc->last->line, mdoc->last->pos, buf);
 		break;
 	default:
 		break;
@@ -1962,7 +2099,9 @@ pre_par(PRE_ARGS)
 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
 	 */
 
-	if (MDOC_Pp != mdoc->last->tok && MDOC_Lp != mdoc->last->tok)
+	if (MDOC_Pp != mdoc->last->tok &&
+	    MDOC_Lp != mdoc->last->tok &&
+	    MDOC_br != mdoc->last->tok)
 		return(1);
 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
 		return(1);
@@ -1976,6 +2115,32 @@ pre_par(PRE_ARGS)
 	return(1);
 }
 
+static int
+post_par(POST_ARGS)
+{
+
+	if (MDOC_ELEM != mdoc->last->type &&
+	    MDOC_BLOCK != mdoc->last->type)
+		return(1);
+
+	if (NULL == mdoc->last->prev) {
+		if (MDOC_Sh != mdoc->last->parent->tok &&
+		    MDOC_Ss != mdoc->last->parent->tok)
+			return(1);
+	} else {
+		if (MDOC_Pp != mdoc->last->prev->tok &&
+		    MDOC_Lp != mdoc->last->prev->tok &&
+		    (MDOC_br != mdoc->last->tok ||
+		     (MDOC_sp != mdoc->last->prev->tok &&
+		      MDOC_br != mdoc->last->prev->tok)))
+			return(1);
+	}
+
+	mdoc_nmsg(mdoc, mdoc->last, MANDOCERR_IGNPAR);
+	mdoc_node_delete(mdoc, mdoc->last);
+	return(1);
+}
+
 static int
 pre_literal(PRE_ARGS)
 {
@@ -2129,9 +2294,9 @@ post_dt(POST_ARGS)
 		free(mdoc->meta.vol);
 		mdoc->meta.vol = mandoc_strdup(cp);
 	} else {
-		/* FIXME: warn about bad arch. */
 		cp = mdoc_a2arch(nn->string);
 		if (NULL == cp) {
+			mdoc_nmsg(mdoc, nn, MANDOCERR_BADVOLARCH);
 			free(mdoc->meta.vol);
 			mdoc->meta.vol = mandoc_strdup(nn->string);
 		} else 
@@ -2192,14 +2357,15 @@ post_os(POST_ARGS)
 	n = mdoc->last;
 
 	/*
-	 * Set the operating system by way of the `Os' macro.  Note that
-	 * if an argument isn't provided and -DOSNAME="\"foo\"" is
-	 * provided during compilation, this value will be used instead
-	 * of filling in "sysname release" from uname().
+	 * Set the operating system by way of the `Os' macro.
+	 * The order of precedence is:
+	 * 1. the argument of the `Os' macro, unless empty
+	 * 2. the -Ios=foo command line argument, if provided
+	 * 3. -DOSNAME="\"foo\"", if provided during compilation
+	 * 4. "sysname release" from uname(3)
  	 */
 
-	if (mdoc->meta.os)
-		free(mdoc->meta.os);
+	free(mdoc->meta.os);
 
 	buf[0] = '\0';
 	if (-1 == (c = concat(buf, n->child, BUFSIZ))) {
@@ -2209,11 +2375,11 @@ post_os(POST_ARGS)
 
 	assert(c);
 
-	/* XXX: yes, these can all be dynamically-adjusted buffers, but
-	 * it's really not worth the extra hackery.
-	 */
-
 	if ('\0' == buf[0]) {
+		if (mdoc->defos) {
+			mdoc->meta.os = mandoc_strdup(mdoc->defos);
+			return(1);
+		}
 #ifdef OSNAME
 		if (strlcat(buf, OSNAME, BUFSIZ) >= BUFSIZ) {
 			mdoc_nmsg(mdoc, n, MANDOCERR_MEM);
diff --git a/contrib/mdocml/out.c b/contrib/mdocml/out.c
index 8dbd68ac11a..c9316649778 100644
--- a/contrib/mdocml/out.c
+++ b/contrib/mdocml/out.c
@@ -1,4 +1,4 @@
-/*	$Id: out.c,v 1.43 2011/09/20 23:05:49 schwarze Exp $ */
+/*	$Id: out.c,v 1.46 2013/10/05 20:30:05 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2011 Ingo Schwarze 
@@ -32,11 +32,11 @@
 #include "out.h"
 
 static	void	tblcalc_data(struct rofftbl *, struct roffcol *,
-			const struct tbl *, const struct tbl_dat *);
+			const struct tbl_opts *, const struct tbl_dat *);
 static	void	tblcalc_literal(struct rofftbl *, struct roffcol *,
 			const struct tbl_dat *);
 static	void	tblcalc_number(struct rofftbl *, struct roffcol *,
-			const struct tbl *, const struct tbl_dat *);
+			const struct tbl_opts *, const struct tbl_dat *);
 
 /* 
  * Convert a `scaling unit' to a consistent form, or fail.  Scaling
@@ -142,7 +142,6 @@ void
 tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
 {
 	const struct tbl_dat	*dp;
-	const struct tbl_head	*hp;
 	struct roffcol		*col;
 	int			 spans;
 
@@ -154,9 +153,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
 
 	assert(NULL == tbl->cols);
 	tbl->cols = mandoc_calloc
-		((size_t)sp->tbl->cols, sizeof(struct roffcol));
-
-	hp = sp->head;
+		((size_t)sp->opts->cols, sizeof(struct roffcol));
 
 	for ( ; sp; sp = sp->next) {
 		if (TBL_SPAN_DATA != sp->pos)
@@ -175,33 +172,14 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
 				continue;
 			assert(dp->layout);
 			col = &tbl->cols[dp->layout->head->ident];
-			tblcalc_data(tbl, col, sp->tbl, dp);
-		}
-	}
-
-	/* 
-	 * Calculate width of the spanners.  These get one space for a
-	 * vertical line, two for a double-vertical line. 
-	 */
-
-	for ( ; hp; hp = hp->next) {
-		col = &tbl->cols[hp->ident];
-		switch (hp->pos) {
-		case (TBL_HEAD_VERT):
-			col->width = (*tbl->len)(1, tbl->arg);
-			break;
-		case (TBL_HEAD_DVERT):
-			col->width = (*tbl->len)(2, tbl->arg);
-			break;
-		default:
-			break;
+			tblcalc_data(tbl, col, sp->opts, dp);
 		}
 	}
 }
 
 static void
 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
-		const struct tbl *tp, const struct tbl_dat *dp)
+		const struct tbl_opts *opts, const struct tbl_dat *dp)
 {
 	size_t		 sz;
 
@@ -225,7 +203,7 @@ tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
 		tblcalc_literal(tbl, col, dp);
 		break;
 	case (TBL_CELL_NUMBER):
-		tblcalc_number(tbl, col, tp, dp);
+		tblcalc_number(tbl, col, opts, dp);
 		break;
 	case (TBL_CELL_DOWN):
 		break;
@@ -251,7 +229,7 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
 
 static void
 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
-		const struct tbl *tp, const struct tbl_dat *dp)
+		const struct tbl_opts *opts, const struct tbl_dat *dp)
 {
 	int 		 i;
 	size_t		 sz, psz, ssz, d;
@@ -273,12 +251,12 @@ tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
 
 	/* FIXME: TBL_DATA_HORIZ et al.? */
 
-	buf[0] = tp->decimal;
+	buf[0] = opts->decimal;
 	buf[1] = '\0';
 
 	psz = (*tbl->slen)(buf, tbl->arg);
 
-	if (NULL != (cp = strrchr(str, tp->decimal))) {
+	if (NULL != (cp = strrchr(str, opts->decimal))) {
 		buf[1] = '\0';
 		for (ssz = 0, i = 0; cp != &str[i]; i++) {
 			buf[0] = str[i];
diff --git a/contrib/mdocml/predefs.in b/contrib/mdocml/predefs.in
index 70074bb617b..d1690e35680 100644
--- a/contrib/mdocml/predefs.in
+++ b/contrib/mdocml/predefs.in
@@ -1,4 +1,4 @@
-/*	$Id: predefs.in,v 1.3 2011/07/31 11:36:49 schwarze Exp $ */
+/*	$Id: predefs.in,v 1.4 2012/07/18 10:39:19 schwarze Exp $ */
 /*
  * Copyright (c) 2011 Kristaps Dzonsons 
  *
@@ -26,7 +26,7 @@
  */
 
 PREDEF("Am", "&")
-PREDEF("Ba", "|")
+PREDEF("Ba", "\\fR|\\fP")
 PREDEF("Ge", "\\(>=")
 PREDEF("Gt", ">")
 PREDEF("If", "infinity")
diff --git a/contrib/mdocml/read.c b/contrib/mdocml/read.c
index 5b14e357d2b..511ba7dc466 100644
--- a/contrib/mdocml/read.c
+++ b/contrib/mdocml/read.c
@@ -1,7 +1,7 @@
-/*	$Id: read.c,v 1.28 2012/02/16 20:51:31 joerg Exp $ */
+/*	$Id: read.c,v 1.39 2013/09/16 00:25:07 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010, 2011 Ingo Schwarze 
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -40,10 +40,6 @@
 #include "man.h"
 #include "main.h"
 
-#ifndef MAP_FILE
-#define	MAP_FILE	0
-#endif
-
 #define	REPARSE_LIMIT	1000
 
 struct	buf {
@@ -66,14 +62,16 @@ struct	mparse {
 	void		 *arg; /* argument to mmsg */
 	const char	 *file; 
 	struct buf	 *secondary;
+	char		 *defos; /* default operating system */
 };
 
 static	void	  resize_buf(struct buf *, size_t);
 static	void	  mparse_buf_r(struct mparse *, struct buf, int);
-static	void	  mparse_readfd_r(struct mparse *, int, const char *, int);
 static	void	  pset(const char *, int, struct mparse *);
 static	int	  read_whole_file(const char *, int, struct buf *, int *);
 static	void	  mparse_end(struct mparse *);
+static	void	  mparse_parse_buffer(struct mparse *, struct buf,
+			const char *);
 
 static	const enum mandocerr	mandoclimits[MANDOCLEVEL_MAX] = {
 	MANDOCERR_OK,
@@ -94,6 +92,7 @@ static	const char * const	mandocerrs[MANDOCERR_MAX] = {
 	"no title in document",
 	"document title should be all caps",
 	"unknown manual section",
+	"unknown manual volume or arch",
 	"date missing, using today's date",
 	"cannot parse date, using it verbatim",
 	"prologue macros out of order",
@@ -105,14 +104,14 @@ static	const char * const	mandocerrs[MANDOCERR_MAX] = {
 	".so is fragile, better use ln(1)",
 	"NAME section must come first",
 	"bad NAME section contents",
-	"manual name not yet set",
 	"sections out of conventional order",
 	"duplicate section name",
-	"section not in conventional manual section",
+	"section header suited to sections 2, 3, and 9 only",
 
 	/* related to macros and nesting */
 	"skipping obsolete macro",
 	"skipping paragraph macro",
+	"moving paragraph macro out of list",
 	"skipping no-space macro",
 	"blocks badly nested",
 	"child violates parent syntax",
@@ -173,10 +172,12 @@ static	const char * const	mandocerrs[MANDOCERR_MAX] = {
 	"input stack limit exceeded, infinite loop?",
 	"skipping bad character",
 	"escaped character not allowed in a name",
+	"manual name not yet set",
 	"skipping text before the first section header",
 	"skipping unknown macro",
 	"NOT IMPLEMENTED, please use groff: skipping request",
 	"argument count wrong",
+	"skipping column outside column list",
 	"skipping end of block that is not open",
 	"missing end of block",
 	"scope open on exit",
@@ -184,6 +185,7 @@ static	const char * const	mandocerrs[MANDOCERR_MAX] = {
 	"macro requires line argument(s)",
 	"macro requires body argument(s)",
 	"macro requires argument(s)",
+	"request requires a numeric argument",
 	"missing list type",
 	"line argument(s) will be lost",
 	"body argument(s) will be lost",
@@ -247,7 +249,8 @@ pset(const char *buf, int pos, struct mparse *curp)
 	switch (curp->inttype) {
 	case (MPARSE_MDOC):
 		if (NULL == curp->pmdoc) 
-			curp->pmdoc = mdoc_alloc(curp->roff, curp);
+			curp->pmdoc = mdoc_alloc(curp->roff, curp,
+					curp->defos);
 		assert(curp->pmdoc);
 		curp->mdoc = curp->pmdoc;
 		return;
@@ -263,7 +266,8 @@ pset(const char *buf, int pos, struct mparse *curp)
 
 	if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
 		if (NULL == curp->pmdoc) 
-			curp->pmdoc = mdoc_alloc(curp->roff, curp);
+			curp->pmdoc = mdoc_alloc(curp->roff, curp,
+					curp->defos);
 		assert(curp->pmdoc);
 		curp->mdoc = curp->pmdoc;
 		return;
@@ -322,6 +326,15 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
 				break;
 			}
 
+			/*
+			 * Make sure we have space for at least
+			 * one backslash and one other character
+			 * and the trailing NUL byte.
+			 */
+
+			if (pos + 2 >= (int)ln.sz)
+				resize_buf(&ln, 256);
+
 			/* 
 			 * Warn about bogus characters.  If you're using
 			 * non-ASCII encoding, you're screwing your
@@ -338,8 +351,6 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
 				mandoc_msg(MANDOCERR_BADCHAR, curp,
 						curp->line, pos, NULL);
 				i++;
-				if (pos >= (int)ln.sz)
-					resize_buf(&ln, 256);
 				ln.buf[pos++] = '?';
 				continue;
 			}
@@ -347,8 +358,6 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
 			/* Trailing backslash = a plain char. */
 
 			if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
-				if (pos >= (int)ln.sz)
-					resize_buf(&ln, 256);
 				ln.buf[pos++] = blk.buf[i++];
 				continue;
 			}
@@ -390,10 +399,20 @@ mparse_buf_r(struct mparse *curp, struct buf blk, int start)
 				break;
 			}
 
-			/* Some other escape sequence, copy & cont. */
+			/* Catch escaped bogus characters. */
 
-			if (pos + 1 >= (int)ln.sz)
-				resize_buf(&ln, 256);
+			c = (unsigned char) blk.buf[i+1];
+
+			if ( ! (isascii(c) && 
+					(isgraph(c) || isblank(c)))) {
+				mandoc_msg(MANDOCERR_BADCHAR, curp,
+						curp->line, pos, NULL);
+				i += 2;
+				ln.buf[pos++] = '?';
+				continue;
+			}
+
+			/* Some other escape sequence, copy & cont. */
 
 			ln.buf[pos++] = blk.buf[i++];
 			ln.buf[pos++] = blk.buf[i++];
@@ -469,7 +488,7 @@ rerun:
 			 */
 			if (curp->secondary) 
 				curp->secondary->sz -= pos + 1;
-			mparse_readfd_r(curp, -1, ln.buf + of, 1);
+			mparse_readfd(curp, -1, ln.buf + of);
 			if (MANDOCLEVEL_FATAL <= curp->file_status)
 				break;
 			pos = 0;
@@ -575,8 +594,7 @@ read_whole_file(const char *file, int fd, struct buf *fb, int *with_mmap)
 		}
 		*with_mmap = 1;
 		fb->sz = (size_t)st.st_size;
-		fb->buf = mmap(NULL, fb->sz, PROT_READ, 
-				MAP_FILE|MAP_SHARED, fd, 0);
+		fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0);
 		if (fb->buf != MAP_FAILED)
 			return(1);
 	}
@@ -643,19 +661,25 @@ mparse_end(struct mparse *curp)
 }
 
 static void
-mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file,
-		int re)
+mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file)
 {
 	const char	*svfile;
+	static int	 recursion_depth;
+
+	if (64 < recursion_depth) {
+		mandoc_msg(MANDOCERR_ROFFLOOP, curp, curp->line, 0, NULL);
+		return;
+	}
 
 	/* Line number is per-file. */
 	svfile = curp->file;
 	curp->file = file;
 	curp->line = 1;
+	recursion_depth++;
 
 	mparse_buf_r(curp, blk, 1);
 
-	if (0 == re && MANDOCLEVEL_FATAL > curp->file_status)
+	if (0 == --recursion_depth && MANDOCLEVEL_FATAL > curp->file_status)
 		mparse_end(curp);
 
 	curp->file = svfile;
@@ -670,12 +694,12 @@ mparse_readmem(struct mparse *curp, const void *buf, size_t len,
 	blk.buf = UNCONST(buf);
 	blk.sz = len;
 
-	mparse_parse_buffer(curp, blk, file, 0);
+	mparse_parse_buffer(curp, blk, file);
 	return(curp->file_status);
 }
 
-static void
-mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
+enum mandoclevel
+mparse_readfd(struct mparse *curp, int fd, const char *file)
 {
 	struct buf	 blk;
 	int		 with_mmap;
@@ -684,7 +708,7 @@ mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
 		if (-1 == (fd = open(file, O_RDONLY, 0))) {
 			perror(file);
 			curp->file_status = MANDOCLEVEL_SYSERR;
-			return;
+			goto out;
 		}
 	/*
 	 * Run for each opened file; may be called more than once for
@@ -695,10 +719,10 @@ mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
 
 	if ( ! read_whole_file(file, fd, &blk, &with_mmap)) {
 		curp->file_status = MANDOCLEVEL_SYSERR;
-		return;
+		goto out;
 	}
 
-	mparse_parse_buffer(curp, blk, file, re);
+	mparse_parse_buffer(curp, blk, file);
 
 #ifdef	HAVE_MMAP
 	if (with_mmap)
@@ -709,18 +733,13 @@ mparse_readfd_r(struct mparse *curp, int fd, const char *file, int re)
 
 	if (STDIN_FILENO != fd && -1 == close(fd))
 		perror(file);
-}
-
-enum mandoclevel
-mparse_readfd(struct mparse *curp, int fd, const char *file)
-{
-
-	mparse_readfd_r(curp, fd, file, 0);
+out:
 	return(curp->file_status);
 }
 
 struct mparse *
-mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, mandocmsg mmsg, void *arg)
+mparse_alloc(enum mparset inttype, enum mandoclevel wlevel,
+		mandocmsg mmsg, void *arg, char *defos)
 {
 	struct mparse	*curp;
 
@@ -732,8 +751,9 @@ mparse_alloc(enum mparset inttype, enum mandoclevel wlevel, mandocmsg mmsg, void
 	curp->mmsg = mmsg;
 	curp->arg = arg;
 	curp->inttype = inttype;
+	curp->defos = defos;
 
-	curp->roff = roff_alloc(curp);
+	curp->roff = roff_alloc(inttype, curp);
 	return(curp);
 }
 
diff --git a/contrib/mdocml/roff.7 b/contrib/mdocml/roff.7
index dd32a45b290..48e16dd905b 100644
--- a/contrib/mdocml/roff.7
+++ b/contrib/mdocml/roff.7
@@ -1,7 +1,7 @@
-.\"	$Id: roff.7,v 1.37 2011/12/11 00:38:11 schwarze Exp $
+.\"	$Id: roff.7,v 1.46 2013/12/26 02:43:18 schwarze Exp $
 .\"
-.\" Copyright (c) 2010, 2011 Kristaps Dzonsons 
-.\" Copyright (c) 2010, 2011 Ingo Schwarze 
+.\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons 
+.\" Copyright (c) 2010, 2011, 2013 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
@@ -15,7 +15,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: December 11 2011 $
+.Dd $Mdocdate: December 26 2013 $
 .Dt ROFF 7
 .Os
 .Sh NAME
@@ -32,7 +32,7 @@ and
 manual formatting languages are based on it,
 many real-world manuals use small numbers of
 .Nm
-requests intermixed with their
+requests and escape sequences intermixed with their
 .Xr mdoc 7
 or
 .Xr man 7
@@ -41,8 +41,8 @@ To properly format such manuals, the
 .Xr mandoc 1
 utility supports a tiny subset of
 .Nm
-requests.
-Only these requests supported by
+requests and escapes.
+Only these requests and escapes supported by
 .Xr mandoc 1
 are documented in the present manual,
 together with the basic language syntax shared by
@@ -83,9 +83,9 @@ depends on the respective processing context.
 .Nm
 documents may contain only graphable 7-bit ASCII characters, the space
 character, and, in certain circumstances, the tab character.
-The back-space character
+The backslash character
 .Sq \e
-indicates the start of an escape sequence for
+indicates the start of an escape sequence, used for example for
 .Sx Comments ,
 .Sx Special Characters ,
 .Sx Predefined Strings ,
@@ -93,6 +93,9 @@ and
 user-defined strings defined using the
 .Sx ds
 request.
+For a listing of escape sequences, consult the
+.Sx ESCAPE SEQUENCE REFERENCE
+below.
 .Ss Comments
 Text following an escaped double-quote
 .Sq \e\(dq ,
@@ -146,12 +149,19 @@ respectively) may be used instead.
 The indicator or numerical representative may be preceded by C
 (constant-width), which is ignored.
 .Pp
+The two-character indicator
+.Sq BI
+requests a font that is both bold and italic.
+It may not be portable to old roff implementations.
+.Pp
 Examples:
 .Bl -tag -width Ds -offset indent -compact
 .It Li \efBbold\efR
-Write in bold, then switch to regular font mode.
+Write in \fBbold\fP, then switch to regular font mode.
 .It Li \efIitalic\efP
-Write in italic, then return to previous font mode.
+Write in \fIitalic\fP, then return to previous font mode.
+.It Li \ef(BIbold italic\efP
+Write in \f(BIbold italic\fP, then return to previous font mode.
 .El
 .Pp
 Text decoration is
@@ -417,6 +427,18 @@ The syntax of this request is the same as that of
 It is currently ignored by
 .Xr mandoc 1 ,
 as are its children.
+.Ss \&cc
+Changes the control character.
+Its syntax is as follows:
+.Bd -literal -offset indent
+.Pf . Cm \&cc Op Ar c
+.Ed
+.Pp
+If
+.Ar c
+is not specified, the control character is reset to
+.Sq \&. .
+Trailing characters are ignored.
 .Ss \&de
 Define a
 .Nm
@@ -619,6 +641,15 @@ Begin an equation block.
 See
 .Xr eqn 7
 for a description of the equation language.
+.Ss \&fam
+Change the font family.
+This line-scoped request is intended to have one argument specifying
+the font family to be selected.
+It is a groff extension, and currently, it is ignored including its
+arguments, and the number of arguments is not checked.
+.Ss \&hw
+Specify hyphenation points in words.
+This line-scoped request is currently ignored.
 .Ss \&hy
 Set automatic hyphenation mode.
 This line-scoped request is currently ignored.
@@ -786,19 +817,22 @@ the name of the request, macro or string to be undefined.
 Currently, it is ignored including its arguments,
 and the number of arguments is not checked.
 .Ss \&nr
-Define a register.
+Define or change a register.
 A register is an arbitrary string value that defines some sort of state,
 which influences parsing and/or formatting.
 Its syntax is as follows:
 .Pp
-.D1 Pf \. Cm \&nr Ar name Ar value
+.D1 Pf \. Cm \&nr Ar name Oo +|- Oc Ns Ar value
 .Pp
 The
 .Ar value
 may, at the moment, only be an integer.
-So far, only the following register
+If it is prefixed by a sign, the register will be
+incremented or decremented instead of assigned to.
+.Pp
+The following register
 .Ar name
-is recognised:
+is handled specially:
 .Bl -tag -width Ds
 .It Cm nS
 If set to a positive integer value, certain
@@ -895,8 +929,251 @@ Begin a table, which formats input in aligned rows and columns.
 See
 .Xr tbl 7
 for a description of the tbl language.
+.Sh ESCAPE SEQUENCE REFERENCE
+The
+.Xr mandoc 1
+.Nm
+parser recognises the following escape sequences.
+Note that the
+.Nm
+language defines more escape sequences not implemented in
+.Xr mandoc 1 .
+In
+.Xr mdoc 7
+and
+.Xr man 7
+documents, using escape sequences is discouraged except for those
+described in the
+.Sx LANGUAGE SYNTAX
+section above.
+.Pp
+A backslash followed by any character not listed here
+simply prints that character itself.
+.Ss \e
+A backslash at the end of an input line can be used to continue the
+logical input line on the next physical input line, joining the text
+on both lines together as if it were on a single input line.
+.Ss \e
+The escape sequence backslash-space
+.Pq Sq \e\ \&
+is an unpaddable space-sized non-breaking space character; see
+.Sx Whitespace .
+.Ss \e\(dq
+The rest of the input line is treated as
+.Sx Comments .
+.Ss \e%
+Hyphenation allowed at this point of the word; ignored by
+.Xr mandoc 1 .
+.Ss \e&
+Non-printing zero-width character; see
+.Sx Whitespace .
+.Ss \e\(aq
+Acute accent special character; use
+.Sq \e(aa
+instead.
+.Ss \e( Ns Ar cc
+.Sx Special Characters
+with two-letter names, see
+.Xr mandoc_char 7 .
+.Ss \e*[ Ns Ar name ]
+Interpolate the string with the
+.Ar name ;
+see
+.Sx Predefined Strings
+and
+.Sx ds .
+For short names, there are variants
+.No \e* Ns Ar c
+and
+.No \e*( Ns Ar cc .
+.Ss \e-
+Special character
+.Dq mathematical minus sign .
+.Ss \e[ Ns Ar name ]
+.Sx Special Characters
+with names of arbitrary length, see
+.Xr mandoc_char 7 .
+.Ss \e^
+One-twelfth em half-narrow space character, effectively zero-width in
+.Xr mandoc 1 .
+.Ss \e`
+Grave accent special character; use
+.Sq \e(ga
+instead.
+.Ss \e{
+Begin conditional input; see
+.Sx if .
+.Ss \e\(ba
+One-sixth em narrow space character, effectively zero-width in
+.Xr mandoc 1 .
+.Ss \e}
+End conditional input; see
+.Sx if .
+.Ss \e~
+Paddable non-breaking space character.
+.Ss \e0
+Digit width space character.
+.Ss \eA\(aq Ns Ar string Ns \(aq
+Anchor definition; ignored by
+.Xr mandoc 1 .
+.Ss \eB\(aq Ns Ar string Ns \(aq
+Test whether
+.Ar string
+is a numerical expession; ignored by
+.Xr mandoc 1 .
+.Ss \eb\(aq Ns Ar string Ns \(aq
+Bracket building function; ignored by
+.Xr mandoc 1 .
+.Ss \eC\(aq Ns Ar name Ns \(aq
+.Sx Special Characters
+with names of arbitrary length.
+.Ss \ec
+Interrupt text processing to insert requests or macros; ignored by
+.Xr mandoc 1 .
+.Ss \eD\(aq Ns Ar string Ns \(aq
+Draw graphics function; ignored by
+.Xr mandoc 1 .
+.Ss \ed
+Move down by half a line; ignored by
+.Xr mandoc 1 .
+.Ss \ee
+Backslash special character.
+.Ss \eF[ Ns Ar name ]
+Switch font family (groff extension); ignored by
+.Xr mandoc 1 .
+For short names, there are variants
+.No \eF Ns Ar c
+and
+.No \eF( Ns Ar cc .
+.Ss \ef[ Ns Ar name ]
+Switch to the font
+.Ar name ,
+see
+.Sx Text Decoration .
+For short names, there are variants
+.No \ef Ns Ar c
+and
+.No \ef( Ns Ar cc .
+.Ss \eg[ Ns Ar name ]
+Interpolate the format of a number register; ignored by
+.Xr mandoc 1 .
+For short names, there are variants
+.No \eg Ns Ar c
+and
+.No \eg( Ns Ar cc .
+.Ss \eH\(aq Ns Oo +|- Oc Ns Ar number Ns \(aq
+Set the height of the current font; ignored by
+.Xr mandoc 1 .
+.Ss \eh\(aq Ns Ar number Ns \(aq
+Horizontal motion; ignored by
+.Xr mandoc 1 .
+.Ss \ek[ Ns Ar name ]
+Mark horizontal input place in register; ignored by
+.Xr mandoc 1 .
+For short names, there are variants
+.No \ek Ns Ar c
+and
+.No \ek( Ns Ar cc .
+.Ss \eL\(aq Ns Ar number Ns Oo Ar c Oc Ns \(aq
+Vertical line drawing function; ignored by
+.Xr mandoc 1 .
+.Ss \el\(aq Ns Ar number Ns Oo Ar c Oc Ns \(aq
+Horizontal line drawing function; ignored by
+.Xr mandoc 1 .
+.Ss \eM[ Ns Ar name ]
+Set fill (background) color (groff extension); ignored by
+.Xr mandoc 1 .
+For short names, there are variants
+.No \eM Ns Ar c
+and
+.No \eM( Ns Ar cc .
+.Ss \em[ Ns Ar name ]
+Set glyph drawing color (groff extension); ignored by
+.Xr mandoc 1 .
+For short names, there are variants
+.No \em Ns Ar c
+and
+.No \em( Ns Ar cc .
+.Ss \eN\(aq Ns Ar number Ns \(aq
+Character
+.Ar number
+on the current font.
+.Ss \en[ Ns Ar name ]
+Interpolate the number register
+.Ar name .
+For short names, there are variants
+.No \en Ns Ar c
+and
+.No \en( Ns Ar cc .
+.Ss \eo\(aq Ns Ar string Ns \(aq
+Overstrike
+.Ar string ;
+ignored by
+.Xr mandoc 1 .
+.Ss \eR\(aq Ns Ar name Oo +|- Oc Ns Ar number Ns \(aq
+Set number register; ignored by
+.Xr mandoc 1 .
+.Ss \eS\(aq Ns Ar number Ns \(aq
+Slant output; ignored by
+.Xr mandoc 1 .
+.Ss \es\(aq Ns Oo +|- Oc Ns Ar number Ns \(aq
+Change point size; ignored by
+.Xr mandoc 1 .
+Alternative forms
+.No \es Ns Oo +|- Oc Ns Ar n ,
+.No \es Ns Oo +|- Oc Ns \(aq Ns Ar number Ns \(aq ,
+.No \es Ns [ Oo +|- Oc Ns Ar number ] ,
+and
+.No \es Ns Oo +|- Oc Ns [ Ar number Ns ]
+are also parsed and ignored.
+.Ss \et
+Horizontal tab; ignored by
+.Xr mandoc 1 .
+.Ss \eu
+Move up by half a line; ignored by
+.Xr mandoc 1 .
+.Ss \eV[ Ns Ar name ]
+Interpolate an environment variable; ignored by
+.Xr mandoc 1 .
+For short names, there are variants
+.No \eV Ns Ar c
+and
+.No \eV( Ns Ar cc .
+.Ss \ev\(aq Ns Ar number Ns \(aq
+Vertical motion; ignored by
+.Xr mandoc 1 .
+.Ss \ew\(aq Ns Ar string Ns \(aq
+Interpolate the width of the
+.Ar string ;
+ignored by
+.Xr mandoc 1 .
+.Ss \eX\(aq Ns Ar string Ns \(aq
+Output
+.Ar string
+as device control function; ignored in nroff mode and by
+.Xr mandoc 1 .
+.Ss \ex\(aq Ns Ar number Ns \(aq
+Extra line space function; ignored by
+.Xr mandoc 1 .
+.Ss \eY[ Ns Ar name ]
+Output a string as a device control function; ignored in nroff mode and by
+.Xr mandoc 1 .
+For short names, there are variants
+.No \eY Ns Ar c
+and
+.No \eY( Ns Ar cc .
+.Ss \eZ\(aq Ns Ar string Ns \(aq
+Print
+.Ar string
+with zero width and height; ignored by
+.Xr mandoc 1 .
+.Ss \ez
+Output the next character without advancing the cursor position;
+approximated in
+.Xr mandoc 1
+by simply skipping the next character.
 .Sh COMPATIBILITY
-This section documents compatibility between mandoc and other other
+This section documents compatibility between mandoc and other
 .Nm
 implementations, at this time limited to GNU troff
 .Pq Qq groff .
@@ -982,8 +1259,6 @@ In 1989, James Clarke re-implemented troff in C++, naming it groff.
 This
 .Nm
 reference was written by
-.An Kristaps Dzonsons ,
-.Mt kristaps@bsd.lv ;
+.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
 and
-.An Ingo Schwarze ,
-.Mt schwarze@openbsd.org .
+.An Ingo Schwarze Aq Mt schwarze@openbsd.org .
diff --git a/contrib/mdocml/roff.c b/contrib/mdocml/roff.c
index b479cc298cf..42240d21fe6 100644
--- a/contrib/mdocml/roff.c
+++ b/contrib/mdocml/roff.c
@@ -1,7 +1,7 @@
-/*	$Id: roff.c,v 1.172 2011/10/24 21:41:45 schwarze Exp $ */
+/*	$Id: roff.c,v 1.189 2013/12/30 18:44:06 schwarze Exp $ */
 /*
- * Copyright (c) 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010, 2011 Ingo Schwarze 
+ * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons 
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -21,6 +21,7 @@
 
 #include 
 #include 
+#include 
 #include 
 #include 
 
@@ -39,11 +40,14 @@ enum	rofft {
 	ROFF_am,
 	ROFF_ami,
 	ROFF_am1,
+	ROFF_cc,
 	ROFF_de,
 	ROFF_dei,
 	ROFF_de1,
 	ROFF_ds,
 	ROFF_el,
+	ROFF_fam,
+	ROFF_hw,
 	ROFF_hy,
 	ROFF_ie,
 	ROFF_if,
@@ -58,6 +62,8 @@ enum	rofft {
 	ROFF_so,
 	ROFF_ta,
 	ROFF_tr,
+	ROFF_Dd,
+	ROFF_TH,
 	ROFF_TS,
 	ROFF_TE,
 	ROFF_T_,
@@ -70,18 +76,8 @@ enum	rofft {
 };
 
 enum	roffrule {
-	ROFFRULE_ALLOW,
-	ROFFRULE_DENY
-};
-
-/*
- * A single register entity.  If "set" is zero, the value of the
- * register should be the default one, which is per-register.
- * Registers are assumed to be unsigned ints for now.
- */
-struct	reg {
-	int		 set; /* whether set or not */
-	unsigned int	 u; /* unsigned integer */
+	ROFFRULE_DENY,
+	ROFFRULE_ALLOW
 };
 
 /*
@@ -101,12 +97,23 @@ struct	roffkv {
 	struct roffkv	*next; /* next in list */
 };
 
+/*
+ * A single number register as part of a singly-linked list.
+ */
+struct	roffreg {
+	struct roffstr	 key;
+	int		 val;
+	struct roffreg	*next;
+};
+
 struct	roff {
+	enum mparset	 parsetype; /* requested parse type */
 	struct mparse	*parse; /* parse point */
 	struct roffnode	*last; /* leaf of stack */
 	enum roffrule	 rstack[RSTACK_MAX]; /* stack of !`ie' rules */
+	char		 control; /* control character */
 	int		 rstackpos; /* position in rstack */
-	struct reg	 regs[REG__MAX];
+	struct roffreg	*regtab; /* number registers */
 	struct roffkv	*strtab; /* user-defined strings & macros */
 	struct roffkv	*xmbtab; /* multi-byte trans table (`tr') */
 	struct roffstr	*xtab; /* single-byte trans table (`tr') */
@@ -169,6 +176,7 @@ static	enum rofferr	 roff_block(ROFF_ARGS);
 static	enum rofferr	 roff_block_text(ROFF_ARGS);
 static	enum rofferr	 roff_block_sub(ROFF_ARGS);
 static	enum rofferr	 roff_cblock(ROFF_ARGS);
+static	enum rofferr	 roff_cc(ROFF_ARGS);
 static	enum rofferr	 roff_ccond(ROFF_ARGS);
 static	enum rofferr	 roff_cond(ROFF_ARGS);
 static	enum rofferr	 roff_cond_text(ROFF_ARGS);
@@ -176,16 +184,22 @@ static	enum rofferr	 roff_cond_sub(ROFF_ARGS);
 static	enum rofferr	 roff_ds(ROFF_ARGS);
 static	enum roffrule	 roff_evalcond(const char *, int *);
 static	void		 roff_free1(struct roff *);
+static	void		 roff_freereg(struct roffreg *);
 static	void		 roff_freestr(struct roffkv *);
 static	char		*roff_getname(struct roff *, char **, int, int);
+static	int		 roff_getnum(const char *, int *, int *);
+static	int		 roff_getop(const char *, int *, char *);
+static	int		 roff_getregn(const struct roff *,
+				const char *, size_t);
 static	const char	*roff_getstrn(const struct roff *, 
 				const char *, size_t);
+static	enum rofferr	 roff_it(ROFF_ARGS);
 static	enum rofferr	 roff_line_ignore(ROFF_ARGS);
 static	enum rofferr	 roff_nr(ROFF_ARGS);
 static	void		 roff_openeqn(struct roff *, const char *,
 				int, int, const char *);
 static	enum rofft	 roff_parse(struct roff *, const char *, int *);
-static	enum rofferr	 roff_parsetext(char *);
+static	enum rofferr	 roff_parsetext(char **, size_t *, int, int *);
 static	enum rofferr	 roff_res(struct roff *, 
 				char **, size_t *, int, int);
 static	enum rofferr	 roff_rm(ROFF_ARGS);
@@ -195,6 +209,8 @@ static	void		 roff_setstrn(struct roffkv **, const char *,
 				size_t, const char *, size_t, int);
 static	enum rofferr	 roff_so(ROFF_ARGS);
 static	enum rofferr	 roff_tr(ROFF_ARGS);
+static	enum rofferr	 roff_Dd(ROFF_ARGS);
+static	enum rofferr	 roff_TH(ROFF_ARGS);
 static	enum rofferr	 roff_TE(ROFF_ARGS);
 static	enum rofferr	 roff_TS(ROFF_ARGS);
 static	enum rofferr	 roff_EQ(ROFF_ARGS);
@@ -215,16 +231,19 @@ static	struct roffmac	 roffs[ROFF_MAX] = {
 	{ "am", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 	{ "ami", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 	{ "am1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
+	{ "cc", roff_cc, NULL, NULL, 0, NULL },
 	{ "de", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 	{ "dei", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 	{ "de1", roff_block, roff_block_text, roff_block_sub, 0, NULL },
 	{ "ds", roff_ds, NULL, NULL, 0, NULL },
 	{ "el", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
+	{ "fam", roff_line_ignore, NULL, NULL, 0, NULL },
+	{ "hw", roff_line_ignore, NULL, NULL, 0, NULL },
 	{ "hy", roff_line_ignore, NULL, NULL, 0, NULL },
 	{ "ie", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
 	{ "if", roff_cond, roff_cond_text, roff_cond_sub, ROFFMAC_STRUCT, NULL },
 	{ "ig", roff_block, roff_block_text, roff_block_sub, 0, NULL },
-	{ "it", roff_line_ignore, NULL, NULL, 0, NULL },
+	{ "it", roff_it, NULL, NULL, 0, NULL },
 	{ "ne", roff_line_ignore, NULL, NULL, 0, NULL },
 	{ "nh", roff_line_ignore, NULL, NULL, 0, NULL },
 	{ "nr", roff_nr, NULL, NULL, 0, NULL },
@@ -234,6 +253,8 @@ static	struct roffmac	 roffs[ROFF_MAX] = {
 	{ "so", roff_so, NULL, NULL, 0, NULL },
 	{ "ta", roff_line_ignore, NULL, NULL, 0, NULL },
 	{ "tr", roff_tr, NULL, NULL, 0, NULL },
+	{ "Dd", roff_Dd, NULL, NULL, 0, NULL },
+	{ "TH", roff_TH, NULL, NULL, 0, NULL },
 	{ "TS", roff_TS, NULL, NULL, 0, NULL },
 	{ "TE", roff_TE, NULL, NULL, 0, NULL },
 	{ "T&", roff_T_, NULL, NULL, 0, NULL },
@@ -244,6 +265,37 @@ static	struct roffmac	 roffs[ROFF_MAX] = {
 	{ NULL, roff_userdef, NULL, NULL, 0, NULL },
 };
 
+const	char *const __mdoc_reserved[] = {
+	"Ac", "Ad", "An", "Ao", "Ap", "Aq", "Ar", "At",
+	"Bc", "Bd", "Bf", "Bk", "Bl", "Bo", "Bq",
+	"Brc", "Bro", "Brq", "Bsx", "Bt", "Bx",
+	"Cd", "Cm", "Db", "Dc", "Dd", "Dl", "Do", "Dq",
+	"Ds", "Dt", "Dv", "Dx", "D1",
+	"Ec", "Ed", "Ef", "Ek", "El", "Em", "em",
+	"En", "Eo", "Eq", "Er", "Es", "Ev", "Ex",
+	"Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
+	"Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp", "LP",
+	"Me", "Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
+	"Oc", "Oo", "Op", "Os", "Ot", "Ox",
+	"Pa", "Pc", "Pf", "Po", "Pp", "PP", "pp", "Pq",
+	"Qc", "Ql", "Qo", "Qq", "Or", "Rd", "Re", "Rs", "Rv",
+	"Sc", "Sf", "Sh", "SH", "Sm", "So", "Sq",
+	"Ss", "St", "Sx", "Sy",
+	"Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
+	"%A", "%B", "%D", "%I", "%J", "%N", "%O",
+	"%P", "%Q", "%R", "%T", "%U", "%V",
+	NULL
+};
+
+const	char *const __man_reserved[] = {
+	"AT", "B", "BI", "BR", "BT", "DE", "DS", "DT",
+	"EE", "EN", "EQ", "EX", "HF", "HP", "I", "IB", "IP", "IR",
+	"LP", "ME", "MT", "OP", "P", "PD", "PP", "PT",
+	"R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS", "SY",
+	"TE", "TH", "TP", "TQ", "TS", "T&", "UC", "UE", "UR", "YS",
+	NULL
+};
+
 /* Array of injected predefined strings. */
 #define	PREDEFS_MAX	 38
 static	const struct predef predefs[PREDEFS_MAX] = {
@@ -253,6 +305,9 @@ static	const struct predef predefs[PREDEFS_MAX] = {
 /* See roffhash_find() */
 #define	ROFF_HASH(p)	(p[0] - ASCII_LO)
 
+static	int	 roffit_lines;  /* number of lines to delay */
+static	char	*roffit_macro;  /* nil-terminated macro line */
+
 static void
 roffhash_init(void)
 {
@@ -351,13 +406,13 @@ roffnode_push(struct roff *r, enum rofft tok, const char *name,
 static void
 roff_free1(struct roff *r)
 {
-	struct tbl_node	*t;
+	struct tbl_node	*tbl;
 	struct eqn_node	*e;
 	int		 i;
 
-	while (NULL != (t = r->first_tbl)) {
-		r->first_tbl = t->next;
-		tbl_free(t);
+	while (NULL != (tbl = r->first_tbl)) {
+		r->first_tbl = tbl->next;
+		tbl_free(tbl);
 	}
 
 	r->first_tbl = r->last_tbl = r->tbl = NULL;
@@ -377,6 +432,10 @@ roff_free1(struct roff *r)
 
 	r->strtab = r->xmbtab = NULL;
 
+	roff_freereg(r->regtab);
+
+	r->regtab = NULL;
+
 	if (r->xtab)
 		for (i = 0; i < 128; i++)
 			free(r->xtab[i].p);
@@ -392,7 +451,7 @@ roff_reset(struct roff *r)
 
 	roff_free1(r);
 
-	memset(&r->regs, 0, sizeof(struct reg) * REG__MAX);
+	r->control = 0;
 
 	for (i = 0; i < PREDEFS_MAX; i++) 
 		roff_setstr(r, predefs[i].name, predefs[i].str, 0);
@@ -409,12 +468,13 @@ roff_free(struct roff *r)
 
 
 struct roff *
-roff_alloc(struct mparse *parse)
+roff_alloc(enum mparset type, struct mparse *parse)
 {
 	struct roff	*r;
 	int		 i;
 
 	r = mandoc_calloc(1, sizeof(struct roff));
+	r->parsetype = type;
 	r->parse = parse;
 	r->rstackpos = -1;
 	
@@ -427,22 +487,23 @@ roff_alloc(struct mparse *parse)
 }
 
 /*
- * Pre-filter each and every line for reserved words (one beginning with
- * `\*', e.g., `\*(ab').  These must be handled before the actual line
- * is processed. 
- * This also checks the syntax of regular escapes.
+ * In the current line, expand user-defined strings ("\*")
+ * and references to number registers ("\n").
+ * Also check the syntax of other escape sequences.
  */
 static enum rofferr
 roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
 {
-	enum mandoc_esc	 esc;
+	char		 ubuf[12]; /* buffer to print the number */
 	const char	*stesc;	/* start of an escape sequence ('\\') */
 	const char	*stnam;	/* start of the name, after "[(*" */
 	const char	*cp;	/* end of the name, e.g. before ']' */
 	const char	*res;	/* the string to be substituted */
-	int		 i, maxl, expand_count;
-	size_t		 nsz;
-	char		*n;
+	char		*nbuf;	/* new buffer to copy bufp to */
+	size_t		 nsz;	/* size of the new buffer */
+	size_t		 maxl;  /* expected length of the escape name */
+	size_t		 naml;	/* actual length of the escape name */
+	int		 expand_count;	/* to avoid infinite loops */
 
 	expand_count = 0;
 
@@ -452,7 +513,7 @@ again:
 		stesc = cp++;
 
 		/*
-		 * The second character must be an asterisk.
+		 * The second character must be an asterisk or an n.
 		 * If it isn't, skip it anyway:  It is escaped,
 		 * so it can't start another escape sequence.
 		 */
@@ -460,12 +521,16 @@ again:
 		if ('\0' == *cp)
 			return(ROFF_CONT);
 
-		if ('*' != *cp) {
-			res = cp;
-			esc = mandoc_escape(&cp, NULL, NULL);
-			if (ESCAPE_ERROR != esc)
+		switch (*cp) {
+		case ('*'):
+			res = NULL;
+			break;
+		case ('n'):
+			res = ubuf;
+			break;
+		default:
+			if (ESCAPE_ERROR != mandoc_escape(&cp, NULL, NULL))
 				continue;
-			cp = res;
 			mandoc_msg
 				(MANDOCERR_BADESCAPE, r->parse, 
 				 ln, (int)(stesc - *bufp), NULL);
@@ -476,7 +541,7 @@ again:
 
 		/*
 		 * The third character decides the length
-		 * of the name of the string.
+		 * of the name of the string or register.
 		 * Save a pointer to the name.
 		 */
 
@@ -499,7 +564,7 @@ again:
 
 		/* Advance to the end of the name. */
 
-		for (i = 0; 0 == maxl || i < maxl; i++, cp++) {
+		for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) {
 			if ('\0' == *cp) {
 				mandoc_msg
 					(MANDOCERR_BADESCAPE, 
@@ -516,7 +581,11 @@ again:
 		 * undefined, resume searching for escapes.
 		 */
 
-		res = roff_getstrn(r, stnam, (size_t)i);
+		if (NULL == res)
+			res = roff_getstrn(r, stnam, naml);
+		else
+			snprintf(ubuf, sizeof(ubuf), "%d",
+			    roff_getregn(r, stnam, naml));
 
 		if (NULL == res) {
 			mandoc_msg
@@ -530,15 +599,15 @@ again:
 		pos = stesc - *bufp;
 
 		nsz = *szp + strlen(res) + 1;
-		n = mandoc_malloc(nsz);
+		nbuf = mandoc_malloc(nsz);
 
-		strlcpy(n, *bufp, (size_t)(stesc - *bufp + 1));
-		strlcat(n, res, nsz);
-		strlcat(n, cp + (maxl ? 0 : 1), nsz);
+		strlcpy(nbuf, *bufp, (size_t)(stesc - *bufp + 1));
+		strlcat(nbuf, res, nsz);
+		strlcat(nbuf, cp + (maxl ? 0 : 1), nsz);
 
 		free(*bufp);
 
-		*bufp = n;
+		*bufp = nbuf;
 		*szp = nsz;
 
 		if (EXPAND_LIMIT >= ++expand_count)
@@ -552,16 +621,20 @@ again:
 }
 
 /*
- * Process text streams: convert all breakable hyphens into ASCII_HYPH.
+ * Process text streams:
+ * Convert all breakable hyphens into ASCII_HYPH.
+ * Decrement and spring input line trap.
  */
 static enum rofferr
-roff_parsetext(char *p)
+roff_parsetext(char **bufp, size_t *szp, int pos, int *offs)
 {
 	size_t		 sz;
 	const char	*start;
+	char		*p;
+	int		 isz;
 	enum mandoc_esc	 esc;
 
-	start = p;
+	start = p = *bufp + pos;
 
 	while ('\0' != *p) {
 		sz = strcspn(p, "-\\");
@@ -573,8 +646,7 @@ roff_parsetext(char *p)
 		if ('\\' == *p) {
 			/* Skip over escapes. */
 			p++;
-			esc = mandoc_escape
-				((const char **)&p, NULL, NULL);
+			esc = mandoc_escape((const char **)&p, NULL, NULL);
 			if (ESCAPE_ERROR == esc)
 				break;
 			continue;
@@ -589,6 +661,22 @@ roff_parsetext(char *p)
 		p++;
 	}
 
+	/* Spring the input line trap. */
+	if (1 == roffit_lines) {
+		isz = asprintf(&p, "%s\n.%s", *bufp, roffit_macro);
+		if (-1 == isz) {
+			perror(NULL);
+			exit((int)MANDOCLEVEL_SYSERR);
+		}
+		free(*bufp);
+		*bufp = p;
+		*szp = isz + 1;
+		*offs = 0;
+		free(roffit_macro);
+		roffit_lines = 0;
+		return(ROFF_REPARSE);
+	} else if (1 < roffit_lines)
+		--roffit_lines;
 	return(ROFF_CONT);
 }
 
@@ -611,7 +699,7 @@ roff_parseln(struct roff *r, int ln, char **bufp,
 	assert(ROFF_CONT == e);
 
 	ppos = pos;
-	ctl = mandoc_getcontrol(*bufp, &pos);
+	ctl = roff_getcontrol(r, *bufp, &pos);
 
 	/*
 	 * First, if a scope is open and we're not a macro, pass the
@@ -629,19 +717,14 @@ roff_parseln(struct roff *r, int ln, char **bufp,
 		assert(ROFF_IGN == e || ROFF_CONT == e);
 		if (ROFF_CONT != e)
 			return(e);
-		if (r->eqn)
-			return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
-		if (r->tbl)
-			return(tbl_read(r->tbl, ln, *bufp, pos));
-		return(roff_parsetext(*bufp + pos));
-	} else if ( ! ctl) {
-		if (r->eqn)
-			return(eqn_read(&r->eqn, ln, *bufp, pos, offs));
-		if (r->tbl)
-			return(tbl_read(r->tbl, ln, *bufp, pos));
-		return(roff_parsetext(*bufp + pos));
-	} else if (r->eqn)
+	}
+	if (r->eqn)
 		return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
+	if ( ! ctl) {
+		if (r->tbl)
+			return(tbl_read(r->tbl, ln, *bufp, pos));
+		return(roff_parsetext(bufp, szp, pos, offs));
+	}
 
 	/*
 	 * If a scope is open, go to the child handler for that macro,
@@ -778,7 +861,7 @@ roffnode_cleanscope(struct roff *r)
 {
 
 	while (r->last) {
-		if (--r->last->endspan < 0)
+		if (--r->last->endspan != 0)
 			break;
 		roffnode_pop(r);
 	}
@@ -984,57 +1067,45 @@ roff_cond_sub(ROFF_ARGS)
 
 	rr = r->last->rule;
 	roffnode_cleanscope(r);
+	t = roff_parse(r, *bufp, &pos);
 
 	/*
-	 * If the macro is unknown, first check if it contains a closing
-	 * delimiter `\}'.  If it does, close out our scope and return
-	 * the currently-scoped rule (ignore or continue).  Else, drop
-	 * into the currently-scoped rule.
+	 * Fully handle known macros when they are structurally
+	 * required or when the conditional evaluated to true.
 	 */
 
-	if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos))) {
-		ep = &(*bufp)[pos];
-		for ( ; NULL != (ep = strchr(ep, '\\')); ep++) {
-			ep++;
-			if ('}' != *ep)
-				continue;
-
-			/*
-			 * Make the \} go away.
-			 * This is a little haphazard, as it's not quite
-			 * clear how nroff does this.
-			 * If we're at the end of line, then just chop
-			 * off the \} and resize the buffer.
-			 * If we aren't, then conver it to spaces.
-			 */
-
-			if ('\0' == *(ep + 1)) {
-				*--ep = '\0';
-				*szp -= 2;
-			} else
-				*(ep - 1) = *ep = ' ';
-
-			roff_ccond(r, ROFF_ccond, bufp, szp, 
-					ln, pos, pos + 2, offs);
-			break;
-		}
-		return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
+	if ((ROFF_MAX != t) &&
+	    (ROFF_ccond == t || ROFFRULE_ALLOW == rr ||
+	     ROFFMAC_STRUCT & roffs[t].flags)) {
+		assert(roffs[t].proc);
+		return((*roffs[t].proc)(r, t, bufp, szp,
+					ln, ppos, pos, offs));
 	}
 
-	/*
-	 * A denied conditional must evaluate its children if and only
-	 * if they're either structurally required (such as loops and
-	 * conditionals) or a closing macro.
-	 */
+	/* Always check for the closing delimiter `\}'. */
 
-	if (ROFFRULE_DENY == rr)
-		if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
-			if (ROFF_ccond != t)
-				return(ROFF_IGN);
+	ep = &(*bufp)[pos];
+	while (NULL != (ep = strchr(ep, '\\'))) {
+		if ('}' != *(++ep))
+			continue;
 
-	assert(roffs[t].proc);
-	return((*roffs[t].proc)(r, t, bufp, szp, 
-				ln, ppos, pos, offs));
+		/*
+		 * If we're at the end of line, then just chop
+		 * off the \} and resize the buffer.
+		 * If we aren't, then convert it to spaces.
+		 */
+
+		if ('\0' == *(ep + 1)) {
+			*--ep = '\0';
+			*szp -= 2;
+		} else
+			*(ep - 1) = *ep = ' ';
+
+		roff_ccond(r, ROFF_ccond, bufp, szp, 
+				ln, pos, pos + 2, offs);
+		break;
+	}
+	return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
 }
 
 /* ARGSUSED */
@@ -1059,9 +1130,61 @@ roff_cond_text(ROFF_ARGS)
 	return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
 }
 
+static int
+roff_getnum(const char *v, int *pos, int *res)
+{
+	int p, n;
+
+	p = *pos;
+	n = v[p] == '-';
+	if (n)
+		p++;
+
+	for (*res = 0; isdigit((unsigned char)v[p]); p++)
+		*res += 10 * *res + v[p] - '0';
+	if (p == *pos + n)
+		return 0;
+
+	if (n)
+		*res = -*res;
+
+	*pos = p;
+	return 1;
+}
+
+static int
+roff_getop(const char *v, int *pos, char *res)
+{
+	int e;
+
+	*res = v[*pos];
+	e = v[*pos + 1] == '=';
+
+	switch (*res) {
+	case '=':
+		break;
+	case '>':
+		if (e)
+			*res = 'g';
+		break;
+	case '<':
+		if (e)
+			*res = 'l';
+		break;
+	default:
+		return(0);
+	}
+
+	*pos += 1 + e;
+
+	return(*res);
+}
+
 static enum roffrule
 roff_evalcond(const char *v, int *pos)
 {
+	int	 not, lh, rh;
+	char	 op;
 
 	switch (v[*pos]) {
 	case ('n'):
@@ -1074,13 +1197,47 @@ roff_evalcond(const char *v, int *pos)
 	case ('t'):
 		(*pos)++;
 		return(ROFFRULE_DENY);
+	case ('!'):
+		(*pos)++;
+		not = 1;
+		break;
 	default:
+		not = 0;
 		break;
 	}
 
-	while (v[*pos] && ' ' != v[*pos])
-		(*pos)++;
-	return(ROFFRULE_DENY);
+	if (!roff_getnum(v, pos, &lh))
+		return ROFFRULE_DENY;
+	if (!roff_getop(v, pos, &op)) {
+		if (lh < 0)
+			lh = 0;
+		goto out;
+	}
+	if (!roff_getnum(v, pos, &rh))
+		return ROFFRULE_DENY;
+	switch (op) {
+	case 'g':
+		lh = lh >= rh;
+		break;
+	case 'l':
+		lh = lh <= rh;
+		break;
+	case '=':
+		lh = lh == rh;
+		break;
+	case '>':
+		lh = lh > rh;
+		break;
+	case '<':
+		lh = lh < rh;
+		break;
+	default:
+		return ROFFRULE_DENY;
+	}
+out:
+	if (not)
+		lh = !lh;
+	return lh ? ROFFRULE_ALLOW : ROFFRULE_DENY;
 }
 
 /* ARGSUSED */
@@ -1088,9 +1245,6 @@ static enum rofferr
 roff_line_ignore(ROFF_ARGS)
 {
 
-	if (ROFF_it == tok)
-		mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it");
-
 	return(ROFF_IGN);
 }
 
@@ -1098,8 +1252,8 @@ roff_line_ignore(ROFF_ARGS)
 static enum rofferr
 roff_cond(ROFF_ARGS)
 {
-	int		 sv;
-	enum roffrule	 rule;
+
+	roffnode_push(r, tok, NULL, ln, ppos);
 
 	/* 
 	 * An `.el' has no conditional body: it will consume the value
@@ -1109,31 +1263,11 @@ roff_cond(ROFF_ARGS)
 	 * If we're not an `el', however, then evaluate the conditional.
 	 */
 
-	rule = ROFF_el == tok ?
+	r->last->rule = ROFF_el == tok ?
 		(r->rstackpos < 0 ? 
 		 ROFFRULE_DENY : r->rstack[r->rstackpos--]) :
 		roff_evalcond(*bufp, &pos);
 
-	sv = pos;
-	while (' ' == (*bufp)[pos])
-		pos++;
-
-	/*
-	 * Roff is weird.  If we have just white-space after the
-	 * conditional, it's considered the BODY and we exit without
-	 * really doing anything.  Warn about this.  It's probably
-	 * wrong.
-	 */
-
-	if ('\0' == (*bufp)[pos] && sv != pos) {
-		mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
-		return(ROFF_IGN);
-	}
-
-	roffnode_push(r, tok, NULL, ln, ppos);
-
-	r->last->rule = rule;
-
 	/*
 	 * An if-else will put the NEGATION of the current evaluated
 	 * conditional into the stack of rules.
@@ -1156,28 +1290,39 @@ roff_cond(ROFF_ARGS)
 		r->last->rule = ROFFRULE_DENY;
 
 	/*
-	 * Determine scope.  If we're invoked with "\{" trailing the
-	 * conditional, then we're in a multiline scope.  Else our scope
-	 * expires on the next line.
+	 * Determine scope.
+	 * If there is nothing on the line after the conditional,
+	 * not even whitespace, use next-line scope.
 	 */
 
-	r->last->endspan = 1;
+	if ('\0' == (*bufp)[pos]) {
+		r->last->endspan = 2;
+		goto out;
+	}
+
+	while (' ' == (*bufp)[pos])
+		pos++;
+
+	/* An opening brace requests multiline scope. */
 
 	if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
 		r->last->endspan = -1;
 		pos += 2;
+		goto out;
 	} 
 
 	/*
-	 * If there are no arguments on the line, the next-line scope is
-	 * assumed.
+	 * Anything else following the conditional causes
+	 * single-line scope.  Warn if the scope contains
+	 * nothing but trailing whitespace.
 	 */
 
 	if ('\0' == (*bufp)[pos])
-		return(ROFF_IGN);
+		mandoc_msg(MANDOCERR_NOARGS, r->parse, ln, ppos, NULL);
 
-	/* Otherwise re-run the roff parser after recalculating. */
+	r->last->endspan = 1;
 
+out:
 	*offs = pos;
 	return(ROFF_RERUN);
 }
@@ -1213,25 +1358,71 @@ roff_ds(ROFF_ARGS)
 	return(ROFF_IGN);
 }
 
-int
-roff_regisset(const struct roff *r, enum regs reg)
-{
-
-	return(r->regs[(int)reg].set);
-}
-
-unsigned int
-roff_regget(const struct roff *r, enum regs reg)
-{
-
-	return(r->regs[(int)reg].u);
-}
-
 void
-roff_regunset(struct roff *r, enum regs reg)
+roff_setreg(struct roff *r, const char *name, int val, char sign)
 {
+	struct roffreg	*reg;
 
-	r->regs[(int)reg].set = 0;
+	/* Search for an existing register with the same name. */
+	reg = r->regtab;
+
+	while (reg && strcmp(name, reg->key.p))
+		reg = reg->next;
+
+	if (NULL == reg) {
+		/* Create a new register. */
+		reg = mandoc_malloc(sizeof(struct roffreg));
+		reg->key.p = mandoc_strdup(name);
+		reg->key.sz = strlen(name);
+		reg->val = 0;
+		reg->next = r->regtab;
+		r->regtab = reg;
+	}
+
+	if ('+' == sign)
+		reg->val += val;
+	else if ('-' == sign)
+		reg->val -= val;
+	else
+		reg->val = val;
+}
+
+int
+roff_getreg(const struct roff *r, const char *name)
+{
+	struct roffreg	*reg;
+
+	for (reg = r->regtab; reg; reg = reg->next)
+		if (0 == strcmp(name, reg->key.p))
+			return(reg->val);
+
+	return(0);
+}
+
+static int
+roff_getregn(const struct roff *r, const char *name, size_t len)
+{
+	struct roffreg	*reg;
+
+	for (reg = r->regtab; reg; reg = reg->next)
+		if (len == reg->key.sz &&
+		    0 == strncmp(name, reg->key.p, len))
+			return(reg->val);
+
+	return(0);
+}
+
+static void
+roff_freereg(struct roffreg *reg)
+{
+	struct roffreg	*old_reg;
+
+	while (NULL != reg) {
+		free(reg->key.p);
+		old_reg = reg;
+		reg = reg->next;
+		free(old_reg);
+	}
 }
 
 /* ARGSUSED */
@@ -1240,18 +1431,21 @@ roff_nr(ROFF_ARGS)
 {
 	const char	*key;
 	char		*val;
+	size_t		 sz;
 	int		 iv;
+	char		 sign;
 
 	val = *bufp + pos;
 	key = roff_getname(r, &val, ln, pos);
 
-	if (0 == strcmp(key, "nS")) {
-		r->regs[(int)REG_nS].set = 1;
-		if ((iv = mandoc_strntoi(val, strlen(val), 10)) >= 0)
-			r->regs[(int)REG_nS].u = (unsigned)iv;
-		else
-			r->regs[(int)REG_nS].u = 0u;
-	}
+	sign = *val;
+	if ('+' == sign || '-' == sign)
+		val++;
+
+	sz = strspn(val, "0123456789");
+	iv = sz ? mandoc_strntoi(val, sz, 10) : 0;
+
+	roff_setreg(r, key, iv, sign);
 
 	return(ROFF_IGN);
 }
@@ -1272,6 +1466,57 @@ roff_rm(ROFF_ARGS)
 	return(ROFF_IGN);
 }
 
+/* ARGSUSED */
+static enum rofferr
+roff_it(ROFF_ARGS)
+{
+	char		*cp;
+	size_t		 len;
+	int		 iv;
+
+	/* Parse the number of lines. */
+	cp = *bufp + pos;
+	len = strcspn(cp, " \t");
+	cp[len] = '\0';
+	if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) {
+		mandoc_msg(MANDOCERR_NUMERIC, r->parse,
+				ln, ppos, *bufp + 1);
+		return(ROFF_IGN);
+	}
+	cp += len + 1;
+
+	/* Arm the input line trap. */
+	roffit_lines = iv;
+	roffit_macro = mandoc_strdup(cp);
+	return(ROFF_IGN);
+}
+
+/* ARGSUSED */
+static enum rofferr
+roff_Dd(ROFF_ARGS)
+{
+	const char *const	*cp;
+
+	if (MPARSE_MDOC != r->parsetype)
+		for (cp = __mdoc_reserved; *cp; cp++)
+			roff_setstr(r, *cp, NULL, 0);
+
+	return(ROFF_CONT);
+}
+
+/* ARGSUSED */
+static enum rofferr
+roff_TH(ROFF_ARGS)
+{
+	const char *const	*cp;
+
+	if (MPARSE_MDOC != r->parsetype)
+		for (cp = __man_reserved; *cp; cp++)
+			roff_setstr(r, *cp, NULL, 0);
+
+	return(ROFF_CONT);
+}
+
 /* ARGSUSED */
 static enum rofferr
 roff_TE(ROFF_ARGS)
@@ -1352,21 +1597,38 @@ roff_EN(ROFF_ARGS)
 static enum rofferr
 roff_TS(ROFF_ARGS)
 {
-	struct tbl_node	*t;
+	struct tbl_node	*tbl;
 
 	if (r->tbl) {
 		mandoc_msg(MANDOCERR_SCOPEBROKEN, r->parse, ln, ppos, NULL);
 		tbl_end(&r->tbl);
 	}
 
-	t = tbl_alloc(ppos, ln, r->parse);
+	tbl = tbl_alloc(ppos, ln, r->parse);
 
 	if (r->last_tbl)
-		r->last_tbl->next = t;
+		r->last_tbl->next = tbl;
 	else
-		r->first_tbl = r->last_tbl = t;
+		r->first_tbl = r->last_tbl = tbl;
+
+	r->tbl = r->last_tbl = tbl;
+	return(ROFF_IGN);
+}
+
+/* ARGSUSED */
+static enum rofferr
+roff_cc(ROFF_ARGS)
+{
+	const char	*p;
+
+	p = *bufp + pos;
+
+	if ('\0' == *p || '.' == (r->control = *p++))
+		r->control = 0;
+
+	if ('\0' != *p)
+		mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
 
-	r->tbl = r->last_tbl = t;
 	return(ROFF_IGN);
 }
 
@@ -1470,7 +1732,7 @@ roff_userdef(ROFF_ARGS)
 
 	/*
 	 * Collect pointers to macro argument strings
-	 * and null-terminate them.
+	 * and NUL-terminate them.
 	 */
 	cp = *bufp + pos;
 	for (i = 0; i < 9; i++)
@@ -1766,3 +2028,38 @@ roff_strdup(const struct roff *r, const char *p)
 	res[(int)ssz] = '\0';
 	return(res);
 }
+
+/*
+ * Find out whether a line is a macro line or not.  
+ * If it is, adjust the current position and return one; if it isn't,
+ * return zero and don't change the current position.
+ * If the control character has been set with `.cc', then let that grain
+ * precedence.
+ * This is slighly contrary to groff, where using the non-breaking
+ * control character when `cc' has been invoked will cause the
+ * non-breaking macro contents to be printed verbatim.
+ */
+int
+roff_getcontrol(const struct roff *r, const char *cp, int *ppos)
+{
+	int		pos;
+
+	pos = *ppos;
+
+	if (0 != r->control && cp[pos] == r->control)
+		pos++;
+	else if (0 != r->control)
+		return(0);
+	else if ('\\' == cp[pos] && '.' == cp[pos + 1])
+		pos += 2;
+	else if ('.' == cp[pos] || '\'' == cp[pos])
+		pos++;
+	else
+		return(0);
+
+	while (' ' == cp[pos] || '\t' == cp[pos])
+		pos++;
+
+	*ppos = pos;
+	return(1);
+}
diff --git a/contrib/mdocml/st.in b/contrib/mdocml/st.in
index 3ba41dd359d..c52ddab9ba1 100644
--- a/contrib/mdocml/st.in
+++ b/contrib/mdocml/st.in
@@ -1,4 +1,4 @@
-/*	$Id: st.in,v 1.19 2012/02/26 21:47:09 schwarze Exp $ */
+/*	$Id: st.in,v 1.22 2013/12/25 14:09:32 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010 Kristaps Dzonsons 
  *
@@ -35,16 +35,19 @@ LINE("-p1003.1-2001",	"IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1-2004",	"IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1-2008",	"IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1",	"IEEE Std 1003.1 (\\(lqPOSIX.1\\(rq)")
-LINE("-p1003.1b",	"IEEE Std 1003.1b (\\(lqPOSIX.1\\(rq)")
-LINE("-p1003.1b-93",	"IEEE Std 1003.1b-1993 (\\(lqPOSIX.1\\(rq)")
-LINE("-p1003.1c-95",	"IEEE Std 1003.1c-1995 (\\(lqPOSIX.1\\(rq)")
-LINE("-p1003.1g-2000",	"IEEE Std 1003.1g-2000 (\\(lqPOSIX.1\\(rq)")
-LINE("-p1003.1i-95",	"IEEE Std 1003.1i-1995 (\\(lqPOSIX.1\\(rq)")
+LINE("-p1003.1b",	"IEEE Std 1003.1b (\\(lqPOSIX.1b\\(rq)")
+LINE("-p1003.1b-93",	"IEEE Std 1003.1b-1993 (\\(lqPOSIX.1b\\(rq)")
+LINE("-p1003.1c-95",	"IEEE Std 1003.1c-1995 (\\(lqPOSIX.1c\\(rq)")
+LINE("-p1003.1d-99",	"IEEE Std 1003.1d-1999 (\\(lqPOSIX.1d\\(rq)")
+LINE("-p1003.1g-2000",	"IEEE Std 1003.1g-2000 (\\(lqPOSIX.1g\\(rq)")
+LINE("-p1003.1i-95",	"IEEE Std 1003.1i-1995 (\\(lqPOSIX.1i\\(rq)")
+LINE("-p1003.1j-2000",	"IEEE Std 1003.1j-2000 (\\(lqPOSIX.1j\\(rq)")
+LINE("-p1003.1q-2000",	"IEEE Std 1003.1q-2000 (\\(lqPOSIX.1q\\(rq)")
+LINE("-p1003.2",	"IEEE Std 1003.2 (\\(lqPOSIX.2\\(rq)")
 LINE("-p1003.2-92",	"IEEE Std 1003.2-1992 (\\(lqPOSIX.2\\(rq)")
 LINE("-p1003.2a-92",	"IEEE Std 1003.2a-1992 (\\(lqPOSIX.2\\(rq)")
-LINE("-p1387.2-95",	"IEEE Std 1387.2-1995 (\\(lqPOSIX.7.2\\(rq)")
-LINE("-p1003.2",	"IEEE Std 1003.2 (\\(lqPOSIX.2\\(rq)")
 LINE("-p1387.2",	"IEEE Std 1387.2 (\\(lqPOSIX.7.2\\(rq)")
+LINE("-p1387.2-95",	"IEEE Std 1387.2-1995 (\\(lqPOSIX.7.2\\(rq)")
 LINE("-isoC",		"ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)")
 LINE("-isoC-90",	"ISO/IEC 9899:1990 (\\(lqISO\\~C90\\(rq)")
 LINE("-isoC-amd1",	"ISO/IEC 9899/AMD1:1995 (\\(lqISO\\~C90, Amendment 1\\(rq)")
@@ -68,11 +71,12 @@ LINE("-xpg4.2",		"X/Open Portability Guide Issue\\~4, Version\\~2 (\\(lqXPG4.2\\
 LINE("-xpg4.3",		"X/Open Portability Guide Issue\\~4, Version\\~3 (\\(lqXPG4.3\\(rq)")
 LINE("-xbd5",		"X/Open Base Definitions Issue\\~5 (\\(lqXBD5\\(rq)")
 LINE("-xcu5",		"X/Open Commands and Utilities Issue\\~5 (\\(lqXCU5\\(rq)")
+LINE("-xsh4.2",		"X/Open System Interfaces and Headers Issue\\~4, Version\\~2 (\\(lqXSH4.2\\(rq)")
 LINE("-xsh5",		"X/Open System Interfaces and Headers Issue\\~5 (\\(lqXSH5\\(rq)")
 LINE("-xns5",		"X/Open Networking Services Issue\\~5 (\\(lqXNS5\\(rq)")
 LINE("-xns5.2",		"X/Open Networking Services Issue\\~5.2 (\\(lqXNS5.2\\(rq)")
 LINE("-xns5.2d2.0",	"X/Open Networking Services Issue\\~5.2 Draft\\~2.0 (\\(lqXNS5.2D2.0\\(rq)")
 LINE("-xcurses4.2",	"X/Open Curses Issue\\~4, Version\\~2 (\\(lqXCURSES4.2\\(rq)")
-LINE("-susv2",		"Version\\~2 of the Single UNIX Specification")
-LINE("-susv3",		"Version\\~3 of the Single UNIX Specification")
+LINE("-susv2",		"Version\\~2 of the Single UNIX Specification (\\(lqSUSv2\\(rq)")
+LINE("-susv3",		"Version\\~3 of the Single UNIX Specification (\\(lqSUSv3\\(rq)")
 LINE("-svid4",		"System\\~V Interface Definition, Fourth Edition (\\(lqSVID4\\(rq)")
diff --git a/contrib/mdocml/tbl.7 b/contrib/mdocml/tbl.7
index ea3d2ba3a8b..d4c829bfddf 100644
--- a/contrib/mdocml/tbl.7
+++ b/contrib/mdocml/tbl.7
@@ -1,4 +1,4 @@
-.\"	$Id: tbl.7,v 1.16 2011/09/03 00:29:21 kristaps Exp $
+.\"	$Id: tbl.7,v 1.18 2013/09/16 22:39:19 schwarze Exp $
 .\"
 .\" Copyright (c) 2010, 2011 Kristaps Dzonsons 
 .\"
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: September 3 2011 $
+.Dd $Mdocdate: September 16 2013 $
 .Dt TBL 7
 .Os
 .Sh NAME
@@ -69,13 +69,6 @@ c5 c5 c5.
 4:5:6
 .TE
 .Ed
-.Pp
-The
-.Nm
-implementation in
-.Xr mandoc 1
-is
-.Ud
 .Sh TABLE STRUCTURE
 Tables are enclosed by the
 .Sq TS
@@ -344,5 +337,4 @@ utility.
 This
 .Nm
 reference was written by
-.An Kristaps Dzonsons ,
-.Mt kristaps@bsd.lv .
+.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
diff --git a/contrib/mdocml/tbl.c b/contrib/mdocml/tbl.c
index b3d651be07a..b244ac80acf 100644
--- a/contrib/mdocml/tbl.c
+++ b/contrib/mdocml/tbl.c
@@ -1,4 +1,4 @@
-/*	$Id: tbl.c,v 1.26 2011/07/25 15:37:00 kristaps Exp $ */
+/*	$Id: tbl.c,v 1.27 2013/05/31 22:08:09 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2011 Ingo Schwarze 
@@ -72,21 +72,21 @@ tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs)
 struct tbl_node *
 tbl_alloc(int pos, int line, struct mparse *parse)
 {
-	struct tbl_node	*p;
+	struct tbl_node	*tbl;
 
-	p = mandoc_calloc(1, sizeof(struct tbl_node));
-	p->line = line;
-	p->pos = pos;
-	p->parse = parse;
-	p->part = TBL_PART_OPTS;
-	p->opts.tab = '\t';
-	p->opts.linesize = 12;
-	p->opts.decimal = '.';
-	return(p);
+	tbl = mandoc_calloc(1, sizeof(struct tbl_node));
+	tbl->line = line;
+	tbl->pos = pos;
+	tbl->parse = parse;
+	tbl->part = TBL_PART_OPTS;
+	tbl->opts.tab = '\t';
+	tbl->opts.linesize = 12;
+	tbl->opts.decimal = '.';
+	return(tbl);
 }
 
 void
-tbl_free(struct tbl_node *p)
+tbl_free(struct tbl_node *tbl)
 {
 	struct tbl_row	*rp;
 	struct tbl_cell	*cp;
@@ -94,8 +94,8 @@ tbl_free(struct tbl_node *p)
 	struct tbl_dat	*dp;
 	struct tbl_head	*hp;
 
-	while (NULL != (rp = p->first_row)) {
-		p->first_row = rp->next;
+	while (NULL != (rp = tbl->first_row)) {
+		tbl->first_row = rp->next;
 		while (rp->first) {
 			cp = rp->first;
 			rp->first = cp->next;
@@ -104,8 +104,8 @@ tbl_free(struct tbl_node *p)
 		free(rp);
 	}
 
-	while (NULL != (sp = p->first_span)) {
-		p->first_span = sp->next;
+	while (NULL != (sp = tbl->first_span)) {
+		tbl->first_span = sp->next;
 		while (sp->first) {
 			dp = sp->first;
 			sp->first = dp->next;
@@ -116,12 +116,12 @@ tbl_free(struct tbl_node *p)
 		free(sp);
 	}
 
-	while (NULL != (hp = p->first_head)) {
-		p->first_head = hp->next;
+	while (NULL != (hp = tbl->first_head)) {
+		tbl->first_head = hp->next;
 		free(hp);
 	}
 
-	free(p);
+	free(tbl);
 }
 
 void
diff --git a/contrib/mdocml/tbl_data.c b/contrib/mdocml/tbl_data.c
index 129695d8bb9..7413aa2d83d 100644
--- a/contrib/mdocml/tbl_data.c
+++ b/contrib/mdocml/tbl_data.c
@@ -1,4 +1,4 @@
-/*	$Id: tbl_data.c,v 1.24 2011/03/20 16:02:05 kristaps Exp $ */
+/*	$Id: tbl_data.c,v 1.27 2013/06/01 04:56:50 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2011 Ingo Schwarze 
@@ -49,13 +49,11 @@ data(struct tbl_node *tbl, struct tbl_span *dp,
 		cp = dp->layout->first;
 
 	/* 
-	 * Skip over spanners and vertical lines to data formats, since
+	 * Skip over spanners, since
 	 * we want to match data with data layout cells in the header.
 	 */
 
-	while (cp && (TBL_CELL_VERT == cp->pos || 
-				TBL_CELL_DVERT == cp->pos ||
-				TBL_CELL_SPAN == cp->pos))
+	while (cp && TBL_CELL_SPAN == cp->pos)
 		cp = cp->next;
 
 	/*
@@ -104,7 +102,7 @@ data(struct tbl_node *tbl, struct tbl_span *dp,
 
 	if (*pos - sv == 2 && 'T' == p[sv] && '{' == p[sv + 1]) {
 		tbl->part = TBL_PART_CDATA;
-		return(0);
+		return(1);
 	}
 
 	assert(*pos - sv >= 0);
@@ -187,7 +185,7 @@ newspan(struct tbl_node *tbl, int line, struct tbl_row *rp)
 
 	dp = mandoc_calloc(1, sizeof(struct tbl_span));
 	dp->line = line;
-	dp->tbl = &tbl->opts;
+	dp->opts = &tbl->opts;
 	dp->layout = rp;
 	dp->head = tbl->first_head;
 
diff --git a/contrib/mdocml/tbl_html.c b/contrib/mdocml/tbl_html.c
index 8e7dc05de0d..6b8ced716b1 100644
--- a/contrib/mdocml/tbl_html.c
+++ b/contrib/mdocml/tbl_html.c
@@ -1,4 +1,4 @@
-/*	$Id: tbl_html.c,v 1.9 2011/09/18 14:14:15 schwarze Exp $ */
+/*	$Id: tbl_html.c,v 1.10 2012/05/27 17:54:54 schwarze Exp $ */
 /*
  * Copyright (c) 2011 Kristaps Dzonsons 
  *
@@ -119,20 +119,12 @@ print_tbl(struct html *h, const struct tbl_span *sp)
 			print_stagq(h, tt);
 			print_otag(h, TAG_TD, 0, NULL);
 
-			switch (hp->pos) {
-			case (TBL_HEAD_VERT):
-				/* FALLTHROUGH */
-			case (TBL_HEAD_DVERT):
-				continue;
-			case (TBL_HEAD_DATA):
-				if (NULL == dp)
-					break;
-				if (TBL_CELL_DOWN != dp->layout->pos)
-					if (dp->string)
-						print_text(h, dp->string);
-				dp = dp->next;
+			if (NULL == dp)
 				break;
-			}
+			if (TBL_CELL_DOWN != dp->layout->pos)
+				if (dp->string)
+					print_text(h, dp->string);
+			dp = dp->next;
 		}
 		break;
 	}
diff --git a/contrib/mdocml/tbl_layout.c b/contrib/mdocml/tbl_layout.c
index 7601f146cad..6cce977fa2e 100644
--- a/contrib/mdocml/tbl_layout.c
+++ b/contrib/mdocml/tbl_layout.c
@@ -1,6 +1,7 @@
-/*	$Id: tbl_layout.c,v 1.22 2011/09/18 14:14:15 schwarze Exp $ */
+/*	$Id: tbl_layout.c,v 1.23 2012/05/27 17:54:54 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2012 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -51,8 +52,7 @@ static	const struct tbl_phrase keys[KEYS_MAX] = {
 	{ '^',		 TBL_CELL_DOWN },
 	{ '-',		 TBL_CELL_HORIZ },
 	{ '_',		 TBL_CELL_HORIZ },
-	{ '=',		 TBL_CELL_DHORIZ },
-	{ '|',		 TBL_CELL_VERT }
+	{ '=',		 TBL_CELL_DHORIZ }
 };
 
 static	int		 mods(struct tbl_node *, struct tbl_cell *, 
@@ -60,10 +60,8 @@ static	int		 mods(struct tbl_node *, struct tbl_cell *,
 static	int		 cell(struct tbl_node *, struct tbl_row *, 
 				int, const char *, int *);
 static	void		 row(struct tbl_node *, int, const char *, int *);
-static	struct tbl_cell *cell_alloc(struct tbl_node *, 
-				struct tbl_row *, enum tbl_cellt);
-static	void		 head_adjust(const struct tbl_cell *, 
-				struct tbl_head *);
+static	struct tbl_cell *cell_alloc(struct tbl_node *, struct tbl_row *,
+				enum tbl_cellt, int vert);
 
 static int
 mods(struct tbl_node *tbl, struct tbl_cell *cp, 
@@ -80,10 +78,6 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
 	case (TBL_CELL_HORIZ):
 		/* FALLTHROUGH */
 	case (TBL_CELL_DHORIZ):
-		/* FALLTHROUGH */
-	case (TBL_CELL_VERT):
-		/* FALLTHROUGH */
-	case (TBL_CELL_DVERT):
 		return(1);
 	default:
 		break;
@@ -214,10 +208,17 @@ static int
 cell(struct tbl_node *tbl, struct tbl_row *rp, 
 		int ln, const char *p, int *pos)
 {
-	int		 i;
+	int		 vert, i;
 	enum tbl_cellt	 c;
 
-	/* Parse the column position (`r', `R', `|', ...). */
+	/* Handle vertical lines. */
+
+	for (vert = 0; '|' == p[*pos]; ++*pos)
+		vert++;
+	while (' ' == p[*pos])
+		(*pos)++;
+
+	/* Parse the column position (`c', `l', `r', ...). */
 
 	for (i = 0; i < KEYS_MAX; i++)
 		if (tolower((unsigned char)p[*pos]) == keys[i].name)
@@ -246,8 +247,6 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
 			return(0);
 		} else if (rp->last)
 			switch (rp->last->pos) {
-			case (TBL_CELL_VERT):
-			case (TBL_CELL_DVERT):
 			case (TBL_CELL_HORIZ):
 			case (TBL_CELL_DHORIZ):
 				mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
@@ -270,25 +269,16 @@ cell(struct tbl_node *tbl, struct tbl_row *rp,
 
 	(*pos)++;
 
-	/* Extra check for the double-vertical. */
-
-	if (TBL_CELL_VERT == c && '|' == p[*pos]) {
-		(*pos)++;
-		c = TBL_CELL_DVERT;
-	} 
-	
 	/* Disallow adjacent spacers. */
 
-	if (rp->last && (TBL_CELL_VERT == c || TBL_CELL_DVERT == c) &&
-			(TBL_CELL_VERT == rp->last->pos || 
-			 TBL_CELL_DVERT == rp->last->pos)) {
+	if (vert > 2) {
 		mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
 		return(0);
 	}
 
 	/* Allocate cell then parse its modifiers. */
 
-	return(mods(tbl, cell_alloc(tbl, rp, c), ln, p, pos));
+	return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos));
 }
 
 
@@ -308,11 +298,11 @@ row:	/*
 	 */
 
 	rp = mandoc_calloc(1, sizeof(struct tbl_row));
-	if (tbl->last_row) {
+	if (tbl->last_row)
 		tbl->last_row->next = rp;
-		tbl->last_row = rp;
-	} else
-		tbl->last_row = tbl->first_row = rp;
+	else
+		tbl->first_row = rp;
+	tbl->last_row = rp;
 
 cell:
 	while (isspace((unsigned char)p[*pos]))
@@ -357,7 +347,8 @@ tbl_layout(struct tbl_node *tbl, int ln, const char *p)
 }
 
 static struct tbl_cell *
-cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
+cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos,
+		int vert)
 {
 	struct tbl_cell	*p, *pp;
 	struct tbl_head	*h, *hp;
@@ -365,108 +356,35 @@ cell_alloc(struct tbl_node *tbl, struct tbl_row *rp, enum tbl_cellt pos)
 	p = mandoc_calloc(1, sizeof(struct tbl_cell));
 
 	if (NULL != (pp = rp->last)) {
-		rp->last->next = p;
-		rp->last = p;
-	} else
-		rp->last = rp->first = p;
+		pp->next = p;
+		h = pp->head->next;
+	} else {
+		rp->first = p;
+		h = tbl->first_head;
+	}
+	rp->last = p;
 
 	p->pos = pos;
+	p->vert = vert;
 
-	/*
-	 * This is a little bit complicated.  Here we determine the
-	 * header the corresponds to a cell.  We add headers dynamically
-	 * when need be or re-use them, otherwise.  As an example, given
-	 * the following:
-	 *
-	 * 	1  c || l 
-	 * 	2  | c | l
-	 * 	3  l l
-	 * 	3  || c | l |.
-	 *
-	 * We first add the new headers (as there are none) in (1); then
-	 * in (2) we insert the first spanner (as it doesn't match up
-	 * with the header); then we re-use the prior data headers,
-	 * skipping over the spanners; then we re-use everything and add
-	 * a last spanner.  Note that VERT headers are made into DVERT
-	 * ones.
-	 */
-
-	h = pp ? pp->head->next : tbl->first_head;
+	/* Re-use header. */
 
 	if (h) {
-		/* Re-use data header. */
-		if (TBL_HEAD_DATA == h->pos && 
-				(TBL_CELL_VERT != p->pos &&
-				 TBL_CELL_DVERT != p->pos)) {
-			p->head = h;
-			return(p);
-		}
-
-		/* Re-use spanner header. */
-		if (TBL_HEAD_DATA != h->pos && 
-				(TBL_CELL_VERT == p->pos ||
-				 TBL_CELL_DVERT == p->pos)) {
-			head_adjust(p, h);
-			p->head = h;
-			return(p);
-		}
-
-		/* Right-shift headers with a new spanner. */
-		if (TBL_HEAD_DATA == h->pos && 
-				(TBL_CELL_VERT == p->pos ||
-				 TBL_CELL_DVERT == p->pos)) {
-			hp = mandoc_calloc(1, sizeof(struct tbl_head));
-			hp->ident = tbl->opts.cols++;
-			hp->prev = h->prev;
-			if (h->prev)
-				h->prev->next = hp;
-			if (h == tbl->first_head)
-				tbl->first_head = hp;
-			h->prev = hp;
-			hp->next = h;
-			head_adjust(p, hp);
-			p->head = hp;
-			return(p);
-		}
-
-		if (NULL != (h = h->next)) {
-			head_adjust(p, h);
-			p->head = h;
-			return(p);
-		}
-
-		/* Fall through to default case... */
+		p->head = h;
+		return(p);
 	}
 
 	hp = mandoc_calloc(1, sizeof(struct tbl_head));
 	hp->ident = tbl->opts.cols++;
+	hp->vert = vert;
 
 	if (tbl->last_head) {
 		hp->prev = tbl->last_head;
 		tbl->last_head->next = hp;
-		tbl->last_head = hp;
 	} else
-		tbl->last_head = tbl->first_head = hp;
+		tbl->first_head = hp;
+	tbl->last_head = hp;
 
-	head_adjust(p, hp);
 	p->head = hp;
 	return(p);
 }
-
-static void
-head_adjust(const struct tbl_cell *cellp, struct tbl_head *head)
-{
-	if (TBL_CELL_VERT != cellp->pos &&
-			TBL_CELL_DVERT != cellp->pos) {
-		head->pos = TBL_HEAD_DATA;
-		return;
-	}
-
-	if (TBL_CELL_VERT == cellp->pos)
-		if (TBL_HEAD_DVERT != head->pos)
-			head->pos = TBL_HEAD_VERT;
-
-	if (TBL_CELL_DVERT == cellp->pos)
-		head->pos = TBL_HEAD_DVERT;
-}
-
diff --git a/contrib/mdocml/tbl_term.c b/contrib/mdocml/tbl_term.c
index f1928f02cb2..e8411ffecea 100644
--- a/contrib/mdocml/tbl_term.c
+++ b/contrib/mdocml/tbl_term.c
@@ -1,7 +1,7 @@
-/*	$Id: tbl_term.c,v 1.21 2011/09/20 23:05:49 schwarze Exp $ */
+/*	$Id: tbl_term.c,v 1.25 2013/05/31 21:37:17 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2011 Kristaps Dzonsons 
- * Copyright (c) 2011 Ingo Schwarze 
+ * Copyright (c) 2011, 2012 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -31,14 +31,14 @@
 static	size_t	term_tbl_len(size_t, void *);
 static	size_t	term_tbl_strlen(const char *, void *);
 static	void	tbl_char(struct termp *, char, size_t);
-static	void	tbl_data(struct termp *, const struct tbl *,
+static	void	tbl_data(struct termp *, const struct tbl_opts *,
 			const struct tbl_dat *, 
 			const struct roffcol *);
 static	size_t	tbl_rulewidth(struct termp *, const struct tbl_head *);
 static	void	tbl_hframe(struct termp *, const struct tbl_span *, int);
 static	void	tbl_literal(struct termp *, const struct tbl_dat *, 
 			const struct roffcol *);
-static	void	tbl_number(struct termp *, const struct tbl *, 
+static	void	tbl_number(struct termp *, const struct tbl_opts *, 
 			const struct tbl_dat *, 
 			const struct roffcol *);
 static	void	tbl_hrule(struct termp *, const struct tbl_span *);
@@ -96,16 +96,16 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
 	/* Horizontal frame at the start of boxed tables. */
 
 	if (TBL_SPAN_FIRST & sp->flags) {
-		if (TBL_OPT_DBOX & sp->tbl->opts)
+		if (TBL_OPT_DBOX & sp->opts->opts)
 			tbl_hframe(tp, sp, 1);
-		if (TBL_OPT_DBOX & sp->tbl->opts ||
-		    TBL_OPT_BOX  & sp->tbl->opts)
+		if (TBL_OPT_DBOX & sp->opts->opts ||
+		    TBL_OPT_BOX  & sp->opts->opts)
 			tbl_hframe(tp, sp, 0);
 	}
 
 	/* Vertical frame at the start of each row. */
 
-	if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
+	if (TBL_OPT_BOX & sp->opts->opts || TBL_OPT_DBOX & sp->opts->opts)
 		term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
 			TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
 
@@ -126,49 +126,23 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
 		dp = sp->first;
 		spans = 0;
 		for (hp = sp->head; hp; hp = hp->next) {
+
 			/* 
 			 * If the current data header is invoked during
 			 * a spanner ("spans" > 0), don't emit anything
 			 * at all.
 			 */
-			switch (hp->pos) {
-			case (TBL_HEAD_VERT):
-				/* FALLTHROUGH */
-			case (TBL_HEAD_DVERT):
-				if (spans <= 0)
-					tbl_vrule(tp, hp);
-				continue;
-			case (TBL_HEAD_DATA):
-				break;
-			}
 
 			if (--spans >= 0)
 				continue;
 
-			/*
-			 * All cells get a leading blank, except the
-			 * first one and those after double rulers.
-			 */
+			/* Separate columns. */
 
-			if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
-				tbl_char(tp, ASCII_NBRSP, 1);
+			if (NULL != hp->prev)
+				tbl_vrule(tp, hp);
 
 			col = &tp->tbl.cols[hp->ident];
-			tbl_data(tp, sp->tbl, dp, col);
-
-			/* No trailing blanks. */
-
-			if (NULL == hp->next)
-				break;
-
-			/*
-			 * Add another blank between cells,
-			 * or two when there is no vertical ruler.
-			 */
-
-			tbl_char(tp, ASCII_NBRSP,
-			    TBL_HEAD_VERT  == hp->next->pos ||
-			    TBL_HEAD_DVERT == hp->next->pos ? 1 : 2);
+			tbl_data(tp, sp->opts, dp, col);
 
 			/* 
 			 * Go to the next data cell and assign the
@@ -185,7 +159,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
 
 	/* Vertical frame at the end of each row. */
 
-	if (TBL_OPT_BOX & sp->tbl->opts || TBL_OPT_DBOX & sp->tbl->opts)
+	if (TBL_OPT_BOX & sp->opts->opts || TBL_OPT_DBOX & sp->opts->opts)
 		term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
 			TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
 	term_flushln(tp);
@@ -196,11 +170,15 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
 	 */
 
 	if (TBL_SPAN_LAST & sp->flags) {
-		if (TBL_OPT_DBOX & sp->tbl->opts ||
-		    TBL_OPT_BOX  & sp->tbl->opts)
+		if (TBL_OPT_DBOX & sp->opts->opts ||
+		    TBL_OPT_BOX  & sp->opts->opts) {
 			tbl_hframe(tp, sp, 0);
-		if (TBL_OPT_DBOX & sp->tbl->opts)
+			tp->skipvsp = 1;
+		}
+		if (TBL_OPT_DBOX & sp->opts->opts) {
 			tbl_hframe(tp, sp, 1);
+			tp->skipvsp = 2;
+		}
 		assert(tp->tbl.cols);
 		free(tp->tbl.cols);
 		tp->tbl.cols = NULL;
@@ -222,17 +200,14 @@ tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
 	size_t		 width;
 
 	width = tp->tbl.cols[hp->ident].width;
-	if (TBL_HEAD_DATA == hp->pos) {
-		/* Account for leading blanks. */
-		if (hp->prev && TBL_HEAD_DVERT != hp->prev->pos)
-			width++;
-		/* Account for trailing blanks. */
-		width++;
-		if (hp->next &&
-		    TBL_HEAD_VERT  != hp->next->pos &&
-		    TBL_HEAD_DVERT != hp->next->pos)
-			width++;
-	}
+
+	/* Account for leading blanks. */
+	if (hp->prev)
+		width += 2 - hp->vert;
+
+	/* Account for trailing blank. */
+	width++;
+
 	return(width);
 }
 
@@ -250,10 +225,11 @@ tbl_hrule(struct termp *tp, const struct tbl_span *sp)
 	if (TBL_SPAN_DHORIZ == sp->pos)
 		c = '=';
 
-	for (hp = sp->head; hp; hp = hp->next)
-		tbl_char(tp,
-		    TBL_HEAD_DATA == hp->pos ? c : '+',
-		    tbl_rulewidth(tp, hp));
+	for (hp = sp->head; hp; hp = hp->next) {
+		if (hp->prev && hp->vert)
+			tbl_char(tp, '+', hp->vert);
+		tbl_char(tp, c, tbl_rulewidth(tp, hp));
+	}
 }
 
 /*
@@ -268,16 +244,17 @@ tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
 	const struct tbl_head *hp;
 
 	term_word(tp, "+");
-	for (hp = sp->head; hp; hp = hp->next)
-		tbl_char(tp,
-		    outer || TBL_HEAD_DATA == hp->pos ? '-' : '+',
-		    tbl_rulewidth(tp, hp));
+	for (hp = sp->head; hp; hp = hp->next) {
+		if (hp->prev && hp->vert)
+			tbl_char(tp, (outer ? '-' : '+'), hp->vert);
+		tbl_char(tp, '-', tbl_rulewidth(tp, hp));
+	}
 	term_word(tp, "+");
 	term_flushln(tp);
 }
 
 static void
-tbl_data(struct termp *tp, const struct tbl *tbl,
+tbl_data(struct termp *tp, const struct tbl_opts *opts,
 		const struct tbl_dat *dp, 
 		const struct roffcol *col)
 {
@@ -323,7 +300,7 @@ tbl_data(struct termp *tp, const struct tbl *tbl,
 		tbl_literal(tp, dp, col);
 		break;
 	case (TBL_CELL_NUMBER):
-		tbl_number(tp, tbl, dp, col);
+		tbl_number(tp, opts, dp, col);
 		break;
 	case (TBL_CELL_DOWN):
 		tbl_char(tp, ASCII_NBRSP, col->width);
@@ -338,16 +315,11 @@ static void
 tbl_vrule(struct termp *tp, const struct tbl_head *hp)
 {
 
-	switch (hp->pos) {
-	case (TBL_HEAD_VERT):
-		term_word(tp, "|");
-		break;
-	case (TBL_HEAD_DVERT):
-		term_word(tp, "||");
-		break;
-	default:
-		break;
-	}
+	tbl_char(tp, ASCII_NBRSP, 1);
+	if (0 < hp->vert)
+		tbl_char(tp, '|', hp->vert);
+	if (2 > hp->vert)
+		tbl_char(tp, ASCII_NBRSP, 2 - hp->vert);
 }
 
 static void
@@ -369,11 +341,19 @@ static void
 tbl_literal(struct termp *tp, const struct tbl_dat *dp, 
 		const struct roffcol *col)
 {
-	size_t		 len, padl, padr;
+	struct tbl_head		*hp;
+	size_t			 width, len, padl, padr;
+	int			 spans;
 
 	assert(dp->string);
 	len = term_strlen(tp, dp->string);
-	padr = col->width > len ? col->width - len : 0;
+
+	hp = dp->layout->head->next;
+	width = col->width;
+	for (spans = dp->spans; spans--; hp = hp->next)
+		width += tp->tbl.cols[hp->ident].width + 3;
+
+	padr = width > len ? width - len : 0;
 	padl = 0;
 
 	switch (dp->layout->pos) {
@@ -401,7 +381,7 @@ tbl_literal(struct termp *tp, const struct tbl_dat *dp,
 }
 
 static void
-tbl_number(struct termp *tp, const struct tbl *tbl,
+tbl_number(struct termp *tp, const struct tbl_opts *opts,
 		const struct tbl_dat *dp,
 		const struct roffcol *col)
 {
@@ -419,12 +399,12 @@ tbl_number(struct termp *tp, const struct tbl *tbl,
 
 	sz = term_strlen(tp, dp->string);
 
-	buf[0] = tbl->decimal;
+	buf[0] = opts->decimal;
 	buf[1] = '\0';
 
 	psz = term_strlen(tp, buf);
 
-	if (NULL != (cp = strrchr(dp->string, tbl->decimal))) {
+	if (NULL != (cp = strrchr(dp->string, opts->decimal))) {
 		buf[1] = '\0';
 		for (ssz = 0, i = 0; cp != &dp->string[i]; i++) {
 			buf[0] = dp->string[i];
diff --git a/contrib/mdocml/term.c b/contrib/mdocml/term.c
index 4ca15ed6fa5..e7b95578752 100644
--- a/contrib/mdocml/term.c
+++ b/contrib/mdocml/term.c
@@ -1,7 +1,7 @@
-/*	$Id: term.c,v 1.201 2011/09/21 09:57:13 schwarze Exp $ */
+/*	$Id: term.c,v 1.214 2013/12/25 00:39:31 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2010, 2011 Ingo Schwarze 
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -33,7 +33,8 @@
 #include "term.h"
 #include "main.h"
 
-static	void		 adjbuf(struct termp *p, int);
+static	size_t		 cond_width(const struct termp *, int, int *);
+static	void		 adjbuf(struct termp *p, size_t);
 static	void		 bufferc(struct termp *, char);
 static	void		 encode(struct termp *, const char *, size_t);
 static	void		 encode1(struct termp *, int);
@@ -82,9 +83,8 @@ term_end(struct termp *p)
  *  - TERMP_NOBREAK: this is the most important and is used when making
  *    columns.  In short: don't print a newline and instead expect the
  *    next call to do the padding up to the start of the next column.
- *
- *  - TERMP_TWOSPACE: make sure there is room for at least two space
- *    characters of padding.  Otherwise, rather break the line.
+ *    p->trailspace may be set to 0, 1, or 2, depending on how many
+ *    space characters are required at the end of the column.
  *
  *  - TERMP_DANGLE: don't newline when TERMP_NOBREAK is specified and
  *    the line is overrun, and don't pad-right if it's underrun.
@@ -104,14 +104,15 @@ term_end(struct termp *p)
 void
 term_flushln(struct termp *p)
 {
-	int		 i;     /* current input position in p->buf */
+	size_t		 i;     /* current input position in p->buf */
+	int		 ntab;	/* number of tabs to prepend */
 	size_t		 vis;   /* current visual position on output */
 	size_t		 vbl;   /* number of blanks to prepend to output */
 	size_t		 vend;	/* end of word visual position on output */
 	size_t		 bp;    /* visual right border position */
 	size_t		 dv;    /* temporary for visual pos calculations */
-	int		 j;     /* temporary loop index for p->buf */
-	int		 jhy;	/* last hyph before overflow w/r/t j */
+	size_t		 j;     /* temporary loop index for p->buf */
+	size_t		 jhy;	/* last hyph before overflow w/r/t j */
 	size_t		 maxvis; /* output position of visible boundary */
 	size_t		 mmax; /* used in calculating bp */
 
@@ -119,7 +120,12 @@ term_flushln(struct termp *p)
 	 * First, establish the maximum columns of "visible" content.
 	 * This is usually the difference between the right-margin and
 	 * an indentation, but can be, for tagged lists or columns, a
-	 * small set of values. 
+	 * small set of values.
+	 *
+	 * The following unsigned-signed subtractions look strange,
+	 * but they are actually correct.  If the int p->overstep
+	 * is negative, it gets sign extended.  Subtracting that
+	 * very large size_t effectively adds a small number to dv.
 	 */
 	assert  (p->rmargin >= p->offset);
 	dv     = p->rmargin - p->offset;
@@ -143,10 +149,12 @@ term_flushln(struct termp *p)
 		 * Handle literal tab characters: collapse all
 		 * subsequent tabs into a single huge set of spaces.
 		 */
+		ntab = 0;
 		while (i < p->col && '\t' == p->buf[i]) {
 			vend = (vis / p->tabwidth + 1) * p->tabwidth;
 			vbl += vend - vis;
 			vis = vend;
+			ntab++;
 			i++;
 		}
 
@@ -158,7 +166,7 @@ term_flushln(struct termp *p)
 		 */
 
 		for (j = i, jhy = 0; j < p->col; j++) {
-			if ((j && ' ' == p->buf[j]) || '\t' == p->buf[j])
+			if (' ' == p->buf[j] || '\t' == p->buf[j])
 				break;
 
 			/* Back over the the last printed character. */
@@ -191,7 +199,16 @@ term_flushln(struct termp *p)
 			} else
 				vbl = p->offset;
 
-			/* Remove the p->overstep width. */
+			/* use pending tabs on the new line */
+
+			if (0 < ntab)
+				vbl += ntab * p->tabwidth;
+
+			/*
+			 * Remove the p->overstep width.
+			 * Again, if p->overstep is negative,
+			 * sign extension does the right thing.
+			 */
 
 			bp += (size_t)p->overstep;
 			p->overstep = 0;
@@ -207,7 +224,7 @@ term_flushln(struct termp *p)
 				j = i;
 				while (' ' == p->buf[i])
 					i++;
-				dv = (size_t)(i - j) * (*p->width)(p, ' ');
+				dv = (i - j) * (*p->width)(p, ' ');
 				vbl += dv;
 				vend += dv;
 				break;
@@ -260,20 +277,17 @@ term_flushln(struct termp *p)
 	}
 
 	if (TERMP_HANG & p->flags) {
-		/* We need one blank after the tag. */
-		p->overstep = (int)(vis - maxvis + (*p->width)(p, ' '));
+		p->overstep = (int)(vis - maxvis +
+				p->trailspace * (*p->width)(p, ' '));
 
 		/*
-		 * Behave exactly the same way as groff:
 		 * If we have overstepped the margin, temporarily move
 		 * it to the right and flag the rest of the line to be
 		 * shorter.
-		 * If we landed right at the margin, be happy.
-		 * If we are one step before the margin, temporarily
-		 * move it one step LEFT and flag the rest of the line
-		 * to be longer.
+		 * If there is a request to keep the columns together,
+		 * allow negative overstep when the column is not full.
 		 */
-		if (p->overstep < -1)
+		if (p->trailspace && p->overstep < 0)
 			p->overstep = 0;
 		return;
 
@@ -281,8 +295,7 @@ term_flushln(struct termp *p)
 		return;
 
 	/* If the column was overrun, break the line. */
-	if (maxvis <= vis +
-	    ((TERMP_TWOSPACE & p->flags) ? (*p->width)(p, ' ') : 0)) {
+	if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) {
 		(*p->endline)(p);
 		p->viscol = 0;
 	}
@@ -316,7 +329,10 @@ term_vspace(struct termp *p)
 
 	term_newln(p);
 	p->viscol = 0;
-	(*p->endline)(p);
+	if (0 < p->skipvsp)
+		p->skipvsp--;
+	else
+		(*p->endline)(p);
 }
 
 void
@@ -369,7 +385,7 @@ void
 term_fontpopq(struct termp *p, const void *key)
 {
 
-	while (p->fonti >= 0 && key != &p->fontq[p->fonti])
+	while (p->fonti >= 0 && key < (void *)(p->fontq + p->fonti))
 		p->fonti--;
 	assert(p->fonti >= 0);
 }
@@ -391,6 +407,7 @@ term_fontpop(struct termp *p)
 void
 term_word(struct termp *p, const char *word)
 {
+	const char	 nbrsp[2] = { ASCII_NBRSP, 0 };
 	const char	*seq, *cp;
 	char		 c;
 	int		 sz, uc;
@@ -399,29 +416,42 @@ term_word(struct termp *p, const char *word)
 
 	if ( ! (TERMP_NOSPACE & p->flags)) {
 		if ( ! (TERMP_KEEP & p->flags)) {
-			if (TERMP_PREKEEP & p->flags)
-				p->flags |= TERMP_KEEP;
 			bufferc(p, ' ');
 			if (TERMP_SENTENCE & p->flags)
 				bufferc(p, ' ');
 		} else
 			bufferc(p, ASCII_NBRSP);
 	}
+	if (TERMP_PREKEEP & p->flags)
+		p->flags |= TERMP_KEEP;
 
 	if ( ! (p->flags & TERMP_NONOSPACE))
 		p->flags &= ~TERMP_NOSPACE;
 	else
 		p->flags |= TERMP_NOSPACE;
 
-	p->flags &= ~(TERMP_SENTENCE | TERMP_IGNDELIM);
+	p->flags &= ~TERMP_SENTENCE;
 
 	while ('\0' != *word) {
-		if ((ssz = strcspn(word, "\\")) > 0)
+		if ('\\' != *word) {
+			if (TERMP_SKIPCHAR & p->flags) {
+				p->flags &= ~TERMP_SKIPCHAR;
+				word++;
+				continue;
+			}
+			if (TERMP_NBRWORD & p->flags) {
+				if (' ' == *word) {
+					encode(p, nbrsp, 1);
+					word++;
+					continue;
+				}
+				ssz = strcspn(word, "\\ ");
+			} else
+				ssz = strcspn(word, "\\");
 			encode(p, word, ssz);
-
-		word += (int)ssz;
-		if ('\\' != *word)
+			word += (int)ssz;
 			continue;
+		}
 
 		word++;
 		esc = mandoc_escape(&word, &seq, &sz);
@@ -468,6 +498,9 @@ term_word(struct termp *p, const char *word)
 		case (ESCAPE_FONTITALIC):
 			term_fontrepl(p, TERMFONT_UNDER);
 			break;
+		case (ESCAPE_FONTBI):
+			term_fontrepl(p, TERMFONT_BI);
+			break;
 		case (ESCAPE_FONT):
 			/* FALLTHROUGH */
 		case (ESCAPE_FONTROMAN):
@@ -477,17 +510,23 @@ term_word(struct termp *p, const char *word)
 			term_fontlast(p);
 			break;
 		case (ESCAPE_NOSPACE):
-			if ('\0' == *word)
+			if (TERMP_SKIPCHAR & p->flags)
+				p->flags &= ~TERMP_SKIPCHAR;
+			else if ('\0' == *word)
 				p->flags |= TERMP_NOSPACE;
 			break;
+		case (ESCAPE_SKIPCHAR):
+			p->flags |= TERMP_SKIPCHAR;
+			break;
 		default:
 			break;
 		}
 	}
+	p->flags &= ~TERMP_NBRWORD;
 }
 
 static void
-adjbuf(struct termp *p, int sz)
+adjbuf(struct termp *p, size_t sz)
 {
 
 	if (0 == p->maxcols)
@@ -495,8 +534,7 @@ adjbuf(struct termp *p, int sz)
 	while (sz >= p->maxcols)
 		p->maxcols <<= 2;
 
-	p->buf = mandoc_realloc
-		(p->buf, sizeof(int) * (size_t)p->maxcols);
+	p->buf = mandoc_realloc(p->buf, sizeof(int) * p->maxcols);
 }
 
 static void
@@ -519,31 +557,39 @@ encode1(struct termp *p, int c)
 {
 	enum termfont	  f;
 
-	if (p->col + 4 >= p->maxcols)
-		adjbuf(p, p->col + 4);
+	if (TERMP_SKIPCHAR & p->flags) {
+		p->flags &= ~TERMP_SKIPCHAR;
+		return;
+	}
+
+	if (p->col + 6 >= p->maxcols)
+		adjbuf(p, p->col + 6);
 
 	f = term_fonttop(p);
 
-	if (TERMFONT_NONE == f) {
-		p->buf[p->col++] = c;
-		return;
-	} else if (TERMFONT_UNDER == f) {
+	if (TERMFONT_UNDER == f || TERMFONT_BI == f) {
 		p->buf[p->col++] = '_';
-	} else
-		p->buf[p->col++] = c;
-
-	p->buf[p->col++] = 8;
+		p->buf[p->col++] = 8;
+	}
+	if (TERMFONT_BOLD == f || TERMFONT_BI == f) {
+		if (ASCII_HYPH == c)
+			p->buf[p->col++] = '-';
+		else
+			p->buf[p->col++] = c;
+		p->buf[p->col++] = 8;
+	}
 	p->buf[p->col++] = c;
 }
 
 static void
 encode(struct termp *p, const char *word, size_t sz)
 {
-	enum termfont	  f;
-	int		  i, len;
+	size_t		  i;
 
-	/* LINTED */
-	len = sz;
+	if (TERMP_SKIPCHAR & p->flags) {
+		p->flags &= ~TERMP_SKIPCHAR;
+		return;
+	}
 
 	/*
 	 * Encode and buffer a string of characters.  If the current
@@ -551,35 +597,25 @@ encode(struct termp *p, const char *word, size_t sz)
 	 * character by character.
 	 */
 
-	if (TERMFONT_NONE == (f = term_fonttop(p))) {
-		if (p->col + len >= p->maxcols) 
-			adjbuf(p, p->col + len);
-		for (i = 0; i < len; i++)
+	if (TERMFONT_NONE == term_fonttop(p)) {
+		if (p->col + sz >= p->maxcols) 
+			adjbuf(p, p->col + sz);
+		for (i = 0; i < sz; i++)
 			p->buf[p->col++] = word[i];
 		return;
 	}
 
 	/* Pre-buffer, assuming worst-case. */
 
-	if (p->col + 1 + (len * 3) >= p->maxcols)
-		adjbuf(p, p->col + 1 + (len * 3));
+	if (p->col + 1 + (sz * 5) >= p->maxcols)
+		adjbuf(p, p->col + 1 + (sz * 5));
 
-	for (i = 0; i < len; i++) {
-		if (ASCII_HYPH != word[i] &&
-		    ! isgraph((unsigned char)word[i])) {
-			p->buf[p->col++] = word[i];
-			continue;
-		}
-
-		if (TERMFONT_UNDER == f)
-			p->buf[p->col++] = '_';
-		else if (ASCII_HYPH == word[i])
-			p->buf[p->col++] = '-';
+	for (i = 0; i < sz; i++) {
+		if (ASCII_HYPH == word[i] ||
+		    isgraph((unsigned char)word[i]))
+			encode1(p, word[i]);
 		else
 			p->buf[p->col++] = word[i];
-
-		p->buf[p->col++] = 8;
-		p->buf[p->col++] = word[i];
 	}
 }
 
@@ -590,12 +626,22 @@ term_len(const struct termp *p, size_t sz)
 	return((*p->width)(p, ' ') * sz);
 }
 
+static size_t
+cond_width(const struct termp *p, int c, int *skip)
+{
+
+	if (*skip) {
+		(*skip) = 0;
+		return(0);
+	} else
+		return((*p->width)(p, c));
+}
 
 size_t
 term_strlen(const struct termp *p, const char *cp)
 {
 	size_t		 sz, rsz, i;
-	int		 ssz, c;
+	int		 ssz, skip, c;
 	const char	*seq, *rhs;
 	enum mandoc_esc	 esc;
 	static const char rej[] = { '\\', ASCII_HYPH, ASCII_NBRSP, '\0' };
@@ -607,10 +653,11 @@ term_strlen(const struct termp *p, const char *cp)
 	 */
 
 	sz = 0;
+	skip = 0;
 	while ('\0' != *cp) {
 		rsz = strcspn(cp, rej);
 		for (i = 0; i < rsz; i++)
-			sz += (*p->width)(p, *cp++);
+			sz += cond_width(p, *cp++, &skip);
 
 		c = 0;
 		switch (*cp) {
@@ -627,14 +674,14 @@ term_strlen(const struct termp *p, const char *cp)
 						(seq + 1, ssz - 1);
 					if ('\0' == c)
 						break;
-					sz += (*p->width)(p, c);
+					sz += cond_width(p, c, &skip);
 					continue;
 				case (ESCAPE_SPECIAL):
 					c = mchars_spec2cp
 						(p->symtab, seq, ssz);
 					if (c <= 0)
 						break;
-					sz += (*p->width)(p, c);
+					sz += cond_width(p, c, &skip);
 					continue;
 				default:
 					break;
@@ -644,12 +691,12 @@ term_strlen(const struct termp *p, const char *cp)
 
 			switch (esc) {
 			case (ESCAPE_UNICODE):
-				sz += (*p->width)(p, '?');
+				sz += cond_width(p, '?', &skip);
 				break;
 			case (ESCAPE_NUMBERED):
 				c = mchars_num2char(seq, ssz);
 				if ('\0' != c)
-					sz += (*p->width)(p, c);
+					sz += cond_width(p, c, &skip);
 				break;
 			case (ESCAPE_SPECIAL):
 				rhs = mchars_spec2str
@@ -661,6 +708,9 @@ term_strlen(const struct termp *p, const char *cp)
 				rhs = seq;
 				rsz = ssz;
 				break;
+			case (ESCAPE_SKIPCHAR):
+				skip = 1;
+				break;
 			default:
 				break;
 			}
@@ -668,15 +718,20 @@ term_strlen(const struct termp *p, const char *cp)
 			if (NULL == rhs)
 				break;
 
+			if (skip) {
+				skip = 0;
+				break;
+			}
+
 			for (i = 0; i < rsz; i++)
 				sz += (*p->width)(p, *rhs++);
 			break;
 		case (ASCII_NBRSP):
-			sz += (*p->width)(p, ' ');
+			sz += cond_width(p, ' ', &skip);
 			cp++;
 			break;
 		case (ASCII_HYPH):
-			sz += (*p->width)(p, '-');
+			sz += cond_width(p, '-', &skip);
 			cp++;
 			break;
 		default:
diff --git a/contrib/mdocml/term.h b/contrib/mdocml/term.h
index 56d076e54a8..8cad4be8383 100644
--- a/contrib/mdocml/term.h
+++ b/contrib/mdocml/term.h
@@ -1,6 +1,7 @@
-/*	$Id: term.h,v 1.90 2011/12/04 23:10:52 schwarze Exp $ */
+/*	$Id: term.h,v 1.97 2013/12/25 00:39:31 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2011, 2012, 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -37,6 +38,7 @@ enum	termfont {
 	TERMFONT_NONE = 0,
 	TERMFONT_BOLD,
 	TERMFONT_UNDER,
+	TERMFONT_BI,
 	TERMFONT__MAX
 };
 
@@ -57,26 +59,28 @@ struct	termp {
 	size_t		  defrmargin;	/* Right margin of the device. */
 	size_t		  rmargin;	/* Current right margin. */
 	size_t		  maxrmargin;	/* Max right margin. */
-	int		  maxcols;	/* Max size of buf. */
+	size_t		  maxcols;	/* Max size of buf. */
 	size_t		  offset;	/* Margin offest. */
 	size_t		  tabwidth;	/* Distance of tab positions. */
-	int		  col;		/* Bytes in buf. */
+	size_t		  col;		/* Bytes in buf. */
 	size_t		  viscol;	/* Chars on current line. */
+	size_t		  trailspace;	/* See termp_flushln(). */
 	int		  overstep;	/* See termp_flushln(). */
+	int		  skipvsp;	/* Vertical space to skip. */
 	int		  flags;
 #define	TERMP_SENTENCE	 (1 << 1)	/* Space before a sentence. */
 #define	TERMP_NOSPACE	 (1 << 2)	/* No space before words. */
-#define	TERMP_NOBREAK	 (1 << 4)	/* See term_flushln(). */
-#define	TERMP_IGNDELIM	 (1 << 6)	/* Delims like regulars. */
-#define	TERMP_NONOSPACE	 (1 << 7)	/* No space (no autounset). */
-#define	TERMP_DANGLE	 (1 << 8)	/* See term_flushln(). */
-#define	TERMP_HANG	 (1 << 9)	/* See term_flushln(). */
-#define	TERMP_TWOSPACE	 (1 << 10)	/* See term_flushln(). */
+#define	TERMP_NONOSPACE	 (1 << 3)	/* No space (no autounset). */
+#define	TERMP_NBRWORD	 (1 << 4)	/* Make next word nonbreaking. */
+#define	TERMP_KEEP	 (1 << 5)	/* Keep words together. */
+#define	TERMP_PREKEEP	 (1 << 6)	/* ...starting with the next one. */
+#define	TERMP_SKIPCHAR	 (1 << 7)	/* Skip the next character. */
+#define	TERMP_NOBREAK	 (1 << 8)	/* See term_flushln(). */
+#define	TERMP_DANGLE	 (1 << 9)	/* See term_flushln(). */
+#define	TERMP_HANG	 (1 << 10)	/* See term_flushln(). */
 #define	TERMP_NOSPLIT	 (1 << 11)	/* See termp_an_pre/post(). */
 #define	TERMP_SPLIT	 (1 << 12)	/* See termp_an_pre/post(). */
 #define	TERMP_ANPREC	 (1 << 13)	/* See termp_an_pre(). */
-#define	TERMP_KEEP	 (1 << 14)	/* Keep words together. */
-#define	TERMP_PREKEEP	 (1 << 15)	/* ...starting with the next one. */
 	int		 *buf;		/* Output buffer. */
 	enum termenc	  enc;		/* Type of encoding. */
 	struct mchars	 *symtab;	/* Encoded-symbol table. */
diff --git a/contrib/mdocml/term_ascii.c b/contrib/mdocml/term_ascii.c
index 2f114786f6a..cb7ac294059 100644
--- a/contrib/mdocml/term_ascii.c
+++ b/contrib/mdocml/term_ascii.c
@@ -1,4 +1,4 @@
-/*	$Id: term_ascii.c,v 1.20 2011/12/04 23:10:52 schwarze Exp $ */
+/*	$Id: term_ascii.c,v 1.21 2013/06/01 14:27:20 schwarze Exp $ */
 /*
  * Copyright (c) 2010, 2011 Kristaps Dzonsons 
  *
@@ -73,7 +73,6 @@ ascii_init(enum termenc enc, char *outopts)
 	struct termp	*p;
 
 	p = mandoc_calloc(1, sizeof(struct termp));
-	p->enc = enc;
 
 	p->tabwidth = 5;
 	p->defrmargin = 78;
@@ -93,7 +92,7 @@ ascii_init(enum termenc enc, char *outopts)
 	if (TERMENC_ASCII != enc) {
 		v = TERMENC_LOCALE == enc ?
 			setlocale(LC_ALL, "") :
-			setlocale(LC_CTYPE, "UTF-8");
+			setlocale(LC_CTYPE, "en_US.UTF-8");
 		if (NULL != v && MB_CUR_MAX > 1) {
 			p->enc = enc;
 			p->advance = locale_advance;
diff --git a/contrib/mdocml/tree.c b/contrib/mdocml/tree.c
index 1430c737e00..fdb70e1b933 100644
--- a/contrib/mdocml/tree.c
+++ b/contrib/mdocml/tree.c
@@ -1,6 +1,7 @@
-/*	$Id: tree.c,v 1.47 2011/09/18 14:14:15 schwarze Exp $ */
+/*	$Id: tree.c,v 1.50 2013/12/24 19:11:46 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2013 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -58,13 +59,11 @@ print_mdoc(const struct mdoc_node *n, int indent)
 {
 	const char	 *p, *t;
 	int		  i, j;
-	size_t		  argc, sz;
-	char		**params;
+	size_t		  argc;
 	struct mdoc_argv *argv;
 
 	argv = NULL;
-	argc = sz = 0;
-	params = NULL;
+	argc = 0;
 	t = p = NULL;
 
 	switch (n->type) {
@@ -161,11 +160,14 @@ print_mdoc(const struct mdoc_node *n, int indent)
 			if (argv[i].sz > 0)
 				printf(" ]");
 		}
-		
-		for (i = 0; i < (int)sz; i++)
-			printf(" [%s]", params[i]);
 
-		printf(" %d:%d\n", n->line, n->pos);
+		putchar(' ');
+		if (MDOC_LINE & n->flags)
+			putchar('*');
+		printf("%d:%d", n->line, n->pos);
+		if (n->lastline != n->line)
+			printf("-%d", n->lastline);
+		putchar('\n');
 	}
 
 	if (n->child)
diff --git a/lib/libmandoc/Makefile b/lib/libmandoc/Makefile
index 9fca07e9fa4..b857b8a918b 100644
--- a/lib/libmandoc/Makefile
+++ b/lib/libmandoc/Makefile
@@ -15,6 +15,6 @@ SRCS=	arch.c att.c chars.c \
 	tbl.c tbl_data.c tbl_layout.c tbl_opts.c vol.c
 
 WARNS?=	3
-CFLAGS+= -DHAVE_CONFIG_H -DVERSION="\"1.12.1\""
+CFLAGS+= -DHAVE_CONFIG_H
 
 .include 
diff --git a/usr.bin/mandoc/Makefile b/usr.bin/mandoc/Makefile
index 0995534673e..ed88f4e7b70 100644
--- a/usr.bin/mandoc/Makefile
+++ b/usr.bin/mandoc/Makefile
@@ -15,7 +15,7 @@ SRCS=	eqn_html.c eqn_term.c html.c main.c man_html.c man_term.c mdoc_html.c \
 	term_ascii.c term_ps.c tree.c
 
 WARNS?=	3
-CFLAGS+= -DHAVE_CONFIG_H -DVERSION="\"1.12.1\""
+CFLAGS+= -DHAVE_CONFIG_H
 DPADD=	${LIBMANDOC}
 LDADD=	${LIBMANDOC}