--- old/usr/src/cmd/mandoc/Makefile.common	Fri Jul 25 15:00:10 2014
+++ new/usr/src/cmd/mandoc/Makefile.common	Fri Jul 25 15:00:10 2014
@@ -29,5 +29,4 @@
 CFLAGS +=	$(CC_VERBOSE)
 
 CPPFLAGS +=	-DHAVE_CONFIG_H -DUSE_WCHAR	\
-		-DOSNAME="\"illumos\""	\
-		-DVERSION="\"1.12.1\""
+		-DOSNAME="\"illumos\""
--- old/usr/src/cmd/mandoc/arch.in	Fri Jul 25 15:00:10 2014
+++ new/usr/src/cmd/mandoc/arch.in	Fri Jul 25 15:00:10 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -38,9 +38,9 @@
 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("news68k",		"NeWS68k")
 LINE("newsmips",	"NeWSMIPS")
 LINE("next68k",		"NeXT68k")
+LINE("octeon",		"OCTEON")
 LINE("ofppc",		"OFPPC")
 LINE("palm",		"Palm")
 LINE("pc532",		"PC532")
--- old/usr/src/cmd/mandoc/chars.c	Fri Jul 25 15:00:10 2014
+++ new/usr/src/cmd/mandoc/chars.c	Fri Jul 25 15:00:10 2014
@@ -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 <kristaps@bsd.lv>
  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@@ -37,7 +37,7 @@
 	int		  unicode;
 };
 
-#define	LINES_MAX	  328
+#define	LINES_MAX	  329
 
 #define CHAR(in, ch, code) \
 	{ NULL, (in), (ch), (code) },
@@ -77,7 +77,7 @@
 	 */
 
 	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;
--- old/usr/src/cmd/mandoc/chars.in	Fri Jul 25 15:00:11 2014
+++ new/usr/src/cmd/mandoc/chars.in	Fri Jul 25 15:00:11 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -42,6 +42,7 @@
 CHAR("^",			"",		0)
 CHAR("|",			"",		0)
 CHAR("}",			"",		0)
+CHAR("t",			"",		0)
 
 /* Accents. */
 CHAR("a\"",			"\"",		779)
--- old/usr/src/cmd/mandoc/config.h	Fri Jul 25 15:00:11 2014
+++ new/usr/src/cmd/mandoc/config.h	Fri Jul 25 15:00:11 2014
@@ -7,10 +7,7 @@
 
 #include <stdio.h>
 
-#define HAVE_STRPTIME
-#define HAVE_GETSUBOPT
-#define HAVE_STRLCAT
-#define HAVE_STRLCPY
+#define VERSION "1.12.3"
 
 #include <sys/types.h>
 
@@ -29,14 +26,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
--- old/usr/src/cmd/mandoc/html.c	Fri Jul 25 15:00:11 2014
+++ new/usr/src/cmd/mandoc/html.c	Fri Jul 25 15:00:11 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 	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 @@
 	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 @@
 	 */
 
 	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 @@
 		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 @@
 	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);
@@ -338,6 +372,33 @@
 			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. */
 			c = mchars_num2uc(seq + 1, len - 1);
@@ -356,19 +417,6 @@
 			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 @@
 	}
 
 	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)) {
--- old/usr/src/cmd/mandoc/html.h	Fri Jul 25 15:00:11 2014
+++ new/usr/src/cmd/mandoc/html.h	Fri Jul 25 15:00:11 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -75,6 +75,7 @@
 	HTMLFONT_NONE = 0,
 	HTMLFONT_BOLD,
 	HTMLFONT_ITALIC,
+	HTMLFONT_BI,
 	HTMLFONT_MAX
 };
 
@@ -117,6 +118,7 @@
 #define	HTML_PREKEEP	 (1 << 3)
 #define	HTML_NONOSPACE	 (1 << 4) /* never add spaces */
 #define	HTML_LITERAL	 (1 << 5) /* literal (e.g., <PRE>) 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 */
--- old/usr/src/cmd/mandoc/lib.in	Fri Jul 25 15:00:11 2014
+++ new/usr/src/cmd/mandoc/lib.in	Fri Jul 25 15:00:11 2014
@@ -10,9 +10,71 @@
  */
 
 /*
- * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2014 Garrett D'Amore <garrett@damore.org>
  */
 
 /*
- * TBD
+ * Note that we don't document "legacy" libraries that have moved into
+ * libc.  While there will be section 3lib man pages for them, they
+ * won't be referenced in other man pages.
  */
+LINE("libadm",		"General Administrative Library (libadm, \\-ladm)")
+LINE("libbsdmalloc",	"BSD Memory Allocation Library (libbsdmalloc, -lbsdmalloc)")
+LINE("libbsm",		"Security and Auditing Library (libbsm, \\lbsm)")
+LINE("libc",		"Standard C Library (libc, \\-lc)")
+LINE("libc_db",		"Threads Debugging Library (libc_db, \\-lc_db)")
+LINE("libcfgadm",	"Configuration Adminstration Library (libcfgadm, \\-lcfgadm)")
+LINE("libcommputil",	"Communication Protocol Parser Utilities Library (libpcommutil, \\-lcommputil)")
+LINE("libcontract",	"Contract Management Library (libcontract, \\-lcontract)")
+LINE("libcpc",		"CPU Performance Counters Library (libcpc, \\-lcpc)")
+LINE("libcurses",	"Curses Library (libcurses, \\-lcurses)")
+LINE("libdat",		"Direct Access Transport Library (libdat, \\-ldat)")
+LINE("libdevid",	"Device ID Library (libdevid, \\-ldevid)")
+LINE("libdevinfo",	"Device Information Library (libdevinfo, \\-ldevinfo)")
+LINE("libdlpi",		"Data Link Provider Interface (DLPI) Library (libdlp, \\-ldlpi)")
+LINE("libdns_sd",	"DNS Service Discovery Library (libdns_sd, \\-ldns_sd)")
+LINE("libelf",		"ELF Access Library (libelf, \\-lelf)")
+LINE("libexacct",	"Extended Accounting File Access Library (libexacct, \\-lexacct)")
+LINE("libfcoe",		"FCoE Port Management Library (libfcoe, \\-lfcoe)")
+LINE("libfstyp",	"File System Type Identification Library (libfstyp, \\-lfstyp \\-lnvpair)")
+LINE("libgen",		"String Pattern Matching Library (libgen, \\-lgen)")
+LINE("libgss",		"Generic Security Services Library (libgss, \\-lgss)")
+LINE("libiscsit",	"iSCSI Management Library (libiscsit, \\-liscsit)")
+LINE("libkstat",	"Kernel Statistics Library (libkstat, \\-lkstat)")
+LINE("libkvm",		"Kernel VM Library (libkvm, \\-lkvm)")
+LINE("libldap",		"LDAP Library (libldap, \\-lldap)")
+LINE("liblgrp",		"Locality Group Library (liblgrp, -llgrp)")
+LINE("libm",		"Mathematical Library (libm, -lm)")
+LINE("libmail",		"User Mailbox Library (libmail, -lmail)")
+LINE("libmalloc",	"Memory Allocation Library (libmalloc, -lmalloc)")
+LINE("libmd",		"Message Digest Library (libmd, -lmd)")
+LINE("libmp",		"Multiple Precision Library (libmp, -lmp)")
+LINE("libmpapi",	"Common Multipath Management Library (libmpapi, -lMPAPI)")
+LINE("libnsl",		"Network Services Library (libnsl, \\-lnsl)")
+LINE("libnvpair",	"Name-Value Pair Library (libnvpair, \\-lnvpair)")
+LINE("libpam",		"PAM (Pluggable Authentication Module) Library (libpam, \\-lpam)")
+LINE("libpicl",		"PICL Library (libpicl, \\-lpicl)")
+LINE("libpicltree",	"PICL Plug-In Library (libpicltree, \\-lpicltree)")
+LINE("libpkcs11",	"PKCS#11 Cryptographic Framework Library (libpkcs11, \\-lpkcs11)")
+LINE("libpool",		"Pool Configuration Manipulation Library (libpool, \\-lpool)")
+LINE("libproc",		"Process Control Library (libproc, \\-lproc)")
+LINE("libproject",	"Project Database Access Library (libproject, \\-lproject)")
+LINE("libresolv",	"Resolver Library (libresolv, \\-lresolv \\-lsocket \\-lnsl)")
+LINE("librpc",		"RPC Service Library (librpcsvc, \\-lrpc)")
+LINE("librsm",		"Remote Shared Memory Interface Library (librsm, \\-lrsm)")
+LINE("libsasl",		"Simple Authentication and Security Library (libsasl, \\-lsasl)")
+LINE("libscf",		"Service Configuration Facility Library (libscf, \\-lscf)")
+LINE("libsec",		"File Access Control Library (libsec, \\-lsec)")
+LINE("libsecdb",	"Security Attributes Database Library (libsecdb, \\-lsecdb)")
+LINE("libsip",		"Session Initiation Protocol Library (libsip, \\-lsip)")
+LINE("libslp",		"Service Location Protocol Library (libslp, \\-lslp)")
+LINE("libsocket",	"Sockets Library (libsocket, \\-lsocket)")
+LINE("libstmf",		"SCSI Target Mode Framework Library (libstmf, \\-lstmf)")
+LINE("libsysevet",	"System Event Inteface Library (libsysevent, \\-lsysevent)")
+LINE("libtecla",	"Interactive Command Line Input Library (libtecla, \\-ltecla)")
+LINE("libtnfctl",	"TNF Probe Control Library (libtnfctl, \\-ltnfctl)")
+LINE("libtsol",		"Trusted Extensions Library (libtsol, \\-ltsol)")
+LINE("libuuid",		"UUID Library (libuuid, \\-luuid)")
+LINE("libvolmgt",	"Volume Management Library (libvolmgt, \\-lvolmgt)")
+LINE("libxcurses",	"X/Open Curses Library (libxcurses, \\-lxcurses)")
+LINE("libxnet",		"X/Open Networking Library (libxnet, \\-lxnet)")
--- old/usr/src/cmd/mandoc/libman.h	Fri Jul 25 15:00:11 2014
+++ new/usr/src/cmd/mandoc/libman.h	Fri Jul 25 15:00:11 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -39,7 +39,7 @@
 	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 @@
 
 __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);
--- old/usr/src/cmd/mandoc/libmandoc.h	Fri Jul 25 15:00:11 2014
+++ new/usr/src/cmd/mandoc/libmandoc.h	Fri Jul 25 15:00:11 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 	ROFF_ERR /* badness: puke and stop */
 };
 
-enum	regs {
-	REG_nS = 0, /* nS register */
-	REG__MAX
-};
-
 __BEGIN_DECLS
 
 struct	roff;
@@ -47,12 +43,11 @@
 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_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 *, 
--- old/usr/src/cmd/mandoc/libmdoc.h	Fri Jul 25 15:00:11 2014
+++ new/usr/src/cmd/mandoc/libmdoc.h	Fri Jul 25 15:00:11 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 
 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 @@
 #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 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 @@
 #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 @@
 
 __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_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 *);
--- old/usr/src/cmd/mandoc/libroff.h	Fri Jul 25 15:00:12 2014
+++ new/usr/src/cmd/mandoc/libroff.h	Fri Jul 25 15:00:12 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -31,7 +31,7 @@
 	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;
--- old/usr/src/cmd/mandoc/main.c	Fri Jul 25 15:00:12 2014
+++ new/usr/src/cmd/mandoc/main.c	Fri Jul 25 15:00:12 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 	struct curparse	 curp;
 	enum mparset	 type;
 	enum mandoclevel rc;
+	char		*defos;
 
 	progname = strrchr(argv[0], '/');
 	if (progname == NULL)
@@ -97,10 +98,24 @@
 	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 @@
 			/* 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 @@
 		(*curp.outfree)(curp.outdata);
 	if (curp.mp)
 		mparse_free(curp.mp);
+	free(defos);
 
 	return((int)rc);
 }
@@ -170,12 +186,12 @@
 
 	fprintf(stderr, "usage: %s "
 			"[-V] "
-			"[-foption] "
+			"[-Ios=name] "
 			"[-mformat] "
 			"[-Ooption] "
 			"[-Toutput] "
-			"[-Wlevel] "
-			"[file...]\n", 
+			"[-Wlevel]\n"
+			"\t      [file ...]\n", 
 			progname);
 
 	exit((int)MANDOCLEVEL_BADARG);
--- old/usr/src/cmd/mandoc/man.c	Fri Jul 25 15:00:12 2014
+++ new/usr/src/cmd/mandoc/man.c	Fri Jul 25 15:00:12 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -40,7 +40,8 @@
 	"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 @@
 
 
 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 @@
 
 
 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 @@
 
 
 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 @@
 
 
 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 @@
 	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 @@
 
 
 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 @@
 	 * 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 @@
 
 	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 @@
 		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 @@
 
 	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 @@
 	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 @@
 	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 @@
 	 */
 
 	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 @@
 	 */
 
 	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 @@
 		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 @@
 		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 @@
 	 * 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,11 +609,11 @@
 	 * 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
@@ -612,8 +620,8 @@
 	 * 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 @@
 	 * 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 @@
 
 	/* 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);
 }
--- old/usr/src/cmd/mandoc/man.h	Fri Jul 25 15:00:12 2014
+++ new/usr/src/cmd/mandoc/man.h	Fri Jul 25 15:00:12 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -52,6 +52,10 @@
 	MAN_in,
 	MAN_ft,
 	MAN_OP,
+	MAN_EX,
+	MAN_EE,
+	MAN_UR,
+	MAN_UE,
 	MAN_MAX
 };
 
--- old/usr/src/cmd/mandoc/man_html.c	Fri Jul 25 15:00:12 2014
+++ new/usr/src/cmd/mandoc/man_html.c	Fri Jul 25 15:00:12 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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_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 @@
 	{ 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 @@
 }
 
 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,7 +166,7 @@
 		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);
@@ -167,7 +173,7 @@
 	} 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_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 @@
 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 @@
 
 	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,12 +264,12 @@
 			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);
@@ -270,13 +276,13 @@
 
 	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 @@
 	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,8 +369,8 @@
 	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");
@@ -371,8 +377,8 @@
 	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 @@
 		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,7 +549,7 @@
 	/* 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. */
 
@@ -550,7 +556,7 @@
 	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 @@
 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
@@ -685,4 +691,28 @@
 	PAIR_STYLE_INIT(&tag, h);
 	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);
 }
--- old/usr/src/cmd/mandoc/man_macro.c	Fri Jul 25 15:00:12 2014
+++ new/usr/src/cmd/mandoc/man_macro.c	Fri Jul 25 15:00:12 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
  *
  * 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 @@
 	{ 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 @@
 	{ 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 @@
  * 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 @@
 		return;
 
 	assert(er < MANDOCERR_FATAL);
-	man_nmsg(m, n, er);
+	man_nmsg(man, n, er);
 }
 
 
@@ -114,7 +120,7 @@
  * 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;
@@ -121,26 +127,26 @@
 
 	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 @@
 		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 @@
 			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 @@
  * 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 @@
 	 */
 	assert(n);
 
-	return(man_unscope(m, n, MANDOCERR_MAX));
+	return(man_unscope(man, n, MANDOCERR_MAX));
 }
 
 
@@ -273,23 +287,25 @@
 	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 (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);
 
-	if ( ! rew_scope(MAN_BODY, m, ntok))
-		return(0);
-	if ( ! rew_scope(MAN_BLOCK, m, ntok))
-		return(0);
-
 	return(1);
 }
 
@@ -298,34 +314,40 @@
 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 @@
 
 	/* 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 @@
 	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 @@
 	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 @@
 
 	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 @@
 	 * 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 @@
 
 
 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 @@
 	if ('\0' == *start)
 		return(0);
 
-	*v = mandoc_getarg(m->parse, v, line, pos);
+	*v = mandoc_getarg(man->parse, v, line, pos);
 	return(1);
 }
--- old/usr/src/cmd/mandoc/man_term.c	Fri Jul 25 15:00:12 2014
+++ new/usr/src/cmd/mandoc/man_term.c	Fri Jul 25 15:00:12 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 	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,7 +65,7 @@
 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);
@@ -73,11 +72,13 @@
 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_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 @@
 	{ 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 @@
 {
 	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,9 +158,9 @@
 		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));
@@ -161,9 +167,10 @@
 
 	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 @@
  * 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 @@
 		if (NULL == n->prev)
 			return;
 
-	term_vspace(p);
+	for (i = 0; i < pardist; i++)
+		term_vspace(p);
 }
 
 /* ARGSUSED */
@@ -243,7 +252,7 @@
 
 	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,7 +265,8 @@
 	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;
 	}
 
@@ -265,6 +275,21 @@
 
 /* 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)
 {
 	enum termfont		 font[2];
@@ -307,7 +332,7 @@
 		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 @@
 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)
+		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);
-		if (MAN_SH == n->parent->tok)
-			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 @@
 
 	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 @@
 {
 
 	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 @@
 	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 @@
 		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 @@
 		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 @@
 	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 @@
 	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 @@
 		/* 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 @@
 	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 @@
 	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 @@
 static int
 pre_SS(DECL_ARGS)
 {
+	int	 i;
 
 	switch (n->type) {
 	case (MAN_BLOCK):
@@ -740,11 +796,12 @@
 				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 @@
 static int
 pre_SH(DECL_ARGS)
 {
+	int	 i;
 
 	switch (n->type) {
 	case (MAN_BLOCK):
@@ -792,7 +850,8 @@
 		/* 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,7 +949,33 @@
 		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)
 {
 	size_t		 rm, rmax;
@@ -910,29 +995,8 @@
 			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 @@
 
 	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 @@
 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 @@
 	/* 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 @@
 
 	p->flags &= ~TERMP_NOBREAK;
 	p->flags |= TERMP_NOSPACE;
+	p->trailspace = 0;
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin;
 
@@ -1051,14 +1142,14 @@
 {
 	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);
@@ -1065,10 +1156,11 @@
 
 	/* 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 @@
 	/* 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;
--- old/usr/src/cmd/mandoc/man_validate.c	Fri Jul 25 15:00:13 2014
+++ new/usr/src/cmd/mandoc/man_validate.c	Fri Jul 25 15:00:13 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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,6 +49,7 @@
 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);
@@ -55,6 +56,7 @@
 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_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_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 @@
 	{ 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 @@
 	{ 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,7 +147,7 @@
 	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);
 }
@@ -145,20 +154,20 @@
 
 
 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 @@
 		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,19 +190,19 @@
 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
@@ -200,10 +209,10 @@
 		 * 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 @@
 {
 	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 @@
 { \
 	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); \
@@ -241,6 +250,17 @@
 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)
 {
 	char	*cp;
@@ -282,7 +302,7 @@
 
 	if (0 == ok) {
 		mandoc_vmsg
-			(MANDOCERR_BADFONT, m->parse,
+			(MANDOCERR_BADFONT, man->parse,
 			 n->line, n->pos, "%s", cp);
 		*cp = '\0';
 	}
@@ -289,7 +309,7 @@
 
 	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 @@
 {
 
 	if (MAN_BLOCK == n->type)
-		m->flags &= ~MAN_LITERAL;
+		man->flags &= ~MAN_LITERAL;
 	return(1);
 }
 
@@ -312,7 +332,7 @@
 	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 @@
 {
 
 	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 @@
 	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,7 +372,25 @@
 	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 @@
 	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,13 +416,13 @@
 			/* 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 */
 
@@ -396,9 +429,9 @@
 	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 @@
 		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 @@
 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 @@
 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 @@
 			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 @@
 			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 @@
 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);
 }
--- old/usr/src/cmd/mandoc/mandoc.c	Fri Jul 25 15:00:13 2014
+++ new/usr/src/cmd/mandoc/mandoc.c	Fri Jul 25 15:00:13 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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)
+
+enum mandoc_esc
+mandoc_escape(const char **end, const char **start, int *sz)
 {
-	int		 i;
-	size_t		 sz;
-	const char	*cp;
+	const char	*local_start;
+	int		 local_sz;
+	char		 term;
+	enum mandoc_esc	 gly; 
 
-	i = 0;
+	/*
+	 * When the caller doesn't provide return storage,
+	 * use local storage.
+	 */
 
-	/* The expression consists of a subexpression. */
+	if (NULL == start)
+		start = &local_start;
+	if (NULL == sz)
+		sz = &local_sz;
 
-	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.
+	 * 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.
 	 */
 
-	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;
-	enum mandoc_esc	 gly; 
-
-	cp = *end;
-	rstart = cp;
-	if (start)
-		*start = rstart;
-	i = lim = 0;
 	gly = ESCAPE_ERROR;
-	term = numeric = '\0';
+	*start = ++*end;
+	*sz = 0;
+	term = '\0';
 
-	switch ((c = cp[i++])) {
+	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 @@
 	 */
 	case ('('):
 		gly = ESCAPE_SPECIAL;
-		lim = 2;
+		*sz = 2;
 		break;
 	case ('['):
 		gly = ESCAPE_SPECIAL;
@@ -131,18 +86,39 @@
 		 * 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 @@
 	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,6 +165,8 @@
 		/* FALLTHROUGH */
 	case ('b'):
 		/* FALLTHROUGH */
+	case ('B'):
+		/* FALLTHROUGH */
 	case ('D'):
 		/* FALLTHROUGH */
 	case ('o'):
@@ -199,12 +173,15 @@
 		/* 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 @@
 	 * 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 @@
 	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 @@
 	 * 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 @@
 	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.
+	 * Read up to the terminating character,
+	 * paying attention to nested escapes.
 	 */
 
-	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.
-	 */
-
 	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 @@
 		}
 		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 @@
  * 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 @@
 	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 @@
 	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 @@
 	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.
--- old/usr/src/cmd/mandoc/mandoc.h	Fri Jul 25 15:00:13 2014
+++ new/usr/src/cmd/mandoc/mandoc.h	Fri Jul 25 15:00:13 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	MANDOCERR_MAX
 };
 
-struct	tbl {
+struct	tbl_opts {
 	char		  tab; /* cell-separator */
 	char		  decimal; /* decimal point */
 	int		  linesize;
@@ -175,12 +180,6 @@
 	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
@@ -187,8 +186,8 @@
  * 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 @@
 	TBL_CELL_DOWN, /* ^ */
 	TBL_CELL_HORIZ, /* _, - */
 	TBL_CELL_DHORIZ, /* = */
-	TBL_CELL_VERT, /* | */
-	TBL_CELL_DVERT, /* || */
 	TBL_CELL_MAX
 };
 
@@ -213,6 +210,7 @@
  */
 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 @@
  * 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 @@
 	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 @@
 			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 *);
--- old/usr/src/cmd/mandoc/mdoc.c	Fri Jul 25 15:00:13 2014
+++ new/usr/src/cmd/mandoc/mdoc.c	Fri Jul 25 15:00:13 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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_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,7 +197,7 @@
  * 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;
 
@@ -204,6 +204,7 @@
 	p = mandoc_calloc(1, sizeof(struct mdoc));
 
 	p->parse = parse;
+	p->defos = defos;
 	p->roff = roff;
 
 	mdoc_hash_init();
@@ -217,61 +218,61 @@
  * 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 @@
  * 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 @@
 	 * 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,8 +313,8 @@
 	/* 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);
 	}
 
@@ -322,23 +321,23 @@
 	/* 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 @@
 
 	switch (p->type) {
 	case (MDOC_BODY):
+		if (ENDBODY_NOT != p->end)
+			break;
 		/* FALLTHROUGH */
 	case (MDOC_TAIL):
 		/* FALLTHROUGH */
@@ -424,27 +425,28 @@
 
 
 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);
 }
@@ -451,70 +453,71 @@
 
 
 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 @@
 		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,28 +562,45 @@
 		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 @@
 
 
 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 @@
 
 	/* 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 @@
  * 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 @@
 	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 @@
 		}
 
 		/* 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,7 +734,7 @@
  * 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;
@@ -713,13 +741,13 @@
 
 	/* 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 @@
 	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_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 @@
 			 * 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,10 +814,10 @@
 	*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,
@@ -796,17 +824,18 @@
 		 * 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 @@
 	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 @@
  * 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 @@
 	/* 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 @@
 	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,7 +911,7 @@
 	 */
 
 	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
@@ -889,14 +918,14 @@
 	 * 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 @@
 
 	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,8 +950,8 @@
 			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);
 	}
@@ -929,7 +958,7 @@
 
 	/* 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);
@@ -936,7 +965,7 @@
 
 err:	/* Error out. */
 
-	m->flags |= MDOC_HALT;
+	mdoc->flags |= MDOC_HALT;
 	return(0);
 }
 
@@ -980,7 +1009,7 @@
 
 	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);
--- old/usr/src/cmd/mandoc/mdoc.h	Fri Jul 25 15:00:13 2014
+++ new/usr/src/cmd/mandoc/mdoc.h	Fri Jul 25 15:00:13 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -311,6 +311,7 @@
 	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 @@
 	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 @@
 	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 */
--- old/usr/src/cmd/mandoc/mdoc_argv.c	Fri Jul 25 15:00:13 2014
+++ new/usr/src/cmd/mandoc/mdoc_argv.c	Fri Jul 25 15:00:13 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2012 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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	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	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 @@
 	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 @@
 	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 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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
  * 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,17 +342,13 @@
 
 	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))
+		if ( ! argv_multi(mdoc, line, &tmp, pos, buf))
 			return(ARGV_ERROR);
 		break;
-	case (ARGV_OPT_SINGLE):
-		if ( ! argv_opt_single(m, line, &tmp, pos, buf))
-			return(ARGV_ERROR);
-		break;
 	case (ARGV_NONE):
 		break;
 	}
@@ -413,14 +407,14 @@
 }
 
 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 @@
 	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,7 +432,7 @@
 	 * 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;
@@ -445,18 +439,19 @@
 				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
@@ -463,10 +458,10 @@
 		 * 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 @@
 		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 @@
 
 		/* 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 @@
 			/* 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 @@
 			(*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 @@
 }
 
 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 @@
 	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 @@
 }
 
 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)
@@ -685,30 +688,6 @@
 		return(1);
 
 	v->sz = 1;
-	v->value = mandoc_malloc(sizeof(char *));
-	v->value[0] = mandoc_strdup(p);
-
-	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);
 
--- old/usr/src/cmd/mandoc/mdoc_html.c	Fri Jul 25 15:00:14 2014
+++ new/usr/src/cmd/mandoc/mdoc_html.c	Fri Jul 25 15:00:14 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -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 @@
 };
 
 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,7 +362,7 @@
 		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);
@@ -368,7 +369,7 @@
 	} 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_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 @@
 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 @@
 
 	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 @@
 
 		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 @@
 
 	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 @@
 	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 @@
 		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 @@
 		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 @@
 	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 @@
 		 */
 
 		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 @@
 	}
 
 	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 @@
 	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 @@
 	case (MDOC_So):
 		/* FALLTHROUGH */
 	case (MDOC_Sq):
-		print_text(h, "\\(aq");
+		print_text(h, "\\(cq");
 		break;
 	default:
 		abort();
--- old/usr/src/cmd/mandoc/mdoc_macro.c	Fri Jul 25 15:00:14 2014
+++ new/usr/src/cmd/mandoc/mdoc_macro.c	Fri Jul 25 15:00:14 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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	  	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 @@
 				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 @@
  * 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 @@
 	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 @@
  */
 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 @@
 			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,34 +550,39 @@
 
 
 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;
@@ -564,7 +589,7 @@
 	}
 
 	assert(n);
-	if ( ! rew_last(m, n))
+	if ( ! rew_last(mdoc, n))
 		return(0);
 
 	/*
@@ -572,10 +597,10 @@
 	 * 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 @@
  * 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 @@
 	 *	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,7 +663,7 @@
 
 	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);
@@ -636,7 +670,7 @@
 		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 @@
 		 * 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 @@
 	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 @@
 	 */
 	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 @@
 			 * 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,7 +774,7 @@
 		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;
 	}
 
@@ -745,18 +781,18 @@
 	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++) {
@@ -763,12 +799,12 @@
 		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 @@
 		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 @@
 	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 @@
 
 	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 @@
 
 	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 @@
 		 */
 
 		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 @@
 			 */
 			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 @@
 			 * 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 @@
 		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 @@
 		 * 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 @@
 	 */
 
 	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 @@
 	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 @@
 
 	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 @@
 		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 @@
 	 * 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,16 +1095,19 @@
 	 */
 
 	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;
 
 	for ( ; ; ) {
@@ -1067,7 +1114,7 @@
 		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 @@
 			 * 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 @@
 				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 @@
 		/* 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 @@
 			 */
 
 			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 @@
 			 */
 
 			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 @@
 			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 @@
 	 * 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 @@
 
 	/* 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 @@
 	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 @@
 	 * 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 @@
 
 	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 @@
 
 		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 @@
 	/* 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 @@
 	 * 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 @@
 	 * 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 @@
 	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 @@
 	 * 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 @@
 		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 @@
 			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 @@
 		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 @@
 	/* 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 @@
 
 	if ( ! nl)
 		return(1);
-	return(append_delims(m, line, pos, buf));
+	return(append_delims(mdoc, line, pos, buf));
 }
 
 
@@ -1481,7 +1539,7 @@
 	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 @@
 
 	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 @@
 
 	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 @@
 		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 @@
 		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 @@
 				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 @@
 	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,7 +1690,7 @@
 
 	/* 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. */
@@ -1638,7 +1697,7 @@
 
 	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 @@
 		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 @@
 {
 	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 @@
 	 * 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 @@
 obsolete(MACRO_PROT_ARGS)
 {
 
-	mdoc_pmsg(m, line, ppos, MANDOCERR_MACROOBS);
+	mdoc_pmsg(mdoc, line, ppos, MANDOCERR_MACROOBS);
 	return(1);
 }
 
@@ -1708,7 +1768,7 @@
  * 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 @@
 	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 @@
 		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 @@
 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 @@
 		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);
--- old/usr/src/cmd/mandoc/mdoc_man.c	Fri Jul 25 15:00:14 2014
+++ new/usr/src/cmd/mandoc/mdoc_man.c	Fri Jul 25 15:00:14 2014
@@ -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 <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,23 +18,19 @@
 #include "config.h"
 #endif
 
+#include <assert.h>
 #include <stdio.h>
 #include <string.h>
 
 #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
+#define	DECL_ARGS const struct mdoc_meta *meta, \
+		  const struct mdoc_node *n
 
-struct	mman {
-	int		  need_space; /* next word needs prior ws */
-	int		  need_nl; /* next word needs prior nl */
-};
-
 struct	manact {
 	int		(*cond)(DECL_ARGS); /* DON'T run actions */
 	int		(*pre)(DECL_ARGS); /* pre-node action */
@@ -45,29 +41,74 @@
 
 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,33 +123,33 @@
 	{ 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"
@@ -115,26 +156,26 @@
 		"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 */
@@ -141,15 +182,15 @@
 	{ 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,94 +199,327 @@
 	{ 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
 man_man(void *arg, const struct man *man)
 {
 
@@ -261,20 +535,26 @@
 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');
 }
 
@@ -281,17 +561,16 @@
 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,12 +581,13 @@
 		 * 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
@@ -314,9 +594,9 @@
 		 * 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 @@
 	 */
 	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 @@
 	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 @@
 	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 @@
 	suffix = manacts[n->tok].suffix;
 	if (NULL == suffix)
 		return;
-	mm->need_space = 0;
-	print_word(mm, suffix);
+	outflags &= ~MMAN_spc;
+	print_word(suffix);
 }
 
-/*
- * Used in listings (percent = %A, e.g.).
- * FIXME: this is incomplete. 
- * It doesn't print a nice ", and" for lists.
- */
 static void
+post_font(DECL_ARGS)
+{
+
+	font_pop();
+}
+
+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 @@
 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 @@
 
 	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 @@
 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 @@
 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 @@
 
 	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 @@
 pre_dl(DECL_ARGS)
 {
 
-	mm->need_nl = 1;
-	print_word(mm, ".RS 6n");
-	mm->need_nl = 1;
+	print_offs("6n");
 	return(1);
 }
 
@@ -508,33 +1004,408 @@
 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
 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:
-			if (bln->norm->Bl.width)
-				print_word(mm, bln->norm->Bl.width);
+			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:
+			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);
 }
 
@@ -541,13 +1412,27 @@
 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 @@
 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,7 +1474,7 @@
 post_pf(DECL_ARGS)
 {
 
-	mm->need_space = 0;
+	outflags &= ~MMAN_spc;
 }
 
 static int
@@ -580,21 +1481,45 @@
 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,10 +1527,46 @@
 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
 pre_xr(DECL_ARGS)
 {
 
@@ -612,14 +1573,14 @@
 	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 @@
 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);
 }
--- old/usr/src/cmd/mandoc/mdoc_term.c	Fri Jul 25 15:00:14 2014
+++ new/usr/src/cmd/mandoc/mdoc_term.c	Fri Jul 25 15:00:14 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
  *
  * 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 @@
 
 #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_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_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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
 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 @@
 		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 @@
 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,7 +293,6 @@
 print_mdoc_node(DECL_ARGS)
 {
 	int		 chld;
-	const void	*font;
 	struct termpair	 npair;
 	size_t		 offset, rmargin;
 
@@ -300,7 +299,7 @@
 	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,34 +307,17 @@
 	/*
 	 * 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 @@
 	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 @@
 	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 @@
 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 @@
 
 	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 @@
 {
 	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 @@
 	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 @@
 	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 @@
 	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 @@
 	 */
 
 	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 @@
 		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 @@
 		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 @@
 
 	p->flags &= ~TERMP_DANGLE;
 	p->flags &= ~TERMP_NOBREAK;
-	p->flags &= ~TERMP_TWOSPACE;
 	p->flags &= ~TERMP_HANG;
+	p->trailspace = 0;
 }
 
 
@@ -999,8 +997,10 @@
 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)
@@ -1007,14 +1007,15 @@
 			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 @@
 
 	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 @@
 
 	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 @@
 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,7 +1380,7 @@
 
 	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);
@@ -1382,7 +1387,7 @@
 	} 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,11 +1407,20 @@
 {
 
 	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);
+}
+
+
+/* ARGSUSED */
 static int
 termp_sh_pre(DECL_ARGS)
 {
@@ -1425,6 +1439,8 @@
 		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;
@@ -1498,17 +1514,6 @@
 
 
 /* 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 @@
 static int
 termp_fn_pre(DECL_ARGS)
 {
+	size_t		 rmargin = 0;
 	int		 pretty;
 
 	pretty = MDOC_SYNPRETTY & n->flags;
@@ -1533,11 +1539,24 @@
 	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 @@
 	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 @@
 	if (pretty) {
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, ";");
+		term_flushln(p);
 	}
 
 	return(0);
@@ -1579,20 +1601,16 @@
 
 	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 @@
 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 @@
 	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 @@
 		pp = "UNIX";
 		break;
 	default:
-		break;
+		abort();
+		/* NOTREACHED */
 	}
 
 	term_word(p, pp);
@@ -1766,16 +1785,6 @@
 
 
 /* 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 @@
 	case (MDOC_Do):
 		/* FALLTHROUGH */
 	case (MDOC_Dq):
-		term_word(p, "``");
+		term_word(p, "\\(lq");
 		break;
 	case (MDOC_Eo):
 		break;
@@ -1944,7 +1953,7 @@
 	case (MDOC_So):
 		/* FALLTHROUGH */
 	case (MDOC_Sq):
-		term_word(p, "`");
+		term_word(p, "\\(oq");
 		break;
 	default:
 		abort();
@@ -1989,7 +1998,7 @@
 	case (MDOC_Do):
 		/* FALLTHROUGH */
 	case (MDOC_Dq):
-		term_word(p, "''");
+		term_word(p, "\\(rq");
 		break;
 	case (MDOC_Eo):
 		break;
@@ -2010,7 +2019,7 @@
 	case (MDOC_So):
 		/* FALLTHROUGH */
 	case (MDOC_Sq):
-		term_word(p, "'");
+		term_word(p, "\\(cq");
 		break;
 	default:
 		abort();
@@ -2023,16 +2032,31 @@
 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 @@
 	if (MDOC_SYNPRETTY & n->flags) {
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, ";");
+		term_flushln(p);
 	}
 }
 
@@ -2071,7 +2096,7 @@
 
 	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 @@
 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 != (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);
+	}
 
-	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, ":");
-
 	term_fontpush(p, TERMFONT_BOLD);
-	term_word(p, sv->string);
+	term_word(p, link->string);
 	term_fontpop(p);
 
 	return(0);
@@ -2225,9 +2249,9 @@
 	 */
 	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 @@
 	 */
 	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);
--- old/usr/src/cmd/mandoc/mdoc_validate.c	Fri Jul 25 15:00:14 2014
+++ new/usr/src/cmd/mandoc/mdoc_validate.c	Fri Jul 25 15:00:14 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 <sys/utsname.h>
 #endif
 
@@ -97,17 +97,19 @@
 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_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_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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
 	{ 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 @@
 	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 @@
 }
 
 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 @@
 	}
 
 	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 @@
 }
 
 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 @@
 
 	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 @@
 	/* 
 	 * 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 @@
 		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 @@
 
 	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 @@
 	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);
+	if (NULL != mdoc->meta.name)
 		return(1);
-	} else if (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 @@
 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 @@
 			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 @@
 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 @@
 
 	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,7 +1863,48 @@
 	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 @@
 
 	/* 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 @@
 			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 @@
 	 * 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);
@@ -1977,6 +2116,32 @@
 }
 
 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 @@
 		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 @@
 	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 @@
 
 	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);
--- old/usr/src/cmd/mandoc/msec.in	Fri Jul 25 15:00:15 2014
+++ new/usr/src/cmd/mandoc/msec.in	Fri Jul 25 15:00:15 2014
@@ -14,231 +14,96 @@
  */
 
 LINE("1",		"User Commands")
-LINE("1B",		"illumos/BSD Compatibility Package Commands")
-LINE("1b",		"illumos/BSD Compatibility Package Commands")
+LINE("1B",		"BSD Compatibility Package Commands")
 LINE("1C",		"Communication Commands")
-LINE("1c",		"Communication Commands")
-LINE("1F",		"FMLI Commands")
-LINE("1f",		"FMLI Commands")
-LINE("1G",		"Graphics and CAD Commands")
-LINE("1g",		"Graphics and CAD Commands")
 LINE("1HAS",		"User Commands")
-LINE("1has",		"User Commands")
 LINE("1M",		"Maintenance Commands")
-LINE("1m",		"Maintenance Commands")
 LINE("1S",		"illumos Specific Commands")
-LINE("1s",		"illumos Specific Commands")
 LINE("2",		"System Calls")
 LINE("3",		"Introduction to Library Functions")
-LINE("3AIO",		"Asynchronous I/O Library Functions")
-LINE("3aio",		"Asynchronous I/O Library Functions")
+LINE("3BSDMALLOC",	"BSD Memory Allocation Library")
 LINE("3BSM",		"Security and Auditing Library Functions")
-LINE("3bsm",		"Security and Auditing Library Functions")
 LINE("3C",		"Standard C Library Functions")
-LINE("3c",		"Standard C Library Functions")
+LINE("3C++",		"C++ Library Functions")
 LINE("3C_DB",		"Threads Debugging Library Functions")
-LINE("3C_db",		"Threads Debugging Library Functions")
 LINE("3CFGADM",		"Configuration Administration Library Functions")
-LINE("3cfgadm",		"Configuration Administration Library Functions")
-LINE("3COMPPUTIL",	"Communication Protocol Parser Utilities Library Functions")
-LINE("3compputil",	"Communication Protocol Parser Utilities Library Functions")
+LINE("3COMMPUTIL",	"Communication Protocol Parser Utilities Library Functions")
 LINE("3CONTRACT",	"Contract Management Library Functions")
-LINE("3contract",	"Contract Management Library Functions")
 LINE("3CPC",		"CPU Performance Counters Library Functions")
-LINE("3cpc",		"CPU Performance Counters Library Functions")
 LINE("3CURSES",		"Curses Library Functions")
-LINE("3curses",		"Curses Library Functions")
 LINE("3DAT",		"Direct Access Transport Library Functions")
-LINE("3dat",		"Direct Access Transport Library Functions")
 LINE("3DEVID",		"Device ID Library Functions")
-LINE("3devid",		"Device ID Library Functions")
 LINE("3DEVINFO",	"Device Information Library Functions")
-LINE("3devinfo",	"Device Information Library Functions")
-LINE("3DL",		"Dynamic Linking Library Functions")
-LINE("3dl",		"Dynamic Linking Library Functions")
 LINE("3DLPI",		"Data Link Provider Interface Library Functions")
-LINE("3dlpi",		"Data Link Provider Interface Library Functions")
-LINE("3DMI",		"DMI Library Functions")
-LINE("3dmi",		"DMI Library Functions")
 LINE("3DNS_SD",		"DNS Service Discovery Library Functions")
-LINE("3dns_sd",		"DNS Service Discovery Library Functions")
 LINE("3DOOR",		"Door Library Functions")
-LINE("3door",		"Door Library Functions")
 LINE("3ELF",		"ELF Library Functions")
-LINE("3elf",		"ELF Library Functions")
 LINE("3EXACCT",		"Extended Accounting File Access Library Functions")
-LINE("3exacct",		"Extended Accounting File Access Library Functions")
 LINE("3EXT",		"Extended Library Functions")
-LINE("3ext",		"Extended Library Functions")
 LINE("3FCOE",		"FCoE Port Management Library Functions")
-LINE("3fcoe",		"FCoE Port Management Library Functions")
 LINE("3FSTYP",		"File System Type Identification Library Functions")
-LINE("3fstyp",		"File System Type Identification Library Functions")
 LINE("3GEN",		"String Pattern-Matching Library Functions")
-LINE("3gen",		"String Pattern-Matching Library Functions")
 LINE("3GSS",		"Generic Security Services API Library Functions")
-LINE("3gss",		"Generic Security Services API Library Functions")
 LINE("3HEAD",		"Headers")
-LINE("3head",		"Headers")
 LINE("3ISCSIT",		"iSCSI Management Library Functions")
-LINE("3iscsit",		"iSCSI Management Library Functions")
 LINE("3KRB",		"Kerberos Library Functions")
-LINE("3krb",		"Kerberos Library Functions")
+LINE("3KRB5",		"MIT Kerberos 5 Library Functions")
 LINE("3KSTAT",		"Kernel Statistics Library Functions")
-LINE("3kstat",		"Kernel Statistics Library Functions")
 LINE("3KVM",		"Kernel VM Library Functions")
-LINE("3kvm",		"Kernel VM Library Functions")
 LINE("3LDAP",		"LDAP Library Functions")
-LINE("3ldap",		"LDAP Library Functions")
 LINE("3LGRP",		"Locality Group Library Functions")
-LINE("3lgrp",		"Locality Group Library Functions")
 LINE("3LIB",		"Interface Libraries")
-LINE("3lib",		"Interface Libraries")
-LINE("3LIBUCB",		"illumos/BSD Compatibility Interface Libraries")
-LINE("3libucb",		"illumos/BSD Compatibility Interface Libraries")
 LINE("3M",		"Mathematical Library Functions")
-LINE("3m",		"Mathematical Library Functions")
 LINE("3MAIL",		"User Mailbox Library Functions")
-LINE("3mail",		"User Mailbox Library Functions")
 LINE("3MALLOC",		"Memory Allocation Library Functions")
-LINE("3malloc",		"Memory Allocation Library Functions")
 LINE("3MP",		"Multiple Precision Library Functions")
-LINE("3mp",		"Multiple Precision Library Functions")
 LINE("3MPAPI",		"Common Multipath Management Library Functions")
-LINE("3mpapi",		"Common Multipath Management Library Functions")
 LINE("3NSL",		"Networking Services Library Functions")
-LINE("3nsl",		"Networking Services Library Functions")
 LINE("3NVPAIR",		"Name-value Pair Library Functions")
-LINE("3nvpair",		"Name-value Pair Library Functions")
 LINE("3PAM",		"PAM Library Functions")
-LINE("3pam",		"PAM Library Functions")
 LINE("3PAPI",		"PAPI Library Functions")
-LINE("3papi",		"PAPI Library Functions")
 LINE("3PERL",		"Perl Library Functions")
-LINE("3perl",		"Perl Library Functions")
 LINE("3PICL",		"PICL Library Functions")
-LINE("3picl",		"PICL Library Functions")
 LINE("3PICLTREE",	"PICL Plug-In Library Functions")
-LINE("3picltree",	"PICL Plug-In Library Functions")
-LINE("3PLOT",		"Graphics Interface Library Functions")
-LINE("3plot",		"Graphics Interface Library Functions")
 LINE("3POOL",		"Pool Configuration Manipulation Library Functions")
-LINE("3pool",		"Pool Configuration Manipulation Library Functions")
 LINE("3PROC",		"Process Control Library Functions")
-LINE("3proc",		"Process Control Library Functions")
 LINE("3PROJECT",	"Project Database Access Library Functions")
-LINE("3project",	"Project Database Access Library Functions")
 LINE("3RAC",		"Remote Asynchronous Calls Library Functions")
-LINE("3rac",		"Remote Asynchronous Calls Library Functions")
 LINE("3RESOLV",		"Resolver Library Functions")
-LINE("3resolv",		"Resolver Library Functions")
 LINE("3RPC",		"RPC Library Functions")
-LINE("3rpc",		"RPC Library Functions")
 LINE("3RSM",		"Remote Shared Memory Library Functions")
-LINE("3rsm",		"Remote Shared Memory Library Functions")
 LINE("3RT",		"Realtime Library Functions")
-LINE("3rt",		"Realtime Library Functions")
 LINE("3SASL",		"Simple Authentication Security Layer Library Functions")
-LINE("3sasl",		"Simple Authentication Security Layer Library Functions")
 LINE("3SCF",		"Service Configuration Facility Library Functions")
-LINE("3scf",		"Service Configuration Facility Library Functions")
-LINE("3SCHED",		"LWP Scheduling Library Functions")
-LINE("3sched",		"LWP Scheduling Library Functions")
 LINE("3SEC",		"File Access Control Library Functions")
-LINE("3sec",		"File Access Control Library Functions")
 LINE("3SECDB",		"Security Attributes Database Library Functions")
-LINE("3secdb",		"Security Attributes Database Library Functions")
 LINE("3SIP",		"Session Initiation Protocol Library Functions")
-LINE("3sip",		"Session Initiation Protocol Library Functions")
 LINE("3SLP",		"Service Location Protocol Library Functions")
-LINE("3slp",		"Service Location Protocol Library Functions")
-LINE("3SNMP",		"SNMP Library Functions")
-LINE("3snmp",		"SNMP Library Functions")
 LINE("3SOCKET",		"Sockets Library Functions")
-LINE("3socket",		"Sockets Library Functions")
 LINE("3STMF",		"SCSI Target Mode Framework Library Functions")
-LINE("3stmf",		"SCSI Target Mode Framework Library Functions")
 LINE("3SYSEVENT",	"System Event Library Functions")
-LINE("3sysevent",	"System Event Library Functions")
 LINE("3TECLA",		"Interactive Command-line Input Library Functions")
-LINE("3tecla",		"Interactive Command-line Input Library Functions")
-LINE("3THR",		"Threads Library Functions")
-LINE("3thr",		"Threads Library Functions")
 LINE("3TNF",		"TNF Library Functions")
-LINE("3tnf",		"TNF Library Functions")
 LINE("3TSOL",		"Trusted Extensions Library Functions")
-LINE("3tsol",		"Trusted Extensions Library Functions")
-LINE("3UCB",		"illumos/BSD Compatibility Library Functions")
-LINE("3ucb",		"illumos/BSD Compatibility Library Functions")
 LINE("3UUID",		"Universally Unique Identifier Library Functions")
-LINE("3uuid",		"Universally Unique Identifier Library Functions")
 LINE("3VOLMGT",		"Volume Management Library Functions")
-LINE("3volmgt",		"Volume Management Library Functions")
 LINE("3XCURSES",	"X/Open Curses Library Functions")
-LINE("3xcurses",	"X/Open Curses Library Functions")
-LINE("3XFN",		"XFN Interface Library Functions")
-LINE("3xfn",		"XFN Interface Library Functions")
 LINE("3XNET",		"X/Open Networking Services Library Functions")
-LINE("3xnet",		"X/Open Networking Services Library Functions")
-LINE("3B",		"illumos/BSD Compatibility Library Functions")
-LINE("3b",		"illumos/BSD Compatibility Library Functions")
-LINE("3E",		"C Library Functions")
-LINE("3e",		"C Library Functions")
 LINE("3F",		"Fortran Library Routines")
-LINE("3f",		"Fortran Library Routines")
-LINE("3G",		"C Library Functions")
-LINE("3g",		"C Library Functions")
-LINE("3K",		"Kernel VM Library Functions")
-LINE("3k",		"Kernel VM Library Functions")
-LINE("3L",		"Lightweight Processes Library")
-LINE("3l",		"Lightweight Processes Library")
-LINE("3N",		"Network Functions")
-LINE("3n",		"Network Functions")
-LINE("3R",		"Realtime Library")
-LINE("3r",		"Realtime Library")
-LINE("3S",		"Standard I/O Functions")
-LINE("3s",		"Standard I/O Functions")
-LINE("3T",		"Threads Library")
-LINE("3t",		"Threads Library")
-LINE("3W",		"C Library Functions")
-LINE("3w",		"C Library Functions")
 LINE("3X",		"Miscellaneous Library Functions")
-LINE("3x",		"Miscellaneous Library Functions")
-LINE("3XC",		"X/Open Curses Library Functions")
-LINE("3xc",		"X/Open Curses Library Functions")
-LINE("3XN",		"X/Open Networking Services Library Functions")
-LINE("3xn",		"X/Open Networking Services Library Functions")
 LINE("4",		"File Formats")
-LINE("4B",		"illumos/BSD Compatibility Package File Formats")
-LINE("4b",		"illumos/BSD Compatibility Package File Formats")
 LINE("5",		"Standards, Environments, and Macros")
 LINE("6",		"Games and Demos")
 LINE("7",		"Device and Network Interfaces")
-LINE("7B",		"illumos/BSD Compatibility Special Files")
-LINE("7b",		"illumos/BSD Compatibility Special Files")
 LINE("7D",		"Devices")
-LINE("7d",		"Devices")
 LINE("7FS",		"File Systems")
-LINE("7fs",		"File Systems")
 LINE("7I",		"Ioctl Requests")
-LINE("7i",		"Ioctl Requests")
 LINE("7IPP",		"IP Quality of Service Modules")
-LINE("7ipp",		"IP Quality of Service Modules")
 LINE("7M",		"STREAMS Modules")
-LINE("7m",		"STREAMS Modules")
 LINE("7P",		"Protocols")
-LINE("7p",		"Protocols")
 LINE("8",		"Maintenance Procedures")
-LINE("8C",		"Maintenance Procedures")
-LINE("8c",		"Maintenance Procedures")
-LINE("8S",		"Maintenance Procedures")
-LINE("8s",		"Maintenance Procedures")
 LINE("9",		"Device Driver Interfaces")
 LINE("9E",		"Driver Entry Points")
-LINE("9e",		"Driver Entry Points")
 LINE("9F",		"Kernel Functions for Drivers")
-LINE("9f",		"Kernel Functions for Drivers")
 LINE("9P",		"Kernel Properties for Drivers")
-LINE("9p",		"Kernel Properties for Drivers")
 LINE("9S",		"Data Structures for Drivers")
-LINE("9s",		"Data Structures for Drivers")
--- old/usr/src/cmd/mandoc/out.c	Fri Jul 25 15:00:15 2014
+++ new/usr/src/cmd/mandoc/out.c	Fri Jul 25 15:00:15 2014
@@ -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 <kristaps@bsd.lv>
  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@@ -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 @@
 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,10 +153,8 @@
 
 	assert(NULL == tbl->cols);
 	tbl->cols = mandoc_calloc
-		((size_t)sp->tbl->cols, sizeof(struct roffcol));
+		((size_t)sp->opts->cols, sizeof(struct roffcol));
 
-	hp = sp->head;
-
 	for ( ; sp; sp = sp->next) {
 		if (TBL_SPAN_DATA != sp->pos)
 			continue;
@@ -175,33 +172,14 @@
 				continue;
 			assert(dp->layout);
 			col = &tbl->cols[dp->layout->head->ident];
-			tblcalc_data(tbl, col, sp->tbl, dp);
+			tblcalc_data(tbl, col, sp->opts, 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;
-		}
-	}
 }
 
 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_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 @@
 
 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 @@
 
 	/* 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];
--- old/usr/src/cmd/mandoc/preconv.c	Fri Jul 25 15:00:15 2014
+++ new/usr/src/cmd/mandoc/preconv.c	Fri Jul 25 15:00:15 2014
@@ -1,4 +1,4 @@
-/*	$Id: preconv.c,v 1.5 2011/07/24 18:15:14 kristaps Exp $ */
+/*	$Id: preconv.c,v 1.6 2013/06/02 03:52:21 schwarze Exp $ */
 /*
  * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  *
@@ -32,13 +32,9 @@
 
 /* 
  * The read_whole_file() and resize_buf() functions are copied from
- * read.c, including all dependency code (MAP_FILE, etc.).
+ * read.c, including all dependency code.
  */
 
-#ifndef MAP_FILE
-#define	MAP_FILE	0
-#endif
-
 enum	enc {
 	ENC_UTF_8, /* UTF-8 */
 	ENC_US_ASCII, /* US-ASCII */
@@ -271,8 +267,7 @@
 	if (S_ISREG(st.st_mode)) {
 		*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);
 	}
--- old/usr/src/cmd/mandoc/predefs.in	Fri Jul 25 15:00:15 2014
+++ new/usr/src/cmd/mandoc/predefs.in	Fri Jul 25 15:00:15 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -26,7 +26,7 @@
  */
 
 PREDEF("Am", "&")
-PREDEF("Ba", "|")
+PREDEF("Ba", "\\fR|\\fP")
 PREDEF("Ge", "\\(>=")
 PREDEF("Gt", ">")
 PREDEF("If", "infinity")
--- old/usr/src/cmd/mandoc/read.c	Fri Jul 25 15:00:15 2014
+++ new/usr/src/cmd/mandoc/read.c	Fri Jul 25 15:00:15 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 	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 @@
 	"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 @@
 	".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 @@
 	"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 @@
 	"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 @@
 	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 @@
 
 	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 @@
 				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 @@
 				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 @@
 			/* 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,11 +399,21 @@
 				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 @@
 			 */
 			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 @@
 		}
 		*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 @@
 }
 
 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 @@
 	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 @@
 		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 @@
 
 	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 @@
 
 	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 @@
 	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);
 }
 
--- old/usr/src/cmd/mandoc/roff.c	Fri Jul 25 15:00:15 2014
+++ new/usr/src/cmd/mandoc/roff.c	Fri Jul 25 15:00:15 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 <assert.h>
 #include <ctype.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -39,11 +40,14 @@
 	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 @@
 	ROFF_so,
 	ROFF_ta,
 	ROFF_tr,
+	ROFF_Dd,
+	ROFF_TH,
 	ROFF_TS,
 	ROFF_TE,
 	ROFF_T_,
@@ -70,21 +76,11 @@
 };
 
 enum	roffrule {
-	ROFFRULE_ALLOW,
-	ROFFRULE_DENY
+	ROFFRULE_DENY,
+	ROFFRULE_ALLOW
 };
 
 /*
- * 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 */
-};
-
-/*
  * An incredibly-simple string buffer.
  */
 struct	roffstr {
@@ -101,12 +97,23 @@
 	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_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_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 @@
 				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 @@
 	{ "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 @@
 	{ "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 @@
 	{ 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 @@
 /* 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 @@
 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 @@
 
 	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_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 @@
 
 
 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 @@
 }
 
 /*
- * 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 @@
 		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 @@
 		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 @@
 
 		/*
 		 * 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 @@
 
 		/* 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 @@
 		 * 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 @@
 		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 @@
 }
 
 /*
- * 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 @@
 		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 @@
 		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 @@
 	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 @@
 		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->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 + 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)
-		return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
+		return(roff_parsetext(bufp, szp, pos, offs));
+	}
 
 	/*
 	 * If a scope is open, go to the child handler for that macro,
@@ -778,7 +861,7 @@
 {
 
 	while (r->last) {
-		if (--r->last->endspan < 0)
+		if (--r->last->endspan != 0)
 			break;
 		roffnode_pop(r);
 	}
@@ -984,57 +1067,45 @@
 
 	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;
+	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));
+	}
 
-			/*
-			 * 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.
-			 */
+	/* Always check for the closing delimiter `\}'. */
 
-			if ('\0' == *(ep + 1)) {
-				*--ep = '\0';
-				*szp -= 2;
-			} else
-				*(ep - 1) = *ep = ' ';
+	ep = &(*bufp)[pos];
+	while (NULL != (ep = strchr(ep, '\\'))) {
+		if ('}' != *(++ep))
+			continue;
 
-			roff_ccond(r, ROFF_ccond, bufp, szp, 
-					ln, pos, pos + 2, offs);
-			break;
-		}
-		return(ROFFRULE_DENY == rr ? ROFF_IGN : ROFF_CONT);
-	}
+		/*
+		 * 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.
+		 */
 
-	/*
-	 * 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.
-	 */
+		if ('\0' == *(ep + 1)) {
+			*--ep = '\0';
+			*szp -= 2;
+		} else
+			*(ep - 1) = *ep = ' ';
 
-	if (ROFFRULE_DENY == rr)
-		if ( ! (ROFFMAC_STRUCT & roffs[t].flags))
-			if (ROFF_ccond != t)
-				return(ROFF_IGN);
-
-	assert(roffs[t].proc);
-	return((*roffs[t].proc)(r, t, bufp, szp, 
-				ln, ppos, pos, offs));
+		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 @@
 	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 @@
 	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 @@
 roff_line_ignore(ROFF_ARGS)
 {
 
-	if (ROFF_it == tok)
-		mandoc_msg(MANDOCERR_REQUEST, r->parse, ln, ppos, "it");
-
 	return(ROFF_IGN);
 }
 
@@ -1098,9 +1252,9 @@
 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
 	 * of the current rstack entry set in prior `ie' calls or
@@ -1109,32 +1263,12 @@
 	 * 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 @@
 		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 @@
 	return(ROFF_IGN);
 }
 
+void
+roff_setreg(struct roff *r, const char *name, int val, char sign)
+{
+	struct roffreg	*reg;
+
+	/* 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_regisset(const struct roff *r, enum regs reg)
+roff_getreg(const struct roff *r, const char *name)
 {
+	struct roffreg	*reg;
 
-	return(r->regs[(int)reg].set);
+	for (reg = r->regtab; reg; reg = reg->next)
+		if (0 == strcmp(name, reg->key.p))
+			return(reg->val);
+
+	return(0);
 }
 
-unsigned int
-roff_regget(const struct roff *r, enum regs reg)
+static int
+roff_getregn(const struct roff *r, const char *name, size_t len)
 {
+	struct roffreg	*reg;
 
-	return(r->regs[(int)reg].u);
+	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);
 }
 
-void
-roff_regunset(struct roff *r, enum regs reg)
+static void
+roff_freereg(struct roffreg *reg)
 {
+	struct roffreg	*old_reg;
 
-	r->regs[(int)reg].set = 0;
+	while (NULL != reg) {
+		free(reg->key.p);
+		old_reg = reg;
+		reg = reg->next;
+		free(old_reg);
+	}
 }
 
 /* ARGSUSED */
@@ -1240,19 +1431,22 @@
 {
 	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);
 }
 
@@ -1274,6 +1468,57 @@
 
 /* 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,7 +1597,7 @@
 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);
@@ -1359,19 +1604,36 @@
 		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 = t;
+	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);
+
+	return(ROFF_IGN);
+}
+
+/* ARGSUSED */
+static enum rofferr
 roff_tr(ROFF_ARGS)
 {
 	const char	*p, *first, *second;
@@ -1470,7 +1732,7 @@
 
 	/*
 	 * Collect pointers to macro argument strings
-	 * and null-terminate them.
+	 * and NUL-terminate them.
 	 */
 	cp = *bufp + pos;
 	for (i = 0; i < 9; i++)
@@ -1765,4 +2027,39 @@
 
 	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);
 }
--- old/usr/src/cmd/mandoc/st.in	Fri Jul 25 15:00:16 2014
+++ new/usr/src/cmd/mandoc/st.in	Fri Jul 25 15:00:16 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -35,16 +35,19 @@
 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.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)")
--- old/usr/src/cmd/mandoc/tbl.c	Fri Jul 25 15:00:16 2014
+++ new/usr/src/cmd/mandoc/tbl.c	Fri Jul 25 15:00:16 2014
@@ -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 <kristaps@bsd.lv>
  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@@ -72,21 +72,21 @@
 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 @@
 	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 @@
 		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 @@
 		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
--- old/usr/src/cmd/mandoc/tbl_data.c	Fri Jul 25 15:00:16 2014
+++ new/usr/src/cmd/mandoc/tbl_data.c	Fri Jul 25 15:00:16 2014
@@ -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 <kristaps@bsd.lv>
  * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
@@ -49,13 +49,11 @@
 		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 @@
 
 	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 @@
 
 	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;
 
--- old/usr/src/cmd/mandoc/tbl_html.c	Fri Jul 25 15:00:16 2014
+++ new/usr/src/cmd/mandoc/tbl_html.c	Fri Jul 25 15:00:16 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -119,20 +119,12 @@
 			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;
 	}
--- old/usr/src/cmd/mandoc/tbl_layout.c	Fri Jul 25 15:00:16 2014
+++ new/usr/src/cmd/mandoc/tbl_layout.c	Fri Jul 25 15:00:16 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2012 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 	{ '^',		 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		 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 @@
 	case (TBL_CELL_HORIZ):
 		/* FALLTHROUGH */
 	case (TBL_CELL_DHORIZ):
-		/* FALLTHROUGH */
-	case (TBL_CELL_VERT):
-		/* FALLTHROUGH */
-	case (TBL_CELL_DVERT):
 		return(1);
 	default:
 		break;
@@ -214,11 +208,18 @@
 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)
 			break;
@@ -246,8 +247,6 @@
 			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,18 +269,9 @@
 
 	(*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);
 	}
@@ -288,7 +278,7 @@
 
 	/* 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 @@
 	 */
 
 	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 @@
 }
 
 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 @@
 	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.
-	 */
+	/* Re-use header. */
 
-	h = pp ? pp->head->next : tbl->first_head;
-
 	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;
-}
-
--- old/usr/src/cmd/mandoc/tbl_term.c	Fri Jul 25 15:00:16 2014
+++ new/usr/src/cmd/mandoc/tbl_term.c	Fri Jul 25 15:00:16 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011, 2012 Ingo Schwarze <schwarze@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -31,7 +31,7 @@
 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 *);
@@ -38,7 +38,7 @@
 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 @@
 	/* 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,50 +126,24 @@
 		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);
+			tbl_data(tp, sp->opts, 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);
-
 			/* 
 			 * Go to the next data cell and assign the
 			 * number of subsequent spans, if applicable.
@@ -185,7 +159,7 @@
 
 	/* 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 @@
 	 */
 
 	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 @@
 	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 @@
 	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 @@
 	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_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 @@
 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 @@
 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 @@
 }
 
 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 @@
 
 	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];
--- old/usr/src/cmd/mandoc/term.c	Fri Jul 25 15:00:17 2014
+++ new/usr/src/cmd/mandoc/term.c	Fri Jul 25 15:00:17 2014
@@ -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 <kristaps@bsd.lv>
- * Copyright (c) 2010, 2011 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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,10 +83,9 @@
  *  - 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.
+ *    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_TWOSPACE: make sure there is room for at least two space
- *    characters of padding.  Otherwise, rather break the line.
- *
  *  - 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 @@
 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 @@
 	 * 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 @@
 		 * 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 @@
 		 */
 
 		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,8 +199,17 @@
 			} 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 @@
 				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 @@
 	}
 
 	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 @@
 		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_newln(p);
 	p->viscol = 0;
-	(*p->endline)(p);
+	if (0 < p->skipvsp)
+		p->skipvsp--;
+	else
+		(*p->endline)(p);
 }
 
 void
@@ -369,7 +385,7 @@
 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 @@
 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,8 +416,6 @@
 
 	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, ' ');
@@ -407,6 +422,8 @@
 		} else
 			bufferc(p, ASCII_NBRSP);
 	}
+	if (TERMP_PREKEEP & p->flags)
+		p->flags |= TERMP_KEEP;
 
 	if ( ! (p->flags & TERMP_NONOSPACE))
 		p->flags &= ~TERMP_NOSPACE;
@@ -413,15 +430,28 @@
 	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 @@
 		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_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 @@
 	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,20 +557,27 @@
 {
 	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;
 }
 
@@ -539,11 +584,12 @@
 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,10 +597,10 @@
 	 * 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;
 	}
@@ -561,25 +607,15 @@
 
 	/* 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 @@
 	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 @@
 	 */
 
 	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,7 +674,7 @@
 						(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
@@ -634,7 +681,7 @@
 						(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 @@
 
 			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 @@
 				rhs = seq;
 				rsz = ssz;
 				break;
+			case (ESCAPE_SKIPCHAR):
+				skip = 1;
+				break;
 			default:
 				break;
 			}
@@ -668,15 +718,20 @@
 			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:
--- old/usr/src/cmd/mandoc/term.h	Fri Jul 25 15:00:17 2014
+++ new/usr/src/cmd/mandoc/term.h	Fri Jul 25 15:00:17 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2011, 2012, 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 	TERMFONT_NONE = 0,
 	TERMFONT_BOLD,
 	TERMFONT_UNDER,
+	TERMFONT_BI,
 	TERMFONT__MAX
 };
 
@@ -57,26 +59,28 @@
 	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. */
--- old/usr/src/cmd/mandoc/term_ascii.c	Fri Jul 25 15:00:17 2014
+++ new/usr/src/cmd/mandoc/term_ascii.c	Fri Jul 25 15:00:17 2014
@@ -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 <kristaps@bsd.lv>
  *
@@ -73,7 +73,6 @@
 	struct termp	*p;
 
 	p = mandoc_calloc(1, sizeof(struct termp));
-	p->enc = enc;
 
 	p->tabwidth = 5;
 	p->defrmargin = 78;
@@ -93,7 +92,7 @@
 	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;
--- old/usr/src/cmd/mandoc/tree.c	Fri Jul 25 15:00:17 2014
+++ new/usr/src/cmd/mandoc/tree.c	Fri Jul 25 15:00:17 2014
@@ -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 <kristaps@bsd.lv>
+ * Copyright (c) 2013 Ingo Schwarze <schwarze@openbsd.org>
  *
  * 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 @@
 {
 	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 @@
 			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)
