Index: vendor/mdocml/dist/config.h.pre =================================================================== --- vendor/mdocml/dist/config.h.pre (revision 275396) +++ vendor/mdocml/dist/config.h.pre (nonexistent) @@ -1,9 +0,0 @@ -#ifndef MANDOC_CONFIG_H -#define MANDOC_CONFIG_H - -#if defined(__linux__) || defined(__MINT__) -# define _GNU_SOURCE /* getsubopt(), strcasestr(), strptime() */ -#endif - -#include -#include Index: vendor/mdocml/dist/apropos.c =================================================================== --- vendor/mdocml/dist/apropos.c (revision 275396) +++ vendor/mdocml/dist/apropos.c (nonexistent) @@ -1,123 +0,0 @@ -/* $Id: apropos.c,v 1.39 2014/04/20 16:46:04 schwarze Exp $ */ -/* - * Copyright (c) 2012 Kristaps Dzonsons - * Copyright (c) 2013 Ingo Schwarze - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "manpath.h" -#include "mansearch.h" - - -int -main(int argc, char *argv[]) -{ - int ch, whatis; - struct mansearch search; - size_t i, sz; - struct manpage *res; - struct manpaths paths; - char *defpaths, *auxpaths; - char *conf_file; - char *progname; - const char *outkey; - extern char *optarg; - extern int optind; - - progname = strrchr(argv[0], '/'); - if (progname == NULL) - progname = argv[0]; - else - ++progname; - - whatis = (0 == strncmp(progname, "whatis", 6)); - - memset(&paths, 0, sizeof(struct manpaths)); - memset(&search, 0, sizeof(struct mansearch)); - - auxpaths = defpaths = NULL; - conf_file = NULL; - outkey = "Nd"; - - while (-1 != (ch = getopt(argc, argv, "C:M:m:O:S:s:"))) - switch (ch) { - case 'C': - conf_file = optarg; - break; - case 'M': - defpaths = optarg; - break; - case 'm': - auxpaths = optarg; - break; - case 'O': - outkey = optarg; - break; - case 'S': - search.arch = optarg; - break; - case 's': - search.sec = optarg; - break; - default: - goto usage; - } - - argc -= optind; - argv += optind; - - if (0 == argc) - goto usage; - - search.deftype = whatis ? TYPE_Nm : TYPE_Nm | TYPE_Nd; - search.flags = whatis ? MANSEARCH_WHATIS : 0; - - manpath_parse(&paths, conf_file, defpaths, auxpaths); - mansearch_setup(1); - ch = mansearch(&search, &paths, argc, argv, outkey, &res, &sz); - manpath_free(&paths); - - if (0 == ch) - goto usage; - - for (i = 0; i < sz; i++) { - printf("%s - %s\n", res[i].names, - NULL == res[i].output ? "" : res[i].output); - free(res[i].file); - free(res[i].names); - free(res[i].output); - } - - free(res); - mansearch_setup(0); - return(sz ? EXIT_SUCCESS : EXIT_FAILURE); -usage: - fprintf(stderr, "usage: %s [-C file] [-M path] [-m path] " - "[-O outkey] " - "[-S arch] [-s section]%s ...\n", progname, - whatis ? " name" : "\n expression"); - return(EXIT_FAILURE); -} Property changes on: vendor/mdocml/dist/apropos.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/mdocml/dist/vol.c =================================================================== --- vendor/mdocml/dist/vol.c (revision 275396) +++ vendor/mdocml/dist/vol.c (nonexistent) @@ -1,36 +0,0 @@ -/* $Id: vol.c,v 1.10 2014/03/23 11:25:26 schwarze Exp $ */ -/* - * Copyright (c) 2009 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "mdoc.h" -#include "libmdoc.h" - -#define LINE(x, y) \ - if (0 == strcmp(p, x)) return(y); - -const char * -mdoc_a2vol(const char *p) -{ - -#include "vol.in" - - return(NULL); -} Property changes on: vendor/mdocml/dist/vol.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/mdocml/dist/vol.in =================================================================== --- vendor/mdocml/dist/vol.in (revision 275396) +++ vendor/mdocml/dist/vol.in (nonexistent) @@ -1,35 +0,0 @@ -/* $Id: vol.in,v 1.6 2010/06/19 20:46:28 kristaps Exp $ */ -/* - * Copyright (c) 2009 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * This file defines volume titles for .Dt. - * - * Be sure to escape strings. - */ - -LINE("USD", "User\'s Supplementary Documents") -LINE("PS1", "Programmer\'s Supplementary Documents") -LINE("AMD", "Ancestral Manual Documents") -LINE("SMM", "System Manager\'s Manual") -LINE("URM", "User\'s Reference Manual") -LINE("PRM", "Programmer\'s Manual") -LINE("KM", "Kernel Manual") -LINE("IND", "Manual Master Index") -LINE("MMI", "Manual Master Index") -LINE("LOCAL", "Local Manual") -LINE("LOC", "Local Manual") -LINE("CON", "Contributed Software Manual") Property changes on: vendor/mdocml/dist/vol.in ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/mdocml/dist/att.in =================================================================== --- vendor/mdocml/dist/att.in (revision 275396) +++ vendor/mdocml/dist/att.in (nonexistent) @@ -1,40 +0,0 @@ -/* $Id: att.in,v 1.8 2011/07/31 17:30:33 schwarze Exp $ */ -/* - * Copyright (c) 2009 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * This file defines the AT&T versions of the .At macro. This probably - * isn't going to change. The right-hand side is the formatted string. - * - * Be sure to escape strings. - * The non-breaking blanks prevent ending an output line right before - * a number. Groff prevent line breaks at the same places. - */ - -LINE("v1", "Version\\~1 AT&T UNIX") -LINE("v2", "Version\\~2 AT&T UNIX") -LINE("v3", "Version\\~3 AT&T UNIX") -LINE("v4", "Version\\~4 AT&T UNIX") -LINE("v5", "Version\\~5 AT&T UNIX") -LINE("v6", "Version\\~6 AT&T UNIX") -LINE("v7", "Version\\~7 AT&T UNIX") -LINE("32v", "Version\\~32V AT&T UNIX") -LINE("III", "AT&T System\\~III UNIX") -LINE("V", "AT&T System\\~V UNIX") -LINE("V.1", "AT&T System\\~V Release\\~1 UNIX") -LINE("V.2", "AT&T System\\~V Release\\~2 UNIX") -LINE("V.3", "AT&T System\\~V Release\\~3 UNIX") -LINE("V.4", "AT&T System\\~V Release\\~4 UNIX") Property changes on: vendor/mdocml/dist/att.in ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/mdocml/dist/config.h.post =================================================================== --- vendor/mdocml/dist/config.h.post (revision 275396) +++ vendor/mdocml/dist/config.h.post (nonexistent) @@ -1,42 +0,0 @@ -#if !defined(__BEGIN_DECLS) -# ifdef __cplusplus -# define __BEGIN_DECLS extern "C" { -# else -# define __BEGIN_DECLS -# endif -#endif -#if !defined(__END_DECLS) -# ifdef __cplusplus -# define __END_DECLS } -# else -# define __END_DECLS -# endif -#endif - -#ifndef HAVE_FGETLN -extern char *fgetln(FILE *, size_t *); -#endif -#ifndef HAVE_GETSUBOPT -extern int getsubopt(char **, char * const *, char **); -extern char *suboptarg; -#endif -#ifndef HAVE_REALLOCARRAY -extern void *reallocarray(void *, size_t, size_t); -#endif -#ifndef HAVE_SQLITE3_ERRSTR -extern const char *sqlite3_errstr(int); -#endif -#ifndef HAVE_STRCASESTR -extern char *strcasestr(const char *, const char *); -#endif -#ifndef HAVE_STRLCAT -extern size_t strlcat(char *, const char *, size_t); -#endif -#ifndef HAVE_STRLCPY -extern size_t strlcpy(char *, const char *, size_t); -#endif -#ifndef HAVE_STRSEP -extern char *strsep(char **, const char *); -#endif - -#endif /* MANDOC_CONFIG_H */ Index: vendor/mdocml/dist/arch.c =================================================================== --- vendor/mdocml/dist/arch.c (revision 275396) +++ vendor/mdocml/dist/arch.c (nonexistent) @@ -1,37 +0,0 @@ -/* $Id: arch.c,v 1.11 2014/04/20 16:46:04 schwarze Exp $ */ -/* - * Copyright (c) 2009 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include - -#include "mdoc.h" -#include "libmdoc.h" - -#define LINE(x, y) \ - if (0 == strcmp(p, x)) return(y); - - -const char * -mdoc_a2arch(const char *p) -{ - -#include "arch.in" - - return(NULL); -} Property changes on: vendor/mdocml/dist/arch.c ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/mdocml/dist/arch.in =================================================================== --- vendor/mdocml/dist/arch.in (revision 275396) +++ vendor/mdocml/dist/arch.in (nonexistent) @@ -1,112 +0,0 @@ -/* $Id: arch.in,v 1.15 2014/04/27 22:42:15 schwarze Exp $ */ -/* - * Copyright (c) 2009 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* - * This file defines the architecture token of the .Dt prologue macro. - * All architectures that your system supports (or the manuals of your - * system) should be included here. The right-hand-side is the - * formatted output. - * - * Be sure to escape strings. - * - * REMEMBER TO ADD NEW ARCHITECTURES TO MDOC.7! - */ - -LINE("acorn26", "Acorn26") -LINE("acorn32", "Acorn32") -LINE("algor", "Algor") -LINE("alpha", "Alpha") -LINE("amd64", "AMD64") -LINE("amiga", "Amiga") -LINE("amigappc", "AmigaPPC") -LINE("arc", "ARC") -LINE("arm", "ARM") -LINE("arm26", "ARM26") -LINE("arm32", "ARM32") -LINE("armish", "ARMISH") -LINE("armv7", "ARMv7") -LINE("aviion", "AViiON") -LINE("atari", "ATARI") -LINE("bebox", "BeBox") -LINE("cats", "cats") -LINE("cesfic", "CESFIC") -LINE("cobalt", "Cobalt") -LINE("dreamcast", "Dreamcast") -LINE("emips", "EMIPS") -LINE("evbarm", "evbARM") -LINE("evbmips", "evbMIPS") -LINE("evbppc", "evbPPC") -LINE("evbsh3", "evbSH3") -LINE("ews4800mips", "EWS4800MIPS") -LINE("hp300", "HP300") -LINE("hp700", "HP700") -LINE("hpcarm", "HPCARM") -LINE("hpcmips", "HPCMIPS") -LINE("hpcsh", "HPCSH") -LINE("hppa", "HPPA") -LINE("hppa64", "HPPA64") -LINE("ia64", "ia64") -LINE("i386", "i386") -LINE("ibmnws", "IBMNWS") -LINE("iyonix", "Iyonix") -LINE("landisk", "LANDISK") -LINE("loongson", "Loongson") -LINE("luna68k", "LUNA68K") -LINE("luna88k", "LUNA88K") -LINE("m68k", "m68k") -LINE("mac68k", "Mac68k") -LINE("macppc", "MacPPC") -LINE("mips", "MIPS") -LINE("mips64", "MIPS64") -LINE("mipsco", "MIPSCo") -LINE("mmeye", "mmEye") -LINE("mvme68k", "MVME68k") -LINE("mvme88k", "MVME88k") -LINE("mvmeppc", "MVMEPPC") -LINE("netwinder", "NetWinder") -LINE("news68k", "NeWS68k") -LINE("newsmips", "NeWSMIPS") -LINE("next68k", "NeXT68k") -LINE("octeon", "OCTEON") -LINE("ofppc", "OFPPC") -LINE("palm", "Palm") -LINE("pc532", "PC532") -LINE("playstation2", "PlayStation2") -LINE("pmax", "PMAX") -LINE("pmppc", "pmPPC") -LINE("powerpc", "PowerPC") -LINE("prep", "PReP") -LINE("rs6000", "RS6000") -LINE("sandpoint", "Sandpoint") -LINE("sbmips", "SBMIPS") -LINE("sgi", "SGI") -LINE("sgimips", "SGIMIPS") -LINE("sh3", "SH3") -LINE("shark", "Shark") -LINE("socppc", "SOCPPC") -LINE("solbourne", "Solbourne") -LINE("sparc", "SPARC") -LINE("sparc64", "SPARC64") -LINE("sun2", "Sun2") -LINE("sun3", "Sun3") -LINE("tahoe", "Tahoe") -LINE("vax", "VAX") -LINE("x68k", "X68k") -LINE("x86", "x86") -LINE("x86_64", "x86_64") -LINE("xen", "Xen") -LINE("zaurus", "Zaurus") Property changes on: vendor/mdocml/dist/arch.in ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/mdocml/dist/preconv.1 =================================================================== --- vendor/mdocml/dist/preconv.1 (revision 275396) +++ vendor/mdocml/dist/preconv.1 (nonexistent) @@ -1,157 +0,0 @@ -.\" $Id: preconv.1,v 1.7 2013/07/13 19:41:16 schwarze Exp $ -.\" -.\" Copyright (c) 2011 Kristaps Dzonsons -.\" -.\" Permission to use, copy, modify, and distribute this software for any -.\" purpose with or without fee is hereby granted, provided that the above -.\" copyright notice and this permission notice appear in all copies. -.\" -.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -.\" -.Dd $Mdocdate: July 13 2013 $ -.Dt PRECONV 1 -.Os -.Sh NAME -.Nm preconv -.Nd recode multibyte UNIX manuals -.Sh SYNOPSIS -.Nm preconv -.Op Fl D Ar enc -.Op Fl e Ar enc -.Op Ar file -.Sh DESCRIPTION -The -.Nm -utility recodes multibyte -.Ux -manual files into -.Xr mandoc 1 -.Po -or other troff system supporting the -.Sq \e[uNNNN] -escape sequence -.Pc -input. -.Pp -By default, it parses from standard output, determining encoding as -described in -.Sx Algorithm . -.Pp -Its arguments are as follows: -.Bl -tag -width Ds -.It Fl D Ar enc -The default encoding. -.It Fl e Ar enc -The document's encoding. -.It Ar file -The input file. -.El -.Pp -The recoded input is written to standard output: Unicode characters in -the ASCII range are printed as regular ASCII characters, while those -above this range are printed using the -.Sq \e[uNNNN] -format documented in -.Xr mandoc_char 7 . -.Pp -If input bytes are improperly formed in the current encoding, they're -passed unmodified to standard output. -For some encodings, such as UTF-8, unrecoverable input sequences will -cause -.Nm -to stop processing and exit. -.Ss Algorithm -An encoding is chosen according to the following steps: -.Bl -enum -.It -From the argument passed to -.Fl e Ar enc . -.It -If a BOM exists, UTF\-8 encoding is selected. -.It -From the coding tags parsed from -.Qq File Variables -on the first two lines of input. -A file variable is an input line of the form -.Pp -.Dl \%.\e\(dq -*- key: val [; key: val ]* -*- -.Pp -A coding tag variable is where -.Cm key -is -.Qq coding -and -.Cm val -is the name of the encoding. -A typical file variable with a coding tag is -.Pp -.Dl \%.\e\(dq -*- mode: troff; coding: utf-8 -*- -.It -From the argument passed to -.Fl D Ar enc . -.It -If all else fails, Latin\-1 is used. -.El -.Pp -The -.Nm -utility recognises the UTF\-8, us\-ascii, and latin\-1 encodings as -passed to the -.Fl e -and -.Fl D -arguments, or as coding tags. -Encodings are matched case-insensitively. -.\" .Sh IMPLEMENTATION NOTES -.\" Not used in OpenBSD. -.\" .Sh RETURN VALUES -.\" For sections 2, 3, & 9 only. -.\" .Sh ENVIRONMENT -.\" For sections 1, 6, 7, & 8 only. -.\" .Sh FILES -.Sh EXIT STATUS -.Ex -std -.Sh EXAMPLES -Explicitly page a UTF\-8 manual -.Pa foo.1 -in the current locale: -.Pp -.Dl $ preconv \-e utf\-8 foo.1 | mandoc -Tlocale | less -.\" .Sh DIAGNOSTICS -.\" For sections 1, 4, 6, 7, & 8 only. -.\" .Sh ERRORS -.\" For sections 2, 3, & 9 only. -.Sh SEE ALSO -.Xr mandoc 1 , -.Xr mandoc_char 7 -.Sh STANDARDS -The -.Nm -utility references the US-ASCII character set standard, ANSI_X3.4\-1968; -the Latin\-1 character set standard, ISO/IEC 8859\-1:1998; the UTF\-8 -character set standard; and UCS (Unicode), ISO/IEC 10646. -.Sh HISTORY -The -.Nm -utility first appeared in the GNU troff -.Pq Dq groff -system in December 2005, authored by Tomohiro Kubota and Werner -Lemberg. -The implementation that is part of the -.Xr mandoc 1 -utility appeared in May 2011. -.Sh AUTHORS -The -.Nm -utility was written by -.An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . -.\" .Sh CAVEATS -.\" .Sh BUGS -.\" .Sh SECURITY CONSIDERATIONS -.\" Not used in OpenBSD. Property changes on: vendor/mdocml/dist/preconv.1 ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/mdocml/dist/INSTALL =================================================================== --- vendor/mdocml/dist/INSTALL (revision 275396) +++ vendor/mdocml/dist/INSTALL (revision 275397) @@ -1,187 +1,188 @@ -$Id: INSTALL,v 1.2 2014/08/10 17:22:26 schwarze Exp $ +$Id: INSTALL,v 1.5 2014/08/18 13:27:47 kristaps Exp $ About mdocml, the portable mandoc distribution ---------------------------------------------- The mandoc manpage compiler toolset is a suite of tools compiling mdoc(7), the roff(7) macro language of choice for BSD manual pages, and man(7), the predominant historical language for UNIX manuals. The toolset does not yet implement man(1); that is only scheduled for the next release, 1.13.2. It can, however, already serve to translate source manpages to the output displayed by man(1). For general information, see . In this document, we describe the installation and deployment of mandoc(1), first as a simple, standalone formatter, and then as part of the man(1) system. In case you have questions or want to provide feedback, read . Consider subscribing to the discuss@ mailing list mentioned on that page. If you intend to help with the development of mandoc, consider subscribing to the tech@ mailing list, too. Enjoy using the mandoc toolset! Ingo Schwarze, Karlsruhe, August 2014 Installation ------------ Before manually installing mandoc on your system, please check whether the newest version of mandoc is already installed by default or available via a binary package or a ports system. A list of the latest bundled and ported versions of mandoc for various operating systems is maintained at . If mandoc is installed, you can check the version by running "mandoc -V". -The version contained in this distribution tarball is listed near -the beginning of the file "Makefile". +You can find the version contained in this distribution tarball +by running "./configure". Regarding how packages and ports are maintained for your operating system, please consult your operating system documentation. To install mandoc manually, the following steps are needed: -1. Decide whether you want to build the base tools mandoc(1), -preconv(1) and demandoc(1) only or whether you also want to build the -database tools apropos(1) and makewhatis(8). For the latter, -the following dependencies are required: +1. If you want to build the CGI program, man.cgi(8), too, run the +command "echo BUILD_CGI=1 > configure.local". Then run "cp +cgi.h.examples cgi.h" and edit cgi.h as desired. -1.1. The SQLite database system, see . +2. Run "./configure". +This script attempts autoconfiguration of mandoc for your system. +Read both its standard output and the file "Makefile.local" it +generates. If anything looks wrong or different from what you +wish, read the file "configure.local.example", create and edit +a file "configure.local", and re-run "./configure" until the +result seems right to you. + +3. Run "make". +Any POSIX-compatible make, in particular both BSD make and GNU make, +should work. If the build fails, look at "configure.local.example" +and go back to step 2. + +4. Run "make -n install" and check whether everything will be +installed to the intended places. Otherwise, put some *DIR variables +into "configure.local" and go back to step 2. + +5. Run "sudo make install". If you intend to build a binary +package using some kind of fake root mechanism, you may need a +command like "make DESTDIR=... install". Read the *-install targets +in the "Makefile" to understand how DESTDIR is used. + +6. To set up a man.cgi(8) server, read its manual page. + +7. To use mandoc(1) as your man(1) formatter, read the "Deployment" +section below. + + +Understanding mandoc dependencies +--------------------------------- +The mandoc(1), preconv(1), and demandoc(1) utilities have no external +dependencies. However, makewhatis(8) and apropos(1) depend on the +following software: + +1. The SQLite database system, see . The recommended version of SQLite is 3.8.4.3 or newer. The mandoc toolset is known to work with version 3.7.5 or newer. Versions older than 3.8.3 may not achieve full performance due to the missing SQLITE_DETERMINISTIC optimization flag. Versions older than 3.8.0 may not show full error information if opening a database fails due to the missing sqlite3_errstr() API. Both are very minor problems, apropos(1) is fully usable with SQLite 3.7.5. Versions older than 3.7.5 may or may not work, they have not been tested. 1.2. The fts(3) directory traversion functions. -A compatibility version will be bundled for 1.13.2 but is not available -yet. If you want apropos(1) and makewhatis(8) but do not have fts(3), -please stay with mandoc 1.12.3 for now and upgrade first to 1.12.4, -then to 1.13.2 when these versionns are released. Be careful: the +If your system does not have them, the bundled compatibility version +will be used, so you need not worry in that case. But be careful: the glibc version of fts(3) is known to be broken on 32bit platforms, see . +If you run into that problem, set "HAVE_FTS=0" in configure.local. 1.3. Marc Espie's ohash(3) library. If your system does not have it, the bundled compatibility version will be used, so you probably need not worry about it. -2. If you choose to build the database tools, too, decide whether -you also want to build the CGI program, man.cgi(8). -3. Read the beginning of the file "Makefile" from "USER SETTINGS" -to "END OF USER SETTINGS" and edit it as required. In particular, -disable "BUILD_TARGETS += db-build" if you do not want database -support or enable "BUILD_TARGETS += cgi-build" if you do want -the CGI program. - -4. Run "make". No separate "./configure" or "make depend" steps -are needed. The former is run automatically by "make". The latter -is a maintainer target. If you merely want to build the released -version as opposed to doing active development, there is no need -to regenerate the dependency specifications. Any POSIX-compatible -make, in particular both BSD make and GNU make, should work. - -5. Run "make -n install" and check whether everything will be -installed to the intended places. Otherwise, edit the *DIR variables -in the Makefile until it is. - -6. Run "sudo make install". If you intend to build a binary -package using some kind of fake root mechanism, you may need a -command like "make DESTDIR=... install". Read the *-install targets -in the "Makefile" to understand how DESTDIR is used. - -7. To set up a man.cgi(8) server, read its manual page. - -8. To use mandoc(1) as your man(1) formatter, read the "Deployment" -section below. - - Checking autoconfiguration quality ---------------------------------- If you want to check whether automatic configuration works well on your platform, consider the following: The mandoc package intentionally does not use GNU autoconf because we consider that toolset a blatant example of overengineering that is obsolete nowadays, since all modern operating systems are now reasonably close to POSIX and do not need arcane shell magic any longer. If your system does need such magic, consider upgrading to reasonably modern POSIX-compliant tools rather than asking for autoconf-style workarounds. As far as mandoc is using any features not mandated by ANSI X3.159-1989 ("ANSI C") or IEEE Std 1003.1-2008 ("POSIX") that some modern systems do not have, we intend to provide autoconfiguration tests and compat_*.c implementations. Please report any that turn out to be missing. Note that while we do strive to produce portable code, we do not slavishly restrict ourselves to POSIX-only interfaces. For improved security and readability, we do use well-designed, modern interfaces like reallocarray(3) even if they are still rather uncommon, of course bundling compat_*.c implementations as needed. Where mandoc is using ANSI C or POSIX features that some systems still lack and that compat_*.c implementations can be provided for without too much hassle, we will consider adding them, too, so please report whatever is missing on your platform. The following steps can be used to manually check the automatic configuration on your platform: -1. Run "make clean". +1. Run "make distclean". -2. Run "make config.h" +2. Run "./configure" 3. Read the file "config.log". It shows the compiler commands used to test the libraries installed on your system and the standard output and standard error output these commands produce. Watch out for unexpected failures. Those are most likely to happen if headers or libraries are installed in unusual places or interfaces defined in unusual headers. You can also look at the file "config.h" and -check that no expected "#define HAVE_*" lines are missing. The -list of tests run can be found in the file "configure". +check that no "#define HAVE_*" differ from your expectations. Deployment ---------- If you want to integrate the mandoc(1) tools with your existing man(1) system as a formatter, then contact us first: on systems without mandoc(1) as the default, you may have your work cut out for you! Usually, you can have your default installation and mandoc(1) work right alongside each other by using user-specific versions of the files mentioned below. 0. Back up each file you want to change! 1. First see whether your system has "/etc/man.conf" or "/etc/manpath.conf" (if it has neither, but man(1) is functional, then let us know) or, if running as your own user, a per-user override file. In either case, find where man(1) is executing nroff(1) or groff(1) to format manuals. Replace these calls with mandoc(1). 2. Then make sure that man(1) isn't running preprocessors, so you may need to replace tbl(1), eqn(1), and similar references with cat(1). Some man(1) implementations, like that on Mac OSX, let you run "man -d" to see how the formatter is invoked. Use this to test your changes. On Mac OS X, for instance, man(1) will prepend all files with ".ll" and ".nr" to set the terminal size, so you need to pass "tail -n+2 | mandoc(1)" to disregard them. 3. Finally, make sure that mandoc(1) is actually being invoked instead of cached pages being pulled up. You can usually do this by commenting out NOCACHE or similar. mandoc(1) still has a long way to go in understanding non-trivial low-level roff(7) markup embedded in some man(7) pages. On the BSD systems using mandoc(1), third-party software is generally vetted on whether it may be formatted with mandoc(1). If not, groff(1) is pulled in as a dependency and used to install a pre-formatted "catpage" intead of directly as manual page source. For more background on switching operating systems to use mandoc(1) instead of groff(1) to format manuals, see the two BSDCan presentations by Ingo Schwarze: Index: vendor/mdocml/dist/LICENSE =================================================================== --- vendor/mdocml/dist/LICENSE (revision 275396) +++ vendor/mdocml/dist/LICENSE (revision 275397) @@ -1,44 +1,46 @@ -$Id: LICENSE,v 1.2 2014/04/23 21:06:41 schwarze Exp $ +$Id: LICENSE,v 1.4 2014/08/21 00:42:38 schwarze Exp $ With the exceptions noted below, all code and documentation contained in the mdocml toolkit is protected by the Copyright of the following developers: Copyright (c) 2008, 2009, 2010, 2011, 2012 Kristaps Dzonsons Copyright (c) 2010, 2011, 2012, 2013, 2014 Ingo Schwarze Copyright (c) 2009, 2010, 2011, 2012 Joerg Sonnenberger Copyright (c) 2013 Franco Fichtner Copyright (c) 1999, 2004 Marc Espie Copyright (c) 1998, 2010 Todd C. Miller Copyright (c) 2008 Otto Moerbeek Copyright (c) 2003 Jason McIntyre See the individual source files for information about who contributed to which file during which years. The mdocml distribution as a whole is distributed by its developers under the following license: Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The following files included from outside sources are protected by other people's Copyright and are distributed under a 3-clause BSD license; see these individual files for details. -compat_getsubopt.c, compat_strcasestr.c, compat_strsep.c: -Copyright (c) 1990, 1993 The Regents of the University of California +compat_fts.c, compat_fts.h, +compat_getsubopt.c, compat_strcasestr.c, compat_strsep.c, +man.1: +Copyright (c) 1989,1990,1993,1994 The Regents of the University of California compat_fgetln.c: Copyright (c) 1998 The NetBSD Foundation, Inc. Index: vendor/mdocml/dist/Makefile =================================================================== --- vendor/mdocml/dist/Makefile (revision 275396) +++ vendor/mdocml/dist/Makefile (revision 275397) @@ -1,505 +1,407 @@ -# $Id: Makefile,v 1.435 2014/08/10 02:45:04 schwarze Exp $ +# $Id: Makefile,v 1.448 2014/11/28 18:57:31 schwarze Exp $ # # Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons # Copyright (c) 2011, 2013, 2014 Ingo Schwarze # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -VERSION = 1.13.1 - -# === USER SETTINGS ==================================================== - -# --- user settings relevant for all builds ---------------------------- - -# Specify this if you want to hard-code the operating system to appear -# in the lower-left hand corner of -mdoc manuals. -# -# CFLAGS += -DOSNAME="\"OpenBSD 5.5\"" - -# IFF your system supports multi-byte functions (setlocale(), wcwidth(), -# putwchar()) AND has __STDC_ISO_10646__ (that is, wchar_t is simply a -# UCS-4 value) should you define USE_WCHAR. If you define it and your -# system DOESN'T support this, -Tlocale will produce garbage. -# If you don't define it, -Tlocale is a synonym for -Tacsii. -# -CFLAGS += -DUSE_WCHAR - -CFLAGS += -g -DHAVE_CONFIG_H -CFLAGS += -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings -PREFIX = /usr/local -BINDIR = $(PREFIX)/bin -INCLUDEDIR = $(PREFIX)/include/mandoc -LIBDIR = $(PREFIX)/lib/mandoc -MANDIR = $(PREFIX)/man -EXAMPLEDIR = $(PREFIX)/share/examples/mandoc - -INSTALL = install -INSTALL_PROGRAM = $(INSTALL) -m 0555 -INSTALL_DATA = $(INSTALL) -m 0444 -INSTALL_LIB = $(INSTALL) -m 0444 -INSTALL_SOURCE = $(INSTALL) -m 0644 -INSTALL_MAN = $(INSTALL_DATA) - -# --- user settings related to database support ------------------------ - -# Building apropos(1) and makewhatis(8) requires both SQLite3 and fts(3). -# To avoid those dependencies, comment the following line. -# Be careful: the fts(3) implementation in glibc is broken on 32bit -# machines, see: https://sourceware.org/bugzilla/show_bug.cgi?id=15838 -# -BUILD_TARGETS += db-build - -# The remaining settings in this section -# are only relevant if db-build is enabled. -# Otherwise, they have no effect either way. - -# If your system has manpath(1), uncomment this. This is most any -# system that's not OpenBSD or NetBSD. If uncommented, apropos(1) -# and makewhatis(8) will use manpath(1) to get the MANPATH variable. -# -#CFLAGS += -DUSE_MANPATH - -# On some systems, SQLite3 may be installed below /usr/local. -# In that case, uncomment the following two lines. -# -#CFLAGS += -I/usr/local/include -#DBLIB += -L/usr/local/lib - -# OpenBSD has the ohash functions in libutil. -# Comment the following line if your system doesn't. -# -DBLIB += -lutil - -SBINDIR = $(PREFIX)/sbin - -# --- user settings related to man.cgi --------------------------------- - -# To build man.cgi, copy cgi.h.example to cgi.h, edit it, -# and enable the following line. -# Obviously, this requires that db-build is enabled, too. -# -#BUILD_TARGETS += cgi-build - -# The remaining settings in this section -# are only relevant if cgi-build is enabled. -# Otherwise, they have no effect either way. - -# If your system does not support static binaries, comment this, -# for example on Mac OS X. -# -STATIC = -static - -# Linux requires -pthread for statical linking. -# -#STATIC += -pthread - -WWWPREFIX = /var/www -HTDOCDIR = $(WWWPREFIX)/htdocs -CGIBINDIR = $(WWWPREFIX)/cgi-bin - -# === END OF USER SETTINGS ============================================= - -INSTALL_TARGETS = $(BUILD_TARGETS:-build=-install) - -BASEBIN = mandoc preconv demandoc -DBBIN = apropos makewhatis +BASEBIN = mandoc demandoc +DBBIN = makewhatis CGIBIN = man.cgi -DBLIB += -lsqlite3 - -TESTSRCS = test-fgetln.c \ +TESTSRCS = test-dirent-namlen.c \ + test-fgetln.c \ + test-fts.c \ test-getsubopt.c \ test-mmap.c \ test-ohash.c \ test-reallocarray.c \ + test-sqlite3.c \ test-sqlite3_errstr.c \ test-strcasestr.c \ test-strlcat.c \ test-strlcpy.c \ test-strptime.c \ - test-strsep.c + test-strsep.c \ + test-wchar.c -SRCS = apropos.c \ - arch.c \ - att.c \ +SRCS = att.c \ cgi.c \ chars.c \ compat_fgetln.c \ + compat_fts.c \ compat_getsubopt.c \ compat_ohash.c \ compat_reallocarray.c \ compat_sqlite3_errstr.c \ compat_strcasestr.c \ compat_strlcat.c \ compat_strlcpy.c \ compat_strsep.c \ demandoc.c \ eqn.c \ eqn_html.c \ eqn_term.c \ html.c \ lib.c \ main.c \ man.c \ man_hash.c \ man_html.c \ man_macro.c \ man_term.c \ man_validate.c \ mandoc.c \ mandoc_aux.c \ mandocdb.c \ manpage.c \ manpath.c \ mansearch.c \ mansearch_const.c \ mdoc.c \ mdoc_argv.c \ mdoc_hash.c \ mdoc_html.c \ mdoc_macro.c \ mdoc_man.c \ mdoc_term.c \ mdoc_validate.c \ msec.c \ out.c \ preconv.c \ read.c \ roff.c \ st.c \ tbl.c \ tbl_data.c \ tbl_html.c \ tbl_layout.c \ tbl_opts.c \ tbl_term.c \ term.c \ term_ascii.c \ term_ps.c \ tree.c \ - vol.c \ $(TESTSRCS) DISTFILES = INSTALL \ LICENSE \ Makefile \ Makefile.depend \ NEWS \ TODO \ apropos.1 \ - arch.in \ - att.in \ cgi.h.example \ chars.in \ + compat_fts.h \ compat_ohash.h \ - config.h.post \ - config.h.pre \ configure \ + configure.local.example \ demandoc.1 \ eqn.7 \ example.style.css \ gmdiff \ html.h \ lib.in \ libman.h \ libmandoc.h \ libmdoc.h \ libroff.h \ main.h \ makewhatis.8 \ man-cgi.css \ + man.1 \ man.7 \ man.cgi.8 \ man.h \ mandoc.1 \ mandoc.3 \ mandoc.db.5 \ mandoc.h \ mandoc_aux.h \ mandoc_char.7 \ mandoc_escape.3 \ mandoc_html.3 \ mandoc_malloc.3 \ manpath.h \ mansearch.3 \ mansearch.h \ mchars_alloc.3 \ mdoc.7 \ mdoc.h \ msec.in \ out.h \ - preconv.1 \ predefs.in \ roff.7 \ st.in \ style.css \ tbl.3 \ tbl.7 \ term.h \ - vol.in \ $(SRCS) LIBMAN_OBJS = man.o \ man_hash.o \ man_macro.o \ man_validate.o -LIBMDOC_OBJS = arch.o \ - att.o \ +LIBMDOC_OBJS = att.o \ lib.o \ mdoc.o \ mdoc_argv.o \ mdoc_hash.o \ mdoc_macro.o \ mdoc_validate.o \ - st.o \ - vol.o + st.o LIBROFF_OBJS = eqn.o \ roff.o \ tbl.o \ tbl_data.o \ tbl_layout.o \ tbl_opts.o LIBMANDOC_OBJS = $(LIBMAN_OBJS) \ $(LIBMDOC_OBJS) \ $(LIBROFF_OBJS) \ chars.o \ mandoc.o \ mandoc_aux.o \ msec.o \ + preconv.o \ read.o COMPAT_OBJS = compat_fgetln.o \ + compat_fts.o \ compat_getsubopt.o \ compat_ohash.o \ compat_reallocarray.o \ compat_sqlite3_errstr.o \ compat_strcasestr.o \ compat_strlcat.o \ compat_strlcpy.o \ compat_strsep.o MANDOC_HTML_OBJS = eqn_html.o \ html.o \ man_html.o \ mdoc_html.o \ tbl_html.o MANDOC_MAN_OBJS = mdoc_man.o MANDOC_TERM_OBJS = eqn_term.o \ man_term.o \ mdoc_term.o \ term.o \ term_ascii.o \ term_ps.o \ tbl_term.o MANDOC_OBJS = $(MANDOC_HTML_OBJS) \ $(MANDOC_MAN_OBJS) \ $(MANDOC_TERM_OBJS) \ main.o \ out.o \ tree.o +MAN_OBJS = $(MANDOC_OBJS) + MAKEWHATIS_OBJS = mandocdb.o mansearch_const.o manpath.o -PRECONV_OBJS = preconv.o +APROPOS_OBJS = mansearch.o mansearch_const.o manpath.o -APROPOS_OBJS = apropos.o mansearch.o mansearch_const.o manpath.o - CGI_OBJS = $(MANDOC_HTML_OBJS) \ cgi.o \ mansearch.o \ mansearch_const.o \ out.o MANPAGE_OBJS = manpage.o mansearch.o mansearch_const.o manpath.o DEMANDOC_OBJS = demandoc.o WWW_MANS = apropos.1.html \ demandoc.1.html \ + man.1.html \ mandoc.1.html \ - preconv.1.html \ mandoc.3.html \ mandoc_escape.3.html \ mandoc_html.3.html \ mandoc_malloc.3.html \ mansearch.3.html \ mchars_alloc.3.html \ tbl.3.html \ mandoc.db.5.html \ eqn.7.html \ man.7.html \ mandoc_char.7.html \ mdoc.7.html \ roff.7.html \ tbl.7.html \ makewhatis.8.html \ man.cgi.8.html \ man.h.html \ mandoc.h.html \ mandoc_aux.h.html \ manpath.h.html \ mansearch.h.html \ mdoc.h.html WWW_OBJS = mdocml.tar.gz \ mdocml.sha256 +include Makefile.local + +INSTALL_TARGETS = $(BUILD_TARGETS:-build=-install) + # === DEPENDENCY HANDLING ============================================== -all: base-build $(BUILD_TARGETS) +all: base-build $(BUILD_TARGETS) Makefile.local base-build: $(BASEBIN) db-build: $(DBBIN) cgi-build: $(CGIBIN) install: base-install $(INSTALL_TARGETS) www: $(WWW_OBJS) $(WWW_MANS) +$(WWW_MANS): mandoc + include Makefile.depend # === TARGETS CONTAINING SHELL COMMANDS ================================ +distclean: clean + rm -f Makefile.local config.h config.h.old config.log config.log.old + clean: - rm -f libmandoc.a $(LIBMANDOC_OBJS) - rm -f apropos $(APROPOS_OBJS) + rm -f libmandoc.a $(LIBMANDOC_OBJS) $(COMPAT_OBJS) + rm -f mandoc $(MANDOC_OBJS) $(APROPOS_OBJS) rm -f makewhatis $(MAKEWHATIS_OBJS) - rm -f preconv $(PRECONV_OBJS) rm -f man.cgi $(CGI_OBJS) rm -f manpage $(MANPAGE_OBJS) rm -f demandoc $(DEMANDOC_OBJS) - rm -f mandoc $(MANDOC_OBJS) - rm -f config.h config.log $(COMPAT_OBJS) rm -f $(WWW_MANS) $(WWW_OBJS) rm -rf *.dSYM base-install: base-build mkdir -p $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(EXAMPLEDIR) mkdir -p $(DESTDIR)$(LIBDIR) mkdir -p $(DESTDIR)$(INCLUDEDIR) mkdir -p $(DESTDIR)$(MANDIR)/man1 mkdir -p $(DESTDIR)$(MANDIR)/man3 mkdir -p $(DESTDIR)$(MANDIR)/man7 $(INSTALL_PROGRAM) $(BASEBIN) $(DESTDIR)$(BINDIR) $(INSTALL_LIB) libmandoc.a $(DESTDIR)$(LIBDIR) $(INSTALL_LIB) man.h mandoc.h mandoc_aux.h mdoc.h \ $(DESTDIR)$(INCLUDEDIR) - $(INSTALL_MAN) mandoc.1 preconv.1 demandoc.1 $(DESTDIR)$(MANDIR)/man1 + $(INSTALL_MAN) man.1 mandoc.1 demandoc.1 \ + $(DESTDIR)$(MANDIR)/man1 $(INSTALL_MAN) mandoc.3 mandoc_escape.3 mandoc_malloc.3 \ mchars_alloc.3 tbl.3 $(DESTDIR)$(MANDIR)/man3 $(INSTALL_MAN) man.7 mdoc.7 roff.7 eqn.7 tbl.7 mandoc_char.7 \ $(DESTDIR)$(MANDIR)/man7 $(INSTALL_DATA) example.style.css $(DESTDIR)$(EXAMPLEDIR) db-install: db-build mkdir -p $(DESTDIR)$(BINDIR) mkdir -p $(DESTDIR)$(SBINDIR) mkdir -p $(DESTDIR)$(MANDIR)/man1 mkdir -p $(DESTDIR)$(MANDIR)/man3 mkdir -p $(DESTDIR)$(MANDIR)/man5 mkdir -p $(DESTDIR)$(MANDIR)/man8 - $(INSTALL_PROGRAM) apropos $(DESTDIR)$(BINDIR) - ln -f $(DESTDIR)$(BINDIR)/apropos $(DESTDIR)$(BINDIR)/whatis + ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/apropos + ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/whatis $(INSTALL_PROGRAM) makewhatis $(DESTDIR)$(SBINDIR) $(INSTALL_MAN) apropos.1 $(DESTDIR)$(MANDIR)/man1 ln -f $(DESTDIR)$(MANDIR)/man1/apropos.1 \ $(DESTDIR)$(MANDIR)/man1/whatis.1 $(INSTALL_MAN) mansearch.3 $(DESTDIR)$(MANDIR)/man3 $(INSTALL_MAN) mandoc.db.5 $(DESTDIR)$(MANDIR)/man5 $(INSTALL_MAN) makewhatis.8 $(DESTDIR)$(MANDIR)/man8 cgi-install: cgi-build mkdir -p $(DESTDIR)$(CGIBINDIR) mkdir -p $(DESTDIR)$(HTDOCDIR) mkdir -p $(DESTDIR)$(WWWPREFIX)/man/mandoc/man1 mkdir -p $(DESTDIR)$(WWWPREFIX)/man/mandoc/man8 $(INSTALL_PROGRAM) man.cgi $(DESTDIR)$(CGIBINDIR) $(INSTALL_DATA) example.style.css $(DESTDIR)$(HTDOCDIR)/man.css $(INSTALL_DATA) man-cgi.css $(DESTDIR)$(HTDOCDIR) $(INSTALL_MAN) apropos.1 $(DESTDIR)$(WWWPREFIX)/man/mandoc/man1/ $(INSTALL_MAN) man.cgi.8 $(DESTDIR)$(WWWPREFIX)/man/mandoc/man8/ www-install: www mkdir -p $(DESTDIR)$(HTDOCDIR)/snapshots $(INSTALL_DATA) $(WWW_MANS) style.css $(DESTDIR)$(HTDOCDIR) $(INSTALL_DATA) $(WWW_OBJS) $(DESTDIR)$(HTDOCDIR)/snapshots $(INSTALL_DATA) mdocml.tar.gz \ $(DESTDIR)$(HTDOCDIR)/snapshots/mdocml-$(VERSION).tar.gz $(INSTALL_DATA) mdocml.sha256 \ $(DESTDIR)$(HTDOCDIR)/snapshots/mdocml-$(VERSION).sha256 +Makefile.local config.h: configure ${TESTSRCS} + @echo "$@ is out of date; please run ./configure" + @exit 1 + depend: config.h mkdep -f Makefile.depend $(CFLAGS) $(SRCS) perl -e 'undef $$/; $$_ = <>; s|/usr/include/\S+||g; \ - s|\\\n||g; s| +| |g; print;' Makefile.depend > Makefile.tmp + s|\\\n||g; s| +| |g; s| $$||mg; print;' \ + Makefile.depend > Makefile.tmp mv Makefile.tmp Makefile.depend libmandoc.a: $(COMPAT_OBJS) $(LIBMANDOC_OBJS) $(AR) rs $@ $(COMPAT_OBJS) $(LIBMANDOC_OBJS) -mandoc: $(MANDOC_OBJS) libmandoc.a - $(CC) $(LDFLAGS) -o $@ $(MANDOC_OBJS) libmandoc.a +mandoc: $(MAN_OBJS) libmandoc.a + $(CC) $(LDFLAGS) -o $@ $(MAN_OBJS) libmandoc.a $(DBLIB) makewhatis: $(MAKEWHATIS_OBJS) libmandoc.a $(CC) $(LDFLAGS) -o $@ $(MAKEWHATIS_OBJS) libmandoc.a $(DBLIB) -preconv: $(PRECONV_OBJS) - $(CC) $(LDFLAGS) -o $@ $(PRECONV_OBJS) - manpage: $(MANPAGE_OBJS) libmandoc.a $(CC) $(LDFLAGS) -o $@ $(MANPAGE_OBJS) libmandoc.a $(DBLIB) -apropos: $(APROPOS_OBJS) libmandoc.a - $(CC) $(LDFLAGS) -o $@ $(APROPOS_OBJS) libmandoc.a $(DBLIB) - man.cgi: $(CGI_OBJS) libmandoc.a $(CC) $(LDFLAGS) $(STATIC) -o $@ $(CGI_OBJS) libmandoc.a $(DBLIB) demandoc: $(DEMANDOC_OBJS) libmandoc.a $(CC) $(LDFLAGS) -o $@ $(DEMANDOC_OBJS) libmandoc.a mdocml.sha256: mdocml.tar.gz sha256 mdocml.tar.gz > $@ mdocml.tar.gz: $(DISTFILES) mkdir -p .dist/mdocml-$(VERSION)/ - $(INSTALL_SOURCE) $(DISTFILES) .dist/mdocml-$(VERSION) + $(INSTALL) -m 0644 $(DISTFILES) .dist/mdocml-$(VERSION) chmod 755 .dist/mdocml-$(VERSION)/configure ( cd .dist/ && tar zcf ../$@ mdocml-$(VERSION) ) rm -rf .dist/ -config.h: configure config.h.pre config.h.post $(TESTSRCS) - rm -f config.log - CC="$(CC)" CFLAGS="$(CFLAGS)" DBLIB="$(DBLIB)" \ - VERSION="$(VERSION)" ./configure - .PHONY: base-install cgi-install db-install install www-install -.PHONY: clean depend +.PHONY: clean distclean depend .SUFFIXES: .1 .3 .5 .7 .8 .h .SUFFIXES: .1.html .3.html .5.html .7.html .8.html .h.html .h.h.html: highlight -I $< > $@ .1.1.html .3.3.html .5.5.html .7.7.html .8.8.html: mandoc ./mandoc -Thtml -Wall,stop \ -Ostyle=style.css,man=%N.%S.html,includes=%I.html $< > $@ Index: vendor/mdocml/dist/Makefile.depend =================================================================== --- vendor/mdocml/dist/Makefile.depend (revision 275396) +++ vendor/mdocml/dist/Makefile.depend (revision 275397) @@ -1,70 +1,72 @@ -apropos.o: apropos.c config.h manpath.h mansearch.h -arch.o: arch.c config.h mdoc.h libmdoc.h arch.in -att.o: att.c config.h mdoc.h libmdoc.h att.in +att.o: att.c config.h mdoc.h libmdoc.h cgi.o: cgi.c config.h mandoc.h mandoc_aux.h main.h manpath.h mansearch.h cgi.h chars.o: chars.c config.h mandoc.h mandoc_aux.h libmandoc.h chars.in -compat_fgetln.o: compat_fgetln.c config.h -compat_getsubopt.o: compat_getsubopt.c config.h -compat_ohash.o: compat_ohash.c config.h -compat_reallocarray.o: compat_reallocarray.c config.h -compat_sqlite3_errstr.o: compat_sqlite3_errstr.c config.h -compat_strcasestr.o: compat_strcasestr.c config.h -compat_strlcat.o: compat_strlcat.c config.h -compat_strlcpy.o: compat_strlcpy.c config.h -compat_strsep.o: compat_strsep.c config.h +compat_fgetln.o: compat_fgetln.c config.h +compat_fts.o: compat_fts.c config.h compat_fts.h +compat_getsubopt.o: compat_getsubopt.c config.h +compat_ohash.o: compat_ohash.c config.h compat_ohash.h +compat_reallocarray.o: compat_reallocarray.c config.h +compat_sqlite3_errstr.o: compat_sqlite3_errstr.c config.h +compat_strcasestr.o: compat_strcasestr.c config.h +compat_strlcat.o: compat_strlcat.c config.h +compat_strlcpy.o: compat_strlcpy.c config.h +compat_strsep.o: compat_strsep.c config.h demandoc.o: demandoc.c config.h man.h mdoc.h mandoc.h eqn.o: eqn.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h eqn_html.o: eqn_html.c config.h mandoc.h out.h html.h eqn_term.o: eqn_term.c config.h mandoc.h out.h term.h html.o: html.c config.h mandoc.h mandoc_aux.h libmandoc.h out.h html.h main.h lib.o: lib.c config.h mdoc.h libmdoc.h lib.in -main.o: main.c config.h mandoc.h mandoc_aux.h main.h mdoc.h man.h +main.o: main.c config.h mandoc.h mandoc_aux.h main.h mdoc.h man.h manpath.h mansearch.h man.o: man.c config.h man.h mandoc.h mandoc_aux.h libman.h libmandoc.h man_hash.o: man_hash.c config.h man.h mandoc.h libman.h man_html.o: man_html.c config.h mandoc.h mandoc_aux.h out.h html.h man.h main.h man_macro.o: man_macro.c config.h man.h mandoc.h libmandoc.h libman.h man_term.o: man_term.c config.h mandoc.h mandoc_aux.h out.h man.h term.h main.h man_validate.o: man_validate.c config.h man.h mandoc.h mandoc_aux.h libman.h libmandoc.h mandoc.o: mandoc.c config.h mandoc.h mandoc_aux.h libmandoc.h mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h -mandocdb.o: mandocdb.c config.h mdoc.h man.h mandoc.h mandoc_aux.h manpath.h mansearch.h +mandocdb.o: mandocdb.c config.h compat_fts.h compat_ohash.h mdoc.h man.h mandoc.h mandoc_aux.h manpath.h mansearch.h manpage.o: manpage.c config.h manpath.h mansearch.h manpath.o: manpath.c config.h mandoc_aux.h manpath.h -mansearch.o: mansearch.c config.h mandoc.h mandoc_aux.h manpath.h mansearch.h +mansearch.o: mansearch.c config.h compat_ohash.h mandoc.h mandoc_aux.h manpath.h mansearch.h mansearch_const.o: mansearch_const.c config.h manpath.h mansearch.h mdoc.o: mdoc.c config.h mdoc.h mandoc.h mandoc_aux.h libmdoc.h libmandoc.h mdoc_argv.o: mdoc_argv.c config.h mdoc.h mandoc.h mandoc_aux.h libmdoc.h libmandoc.h mdoc_hash.o: mdoc_hash.c config.h mdoc.h libmdoc.h mdoc_html.o: mdoc_html.c config.h mandoc.h mandoc_aux.h out.h html.h mdoc.h main.h mdoc_macro.o: mdoc_macro.c config.h mdoc.h mandoc.h libmdoc.h libmandoc.h mdoc_man.o: mdoc_man.c config.h mandoc.h mandoc_aux.h out.h man.h mdoc.h main.h mdoc_term.o: mdoc_term.c config.h mandoc.h mandoc_aux.h out.h term.h mdoc.h main.h mdoc_validate.o: mdoc_validate.c config.h mdoc.h mandoc.h mandoc_aux.h libmdoc.h libmandoc.h msec.o: msec.c config.h mandoc.h libmandoc.h msec.in out.o: out.c config.h mandoc_aux.h mandoc.h out.h -preconv.o: preconv.c config.h +preconv.o: preconv.c config.h mandoc.h libmandoc.h read.o: read.c config.h mandoc.h mandoc_aux.h libmandoc.h mdoc.h man.h main.h -roff.o: roff.c config.h mandoc.h mandoc_aux.h libroff.h libmandoc.h predefs.in +roff.o: roff.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h predefs.in st.o: st.c config.h mdoc.h libmdoc.h st.in tbl.o: tbl.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h tbl_data.o: tbl_data.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h tbl_html.o: tbl_html.c config.h mandoc.h out.h html.h tbl_layout.o: tbl_layout.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h tbl_opts.o: tbl_opts.c config.h mandoc.h libmandoc.h libroff.h tbl_term.o: tbl_term.c config.h mandoc.h out.h term.h term.o: term.c config.h mandoc.h mandoc_aux.h out.h term.h main.h term_ascii.o: term_ascii.c config.h mandoc.h mandoc_aux.h out.h term.h main.h term_ps.o: term_ps.c config.h mandoc.h mandoc_aux.h out.h main.h term.h tree.o: tree.c config.h mandoc.h mdoc.h man.h main.h -vol.o: vol.c config.h mdoc.h libmdoc.h vol.in -test-fgetln.o: test-fgetln.c -test-getsubopt.o: test-getsubopt.c -test-mmap.o: test-mmap.c -test-ohash.o: test-ohash.c -test-reallocarray.o: test-reallocarray.c -test-sqlite3_errstr.o: test-sqlite3_errstr.c -test-strcasestr.o: test-strcasestr.c -test-strlcat.o: test-strlcat.c -test-strlcpy.o: test-strlcpy.c -test-strptime.o: test-strptime.c -test-strsep.o: test-strsep.c +test-dirent-namlen.o: test-dirent-namlen.c +test-fgetln.o: test-fgetln.c +test-fts.o: test-fts.c +test-getsubopt.o: test-getsubopt.c +test-mmap.o: test-mmap.c +test-ohash.o: test-ohash.c +test-reallocarray.o: test-reallocarray.c +test-sqlite3.o: test-sqlite3.c +test-sqlite3_errstr.o: test-sqlite3_errstr.c +test-strcasestr.o: test-strcasestr.c +test-strlcat.o: test-strlcat.c +test-strlcpy.o: test-strlcpy.c +test-strptime.o: test-strptime.c +test-strsep.o: test-strsep.c +test-wchar.o: test-wchar.c Index: vendor/mdocml/dist/NEWS =================================================================== --- vendor/mdocml/dist/NEWS (revision 275396) +++ vendor/mdocml/dist/NEWS (revision 275397) @@ -1,450 +1,447 @@ -$Id: NEWS,v 1.5 2014/08/10 16:32:57 schwarze Exp $ +$Id: NEWS,v 1.6 2014/08/11 01:39:00 schwarze Exp $ This file lists the most important changes in the mdocml.bsd.lv distribution. Changes in version 1.13.1, released on August 10, 2014 --- MAJOR NEW FEATURES --- * A complete apropos(1)/makewhatis(8)/man.cgi(8) suite based on SQLite3 is now included. - CAVEAT: This also requires a working fts(3) implementation. - If your system lacks that *and* you want apropos(1)/makewhatis(8), - stay with 1.12.3 for now, then go to 1.12.4 and 1.13.2. * The roff(7) parser now provides an almost complete implementation of numerical expressions. * Warning and error messages have been improved in many ways. Almost all fatal errors were downgraded to normal errors and some even to warnings. Almost all messages now mention the macro where the issue is detected and many indicate the workaround employed. The mandoc(1) manual now includes a list explaining all messages. --- MINOR NEW FEATURES --- * The roff(7) parser now supports the .ami (append to macro with indirectly specified name), .as (append to user-defined string), .dei (define macro with indirectly specified name), .ll (line length), and .rr (remove register) requests. * The roff(7) parser now supports string comparison and numerical conditionals in the .if and .ie requests. * The roff parser now fully supports the \B (validate numerical expression) and partially supports the \w (measure text width) escape sequences. * The terminal formatter now supports the \: (optional line break) escape sequence. * The roff parser now supports expansion of user-defined strings involving indirect references. * The roff(7) parser now handles some pre-defined read-only number registers that occur in the pod2man(1) preamble. * For backward compatibility, the mdoc(7) parser and formatters now support the obsolete macros .En, .Es, .Fr, and .Ot. * The mdoc(7) formatter non partially supports .Bd -centered. * tbl(7) now handles leading and trailing vertical lines. * The build system now provides fallback versions of strcasestr(3) and strsep(3) for systems lacking them. * The mdoc(7) manual now explains how various standards supported by the .St macro are related to each other. --- BUGFIXES --- * In the roff(7) parser, several bugs were fixed with respect to closing conditional blocks on macro lines. * Parsing of roff(7) identifiers and escape sequences was improved in multiple respects. * In the mdoc(7) parser, the handling of defective document prologues was improved in multiple ways. * The mdoc(7) parser no longer skips content before the first section header, and it no longer deletes non-.% content from .Rs blocks. * In the mdoc(7) parser, a crash was fixed related to weird .Sh headers. * In the mdoc(7) parser, handling of .Sm with missing or invalid arguments was corrected. * In the mdoc(7) parser, trailing punctuation at the end of partial implicit macros no longer triggers end-of-sentence spacing. * In the terminal formatter, two crashes were fixed: one triggered by excessive indentation and another by excessively long .Nm arguments. * In the terminal formatter, a floating point rounding bug was fixed that sometimes caused an off-by-one error in indentation. * In the UTF-8 formatter, rendering of accents, breakable hyphens, and non-breakable spaces was corrected. * In the HTML formatter, encoding of special characters was corrected in multiple respects. * In the mdoc(7) formatter, rendering of .Ex and .Rv was improved for various edge cases. * In the mdoc(7) formatter, handling of empty .Bl -inset item heads was improved. * In the man(7) formatter, some bugs were fixed with respect to same-line detection in the context of .TP and .nf macros, and the indentation of .IP and .TP blocks was improved. * The mandoc(3) library no longer prints to stderr. --- THANKS TO --- Abhinav Upadhyay (NetBSD), Andreas Voegele, Anthony Bentley (OpenBSD), Christian Weisgerber (OpenBSD), Havard Eidnes (NetBSD), Jan Stary, Jason McIntyre (OpenBSD), Jeremie Courreges-Anglas (OpenBSD), Joerg Sonnenberger (NetBSD), Juan Francisco Cantero Hurtado (OpenBSD), Marc Espie (OpenBSD), Matthias Scheler (NetBSD), Pascal Stumpf (OpenBSD), Paul Onyschuk (Alpine Linux), Sebastien Marie, Steffen Nurpmeso, Stuart Henderson (OpenBSD), Ted Unangst (OpenBSD), Theo de Raadt (OpenBSD), Thomas Klausner (NetBSD), and Ulrich Spoerlein (FreeBSD) for reporting bugs and missing features. Changes in version 1.12.3, released on December 31, 2013 * In the mdoc(7) SYNOPSIS, line breaks and hanging indentation now work correctly for .Fo/.Fa/.Fc and .Fn blocks. Thanks to Franco Fichtner for doing part of the work. * The mdoc(7) .Bk macro got some addititonal bugfixes. * In mdoc(7) macro arguments, double quotes can now be quoted by doubling them, just like in man(7). Thanks to Tsugutomo ENAMI for the patch. * At the end of man(7) macro lines, end-of-sentence spacing now works. Thanks to Franco Fichtner for the patch. * For backward compatibility, the man(7) parser now supports the man-ext .UR/.UE (uniform resource identifier) block macros. * The man(7) parser now handles closing blocks that are not open more gracefully. * The man(7) parser now ignores blank lines right after .SH and .SS. * In the man(7) formatter, reset indentation when leaving a block, not just when entering the next one. * The roff(7) .nr request now supports incrementing and decrementing number registers and stops parsing the number right before the first non-digit character. * The roff(7) parser now supports the alternative escape sequence syntax \C'uXXXX' for Unicode characters. * The roff(7) parser now parses and ignores the .fam (font family) and .hw (hyphenation points) requests and the \d and \u escape sequences. * The roff(7) manual got a new ESCAPE SEQUENCE REFERENCE. Changes in version 1.12.2, released on Oktober 5, 2013 * The mdoc(7) to man(7) converter, to be called as mandoc -Tman, is now fully functional. * The mandoc(1) utility now supports the -Ios (default operating system) input option, and the -Tutf8 output mode now actually works. * The mandocdb(8) utility no longer truncates existing databases when starting to build new ones, but only replaces them when the build actually succeeds. * The man(7) parser now supports the PD macro (paragraph distance), and (for GNU man-ext compatibility only) EX (example block) and EE (example end). Plus several bugfixes regarding indentation, line breaks, and vertical spacing, and regarding RS following TP. * The roff(7) parser now supports the \f(BI (bold+italic) font escape, the \z (zero cursor advance) escape and the cc (change control character) and it (input line trap) requests. Plus bugfixes regarding the \t (tab) escape, nested escape sequences, and conditional requests. * In mdoc(7), several bugs were fixed related to UTF-8 output of quoting enclosures, delimiter handling, list indentation and horizontal and vertical spacing, formatting of the Lk, %U, and %C macros, plus some bugfixes related to the handling of syntax errors like badly nested font blocks, stray Ta macros outside column lists, unterminated It Xo blocks, and non-text children of Nm blocks. * In tbl(7), the width of horizontal spans and the vertical spacing around tables was corrected, and in man(7) files, a crash was fixed that was triggered by some particular unclosed T{ macros. * For mandoc developers, we now provide a tbl(3) library manual and gmdiff, a very small, very simplistic groff-versus-mandoc output comparison tool. * Provide this NEWS file. Changes in version 1.12.1, released on March 23, 2012 * Significant work on apropos(1) and mandocdb(8). These tools are now much more robust. A whatis(1) implementation is now handled as an apropos(1) mode. These tools are also able to minimally handle pre-formatted pages, that is, those already formatted by another utility such as GNU troff. * The man.cgi(7) script is also now available for wider testing. It interfaces with mandocdb(8) manuals cached by catman(8). HTML output is generated on-the-fly by libmandoc or internal methods to convert pre-formatted pages. * The mailing list archive for the discuss and tech lists are being hosted by Gmane at gmane.comp.tools.mdocml.user and gmane.comp.tools.mdocml.devel, respectively. Changes in version 1.12.0, released on October 8, 2011 * This version features a new, work-in-progress mandoc(1) output mode: -Tman. This mode allows a system maintainer to distribute man(7) media for older systems that may not natively support mdoc(7), such as old Solaris systems. * The -Ofragment option was added to mandoc(1)'s -Thtml and -Txhtml modes. * While adding features, an apropos(1) utility has been merged from the mandoc-tools sandbox. This interfaces with mandocdb(8) for semantic search of manual content. apropos(1) is different from the traditional apropos primarily in allowing keyword search (such as for functions, utilities, etc.) and regular expressions. Note that the calling syntax for apropos is likely to change as it settles down. * In documentation news, the mdoc(7) and man(7) manuals have been made considerably more readable by adding MACRO OVERVIEW sections, by moving the gory details of the LANGUAGE SYNTAX to the roff(7) manual, and by moving the very technical MACRO SYNTAX sections down to the bottom of the page. * Furthermore, for tbl(7), the -Tascii mode horizontal spacing of tables was rewritten completely. It is now compatible with groff(1), both with and without frames and rulers. * Nesting of indented blocks is now supported in man(7), and several bugs were fixed regarding indentation and alignment. * The page headers in mdoc(7) are now nicer for very long titles. Changes in version 1.11.7, released on September 2, 2011 * Added demandoc(1) utility for stripping away macros and escapes. This replaces the historical deroff(1) utility. * Also improved the mdoc(7) and man(7) manuals. Changes in version 1.11.6, released on August 16, 2011 * Handling of tr macro in roff(7) implemented. This makes Perl documentation much more readable. Hyphenation is also now enabled in man(7) format documents. Many other general improvements have been implemented. Changes in version 1.11.5, released on July 24, 2011 * Significant eqn(7) improvements. mdocml can now parse arbitrary eqn input (although few GNU extensions are accepted, nor is mixing low-level roff with eqn). See the eqn(7) manual for details. For the time being, equations are rendered as simple in-line text. The equation parser satisfies the language specified in the Second Edition User's Guide: http://www.kohala.com/start/troff/v7man/eqn/eqn2e.ps Changes in version 1.11.4, released on July 12, 2011 * Bug-fixes and clean-ups across all systems, especially in mandocdb(8) and the man(7) parser. This release was significantly assisted by participants in OpenBSD's c2k11. Thanks! Changes in version 1.11.3, released on May 26, 2011 * Introduce locale-encoding of output with the -Tlocale output option and Unicode escaped-character input. See mandoc(1) and mandoc_char(7), respectively, for details. This allows for non-ASCII characters (e.g., \[u5000]) to be rendered in the locale's encoding, if said environment supports wide-character encoding (if it does not, -Tascii is used instead). Locale support can be turned off at compile time by removing -DUSE_WCHAR in the Makefile, in which case -Tlocale is always a synonym for -Tascii. * Furthermore, multibyte-encoded documents, such as those in UTF-8, may be on-the-fly recoded into mandoc(1) input by using the newly-added preconv(1) utility. Note: in the future, this feature may be integrated into mandoc(1). Changes in version 1.11.2, released on May 12, 2011 * Corrected some installation issues in version 1.11.1. * Further migration to libmandoc. * Initial public release (this utility is very much under development) of mandocdb(8). This utility produces keyword databases of manual content, which features semantic querying of manual content. Changes in version 1.11.1, released on April 4, 2011 * The earlier libroff, libmdoc, and libman soup have been merged into a single library, libmandoc, which manages all aspects of parsing real manuals, from line-handling to tbl(7) parsing. * As usual, many general fixes and improvements have also occurred. In particular, a great deal of redundancy and superfluous code has been removed with the merging of the backend libraries. * see also the changes in 1.10.10 Changes in version 1.10.10, March 20, 2011, NOT released * Initial eqn(7) functionality is in place. For the time being, this is limited to the recognition of equation blocks; future version of mdocml will expand upon this framework. Changes in version 1.10.9, released on January 7, 2011 * Many back-end fixes have been implemented: argument handling (quoting), man(7) improvements, error/warning classes, and many more. * Initial tbl(7) functionality (see the "TS", "TE", and "T&" macros in the roff(7) manual) has been merged from tbl.bsd.lv. Output is still minimal, especially for -Thtml and -Txhtml, but manages to at least display data. This means that mandoc(1) now has built-in support for two troff preprocessors via libroff: soelim(1) and tbl(1). Changes in version 1.10.8, released on December 24, 2010 * Overhauled the -Thtml and -Txhtml output modes. They now display readable output in arbitrary browsers, including text-based ones like lynx(1). See HTML and XHTML manuals in the DOCUMENTATION section for examples. Attention: available style-sheet classes have been considerably changed! See the example.style.css file for details. Lastly, libmdoc and libman have been cleaned up and reduced in size and complexity. * see also the changes in 1.10.7 Changes in version 1.10.7, December 6, 2010, NOT released Significant improvements merged from OpenBSD downstream, including: * many new roff(7) components, * in-line implementation of troff's soelim(1), * broken-block handling, * overhauled error classifications, and * cleaned up handling of error conditions. Changes in version 1.10.6, released on September 27, 2010 * Calling conventions for mandoc(1) have changed: -W improved and -f deprecated. * Non-ASCII characters are also now uniformly discarded. * Lots of documentation improvements. * Many incremental fixes accomodating for groff's more interesting productions. * Lastly, pod2man(1) preambles are now fully accepted after some considerable roff(7) and special character support. Changes in version 1.10.5, released on July 27, 2010 * Primarily a bug-fix and polish release, but including -Tpdf support in mandoc(1) by way of "Summer of Code". Highlights: * fix "Sm" and "Bd" handling * fix end-of-sentence handling for embedded sentences * polish man(7) documentation * document all mdoc(7) macros * polish mandoc(1) -Tps output * lots of internal clean-ups in character escapes * un-break literal contexts in man(7) documents * improve -Thtml output for -man * add mandoc(1) -Tpdf support Changes in version 1.10.4, released on July 12, 2010 * Lots of features developed during both "Summer of Code" and the OpenBSD c2k10 hackathon: * minimal "ds" roff(7) symbols are supported * beautified SYNOPSIS section output * acceptance of scope-block breakage in mdoc(7) * clarify error message status * many minor bug-fixes and formatting issues resolved * see also changes in 1.10.3 Changes in version 1.10.3, June 29, 2010, NOT released * variable font-width and paper-size support in mandoc(1) -Tps output * "Bk" mdoc(7) support Changes in version 1.10.2, released on June 19, 2010 * Small release featuring text-decoration in -Tps output, a few minor relaxations of errors, and some optimisations. Changes in version 1.10.1, released on June 7, 2010 * This primarily focusses on the "Bl" and "It" macros described in mdoc(7). Multi-line column support is now fully compatible with groff, as are implicit list entries for columns. * Removed manuals(7) in favour of http://manpages.bsd.lv. * The way we handle the SYNOPSIS section (see the SYNOPSIS documentation in MANUAL STRUCTURE) has also been considerably simplified compared to groff's method. * Furthermore, the -Owidth=width output option has been added to -Tascii, see mandoc(1). * Lastly, initial PostScript output has been added with the -Tps option to mandoc(1). It's brutally simple at the moment: fixed-font, with no font decorations. Changes in version 1.10.0, released on May 29, 2010 * Release consisting of the results from the m2k10 hackathon and up-merge from OpenBSD. This requires a significant note of thanks to Ingo Schwarze (OpenBSD) and Joerg Sonnenberger (NetBSD) for their hard work, and again to Joerg for hosting m2k10. Highlights (mostly cribbed from Ingo's m2k10 report) follow in no particular order: * a libroff preprocessor in front of libmdoc and libman stripping out roff(7) instructions; * end-of-sentence (EOS) detection in free-form and macro lines; * correct handling of tab-separated columnar lists in mdoc(7); * improved main calling routines to optionally use mmap(3) for better performance; * cleaned up exiting when invoked as -Tlint or over multiple files with -fign-errors; * error and warning message handling re-written to be unified for libroff, libmdoc, and libman; * handling of badly-nested explicit-scoped macros; * improved free-form text parsing in libman and libmdoc; * significant GNU troff compatibility improvements in -Tascii, largely in terms of spacing; * a regression framework for making sure the many fragilities of GNU troff aren't trampled in subsequent work; * support for -Tascii breaking at hyphens encountered in free-form text; * and many more minor fixes and improvements Changes in version 1.9.25, released on May 13, 2010 * Fixed handling of "\*(Ba" escape. * Backed out -fno-ign-chars (pointless complexity). * Fixed erroneous breaking of literal lines. * Fixed SYNOPSIS breaking lines before non-initial macros. * Changed default section ordering. * Most importantly, the framework for end-of-sentence double-spacing is in place, now implemented for the "end-of-sentence, end-of-line" rule. * This is a stable roll-back point before the mandoc hackathon in Rostock! Changes in version 1.9.24, released on May 9, 2010 * Rolled back break-at-hyphen. * -DUGLY is now the default (no feature splits!). * Free-form text is not de-chunked any more: lines are passed whole-sale into the front-end, including whitespace. * Added mailing lists. Changes in version 1.9.23, released on April 7, 2010 * mdocml has been linked to the OpenBSD build. * This version incorporates many small changes, mostly from patches by OpenBSD, allowing crufty manuals to slip by with warnings instead of erroring-out. * Some subtle semantic issues, such as punctuation scope, have also been fixed. * Lastly, some issues with -Thtml have been fixed, which prompted an update to the online manual pages style layout. Changes in version 1.9.22, released on March 31, 2010 * Adjusted merge of the significant work by Ingo Schwarze in getting "Xo" blocks (block full implicit, e.g., "It" for non-columnar lists) to work properly. This isn't enabled by default: you must specify -DUGLY as a compiler flag (see the Makefile for details). Changes in version 1.9.20, released on March 30, 2010 * More efforts to get roff instructions in man(7) documents under control. Note that roff instructions embedded in line-scoped, next-line macros (e.g. "B") are not supported. * Leading punctuation for mdoc(7) macros, such as "Fl ( ( a", are now correctly handled. Changes in version 1.9.18, released on March 27, 2010 * Many fixes (largely pertaining to scope) and improvements (e.g., handling of apostrophe-control macros, which fixes the strange "BR" seen in some macro output) to handling roff instructions in man(7) documents. Changes in version 1.9.17, released on March 25, 2010 * Accept perlpod(1) standard preamble. * Also accept (and discard) "de", "dei", "am", "ami", and "ig" roff macro blocks. Changes in version 1.9.16, released on March 22, 2010 * Inspired by patches and bug reports by Ingo Schwarze, allowed man(7) to accept non-printing elements to be nested within next-line scopes, such as "br" within "B" or "TH", which is valid roff. * Longsoon architecture also noted and Makefile cleaned up. Changes in version 1.9.15, released on February 18, 2010 * Moved to our new BSD.lv home. * XHTML is now an acceptable output mode for mandoc(1); * "Xr" made more compatible with groff; * "Vt" fixed when invoked in SYNOPSIS; * "\\" escape removed; * end-of-line white-space detected for all lines; * subtle bug fixed in list display for some modes; * compatibility layer checked in for compilation in diverse UNIX systems; * and column lengths handled correctly. For older releases, see the ChangeLog files in http://mdocml.bsd.lv/snapshots/ . Index: vendor/mdocml/dist/TODO =================================================================== --- vendor/mdocml/dist/TODO (revision 275396) +++ vendor/mdocml/dist/TODO (revision 275397) @@ -1,437 +1,603 @@ ************************************************************************ * Official mandoc TODO. -* $Id: TODO,v 1.176 2014/08/09 14:24:53 schwarze Exp $ +* $Id: TODO,v 1.189 2014/11/26 21:40:17 schwarze Exp $ ************************************************************************ +Many issues are annotated for difficulty as follows: + + - loc = locality of the issue + * single file issue, affects file only, or very few + ** single module issue, affects several files of one module + *** cross-module issue, significantly impacts multiple modules + and may require substantial changes to internal interfaces + - exist = difficulty of the existing code in this area + * affected code is straightforward and easy to read and change + ** affected code is somewhat complex, but once you understand + the design, not particularly difficult to understand + *** affected code uses a special, exceptionally tricky design + - algo = difficulty of the new algorithm to be written + * the required logic and code is straightforward + ** the required logic is somewhat complex and needs a careful design + *** the required logic is exceptionally tricky, + maybe an approach to solve that is not even known yet + - size = the amount of code to be written or changed + * a small number of lines (at most 100, usually much less) + ** a considerable amount of code (several dozen to a few hundred) + *** a large amount of code (many hundreds, maybe thousands) + - imp = importance of the issue + * mostly for completeness + ** would be nice to have + *** issue causes considerable inconvenience + +Obviously, as the issues have not been solved yet, these annotations +are mere guesses, and some may be wrong. + ************************************************************************ * crashes ************************************************************************ - The abort() in bufcat(), html.c, can be triggered via buffmt_includes() by running -Thtml -Oincludes on a file containing a long .In argument. Fixing this will probably require reworking the whole bufcat() concept. + loc ** exist * algo * size ** imp ** ************************************************************************ * missing features ************************************************************************ --- missing roff features ---------------------------------------------- - .ad (adjust margins) .ad l -- adjust left margin only (flush left) .ad r -- adjust right margin only (flush right) .ad c -- center text on line .ad b -- adjust both margins (alias: .ad n) .na -- temporarily disable adjustment without changing the mode .ad -- re-enable adjustment without changing the mode Adjustment mode is ignored while in no-fill mode (.nf). + loc *** exist *** algo ** size ** imp ** (parser reorg would help) - .fc (field control) found by naddy@ in xloadimage(1) + loc ** exist *** algo * size * imp * - .nr third argument (auto-increment step size, requires \n+) found by bentley@ in sbcl(1) Mon, 9 Dec 2013 18:36:57 -0700 + loc * exist * algo * size * imp ** - .ns (no-space mode) occurs in xine-config(1) reported by brad@ Sat, 15 Jan 2011 15:45:23 -0500 + loc *** exist *** algo *** size ** imp * - .ta (tab settings) occurs in ircbug(1) and probably gnats(1) reported by brad@ Sat, 15 Jan 2011 15:50:51 -0500 also Tcl_NewStringObj(3) via wiz@ Wed, 5 Mar 2014 22:27:43 +0100 + loc ** exist *** algo ** size ** imp ** - .ti (temporary indent) found by naddy@ in xloadimage(1) found by bentley@ in nmh(1) Mon, 23 Apr 2012 13:38:28 -0600 + loc ** exist ** algo ** size * imp ** (parser reorg helps a lot) - .while and .shift found by jca@ in ratpoison(1) Sun, 30 Jun 2013 12:01:09 +0200 + loc * exist ** algo ** size ** imp ** - \c (interrupted text) should prevent the line break even inside .Bd literal; that occurs in chat(8) also found in cclive(1) - DocBook output + loc ** exist *** algo ** size * imp * - \h horizontal move found in cclive(1) DocBook output Anthony J. Bentley on discuss@ Sat, 21 Sep 2013 22:29:34 -0600 + loc ** exist ** algo ** size * imp ** (parser reorg helps a lot) - \n+ and \n- numerical register increment and decrement found by bentley@ in sbcl(1) Mon, 9 Dec 2013 18:36:57 -0700 + loc * exist * algo * size * imp ** -- \w'' width measurements +- \w'' improve width measurements would not be very useful without an expression parser, see below needed for Tcl_NewStringObj(3) via wiz@ Wed, 5 Mar 2014 22:27:43 +0100 + loc ** exist *** algo *** size * imp *** - using undefined strings or macros defines them to be empty wl@ Mon, 14 Nov 2011 14:37:01 +0000 + loc * exist * algo * size * imp * --- missing mdoc features ---------------------------------------------- - fix bad block nesting involving multiple identical explicit blocks see the OpenBSD mdoc_macro.c 1.47 commit message + loc * exist *** algo *** size * imp ** - .Bl -column .Xo support is missing ultimate goal: restore .Xr and .Dv to lib/libc/compat-43/sigvec.3 lib/libc/gen/signal.3 lib/libc/sys/sigaction.2 + loc * exist *** algo *** size * imp ** - edge case: decide how to deal with blk_full bad nesting, e.g. .Sh .Nm .Bk .Nm .Ek .Sh found by jmc@ in ssh-keygen(1) from jmc@ Wed, 14 Jul 2010 18:10:32 +0100 + loc * exist *** algo *** size ** imp ** - \\ is now implemented correctly * when defining strings and macros using .ds and .de * when parsing roff(7) and man(7) macro arguments It does not yet work in mdoc(7) macro arguments because libmdoc does not yet use mandoc_getarg(). Also check what happens in plain text, it must be identical to \e. - .Bd -centered implies -filled, not -unfilled, which is not easy to implement; it requires code similar to .ce, which we don't have either. Besides, groff has bug causing text right *before* .Bd -centered to be centered as well. + loc *** exist *** algo ** size ** imp ** (parser reorg would help) - .Bd -filled should not be the same as .Bd -ragged, but align both the left and right margin. In groff, it is implemented in terms of .ad b, which we don't have either. Found in cksum(1). + loc *** exist *** algo ** size ** imp ** (parser reorg would help) - implement blank `Bl -column', such as .Bl -column .It foo Ta bar .El + loc * exist *** algo *** size * imp * - explicitly disallow nested `Bl -column', which would clobber internal flags defined for struct mdoc_macro + loc * exist * algo * size * imp ** - In .Bl -column .It, the end of the line probably has to be regarded as an implicit .Ta, if there could be one, see the following mildly ugly code from login.conf(5): .Bl -column minpasswordlen program xetcxmotd .It path Ta path Ta value of Dv _PATH_DEFPATH .br Default search path. reported by Michal Mazurek via jmc@ Thu, 7 Apr 2011 16:00:53 +0059 + loc * exist *** algo ** size * imp ** - inside `.Bl -column' phrases, punctuation is handled like normal text, e.g. `.Bl -column .It Fl x . Ta ...' should give "-x -." - inside `.Bl -column' phrases, TERMP_IGNDELIM handling by `Pf' is not safe, e.g. `.Bl -column .It Pf a b .' gives "ab." but should give "ab ." - set a meaningful default if no `Bl' list type is assigned + loc * exist * algo * size * imp ** (already done?) - have a blank `It' head for `Bl -tag' not puke + loc * exist * algo * size * imp ** (already done?) - check whether it is correct that `D1' uses INDENT+1; does it need its own constant? + loc * exist ** algo ** size * imp ** - prohibit `Nm' from having non-text HEAD children (e.g., NetBSD mDNSShared/dns-sd.1) (mdoc_html.c and mdoc_term.c `Nm' handlers can be slightly simplified) - support translated section names e.g. x11/scrotwm scrotwm_es.1:21:2: error: NAME section must be first that one uses NOMBRE because it is spanish... deraadt tends to think that section-dependent macro behaviour is a bad idea in the first place, so this may be irrelevant + loc ** exist ** algo ** size * imp ** - When there is free text in the SYNOPSIS and that free text contains the .Nm macro, groff somehow understands to treat the .Nm as an in-line macro, while mandoc treats it as a block macro and breaks the line. No idea how the logic for distinguishing in-line and block instances should be, needs investigation. uqs@ Thu, 2 Jun 2011 11:03:51 +0200 uqs@ Thu, 2 Jun 2011 11:33:35 +0200 + loc * exist ** algo *** size * imp ** --- missing man features ----------------------------------------------- - -T[x]html doesn't stipulate non-collapsing spaces in literal mode --- missing tbl features ----------------------------------------------- - look at the POSIX manuals in the books/man-pages-posix port, they use some unsupported tbl(7) features. + loc * exist ** algo ** size ** imp *** -- investigate tbl(1) errors in sox(1) - see also naddy@ Sat, 16 Oct 2010 23:51:57 +0200 +- use Unicode U+2500 to U+256C for table borders + in tbl(7) -Tutf-8 output + suggested by bentley@ Tue, 14 Oct 2014 04:10:55 -0600 + loc * exist ** algo * size * imp ** - allow standalone `.' to be interpreted as an end-of-layout delimiter instead of being thrown away as a no-op roff line reported by Yuri Pankov, Wed 18 May 2011 11:34:59 CEST + loc ** exist ** algo ** size * imp ** +--- missing eqn features ----------------------------------------------- + +- The "size" keyword is parsed, but ignored by the formatter. + loc * exist * algo * size * imp * + +- The spacing characters `~', `^', and tab are currently ignored, + see User's Guide (Second Edition) page 2 section 4. + loc * exist * algo ** size * imp ** + +- Mark and lineup are parsed and ignored, + see User's Guide (Second Edition) page 5 section 15. + loc ** exist ** algo ** size ** imp ** + --- missing misc features ---------------------------------------------- - italic correction (\/) in PostScript mode Werner LEMBERG on groff at gnu dot org Sun, 10 Nov 2013 12:47:46 + loc ** exist ** algo * size * imp * - When makewhatis(8) encounters a FATAL parse error, it silently treats the file as formatted, which makes no sense at all for paths like man1/foo.1 - and which also contradicts what the manual says at the end of the description. The end result will be ENOENT for file names returned by mansearch() in manpage.file. + loc * exist * algo * size * imp ** - makewhatis(8) for preformatted pages: parse the section number from the header line and compare to the section number from the directory name + loc * exist * algo * size * imp ** - Does makewhatis(8) detect missing NAME sections, missing names, and missing descriptions in all the file formats? + loc * exist * algo * size * imp *** - clean up escape sequence handling, creating three classes: (1) fully implemented, or parsed and ignored without loss of content (2) unimplemented, potentially causing loss of content or serious mangling of formatting (e.g. \n) -> ERROR see textproc/mgdiff(1) for nice examples (3) undefined, just output the character -> perhaps WARNING + loc *** exist ** algo ** size ** imp *** (parser reorg helps) - kettenis wants base roff, ms, and me Fri, 1 Jan 2010 22:13:15 +0100 (CET) + loc ** exist ** algo ** size *** imp * --- compatibility checks ----------------------------------------------- - is .Bk implemented correctly in modern groff? sobrado@ Tue, 19 Apr 2011 22:12:55 +0200 - compare output to Heirloom roff, Solaris roff, and http://repo.or.cz/w/neatroff.git http://litcave.rudi.ir/ +- look at AT&T DWB http://www2.research.att.com/sw/download + Carsten Kunze has patches + Mon, 4 Aug 2014 17:01:28 +0200 + - look at pages generated from reStructeredText, e.g. devel/mercurial hg(1) These are a weird mixture of man(7) and custom autogenerated low-level roff stuff. Figure out to what extent we can cope. For details, see http://docutils.sourceforge.net/rst.html noted by stsp@ Sat, 24 Apr 2010 09:17:55 +0200 reminded by nicm@ Mon, 3 May 2010 09:52:41 +0100 - look at pages generated from ronn(1) github.com/rtomayko/ronn (based on markdown) - look at pages generated from Texinfo source by yat2m, e.g. security/gnupg First impression is not that bad. - look at pages generated by pandoc; see https://github.com/jgm/pandoc/blob/master/src/Text/Pandoc/Writers/Man.hs porting planned by kili@ Thu, 19 Jun 2014 19:46:28 +0200 - check compatibility with Plan9: http://swtch.com/usr/local/plan9/tmac/tmac.an http://swtch.com/plan9port/man/man7/man.html "Anthony J. Bentley" 28 Dec 2010 21:58:40 -0700 - check compatibility with the man(7) formatter https://raw.githubusercontent.com/rofl0r/hardcore-utils/master/man.c +- check compatibility with + http://ikiwiki.info/plugins/contrib/mandoc/ + https://github.com/schmonz/ikiwiki/compare/mandoc + Amitai Schlair Mon, 19 May 2014 14:05:53 -0400 + ************************************************************************ * formatting issues: ugly output ************************************************************************ - a column list with blank `Ta' cells triggers a spurrious start-with-whitespace printing of a newline - In .Bl -column, .It Em AuthenticationKey Length ought to render "Key Length" with emphasis, too, see OpenBSD iked.conf(5). reported again Nicolas Joly via wiz@ Wed, 12 Oct 2011 00:20:00 +0200 + loc * exist *** algo *** size ** imp *** - empty phrases in .Bl column produce too few blanks try e.g. .Bl -column It Ta Ta reported by millert Fri, 02 Apr 2010 16:13:46 -0400 + loc * exist *** algo *** size * imp ** - .%T can have trailing punctuation. Currently, it puts the trailing punctuation into a trailing MDOC_TEXT element inside its own scope. That element should rather be outside its scope, such that the punctuation does not get underlines. This is not trivial to implement because .%T then needs some features of in_line_eoln() - slurp all arguments into one single text element - and one feature of in_line() - put trailing punctuation out of scope. Found in mount_nfs(8) and exports(5), search for "Appendix". + loc ** exist ** algo *** size * imp ** - Trailing punctuation after .%T triggers EOS spacing, at least outside .Rs (eek!). Simply setting ARGSFL_DELIM for .%T is not the right solution, it sends mandoc into an endless loop. reported by Nicolas Joly Sat, 17 Nov 2012 11:49:54 +0100 + loc * exist ** algo ** size * imp ** - global variables in the SYNOPSIS of section 3 pages .Vt vs .Vt/.Va vs .Ft/.Va vs .Ft/.Fa ... from kristaps@ Tue, 08 Jun 2010 11:13:32 +0200 - in enclosures, mandoc sometimes fancies a bogus end of sentence reminded by jmc@ Thu, 23 Sep 2010 18:13:39 +0059 + loc * exist ** algo *** size * imp *** - formatting /usr/local/man/man1/latex2man.1 with groff and mandoc reveals lots of bugs both in groff and mandoc... reported by bentley@ Wed, 22 May 2013 23:49:30 -0600 --- PDF issues --------------------------------------------------------- - PDF output doesn't use a monospaced font for .Bd -literal Example: "mandoc -Tpdf afterboot.8 > output.pdf && pdfviewer output.pdf". Search the text "Routing tables". Also check what PostScript mode does when fixing this. reported by juanfra@ Wed, 04 Jun 2014 21:44:58 +0200 + instructions from juanfra@ Wed, 11 Jun 2014 02:21:01 +0200 + add a new <> block to the PDF files with /BaseFont /Courier + and change the /Name from /F0 to the new font (/F5 (?)). + loc * exist ** algo ** size * imp ** --- HTML issues -------------------------------------------------------- -
formatting is ugly hints are easy to find on the web, e.g. http://stackoverflow.com/questions/1713048/ see also matthew@ Fri, 18 Jul 2014 19:25:12 -0700 + loc * exist * algo ** size * imp *** +- jsg on icb, Nov 3, 2014: + try to guess Xr in man(7) for hyperlinking + +- The tables used to render the three-part page headers actually force + the width of the to the max-width given for . + Not yet sure how to fix that... + Observed by an Anonymous Coward on undeadly.org: + http://undeadly.org/cgi?action=article&sid=20140925064244&pid=1 + loc * exist * algo ** size * imp *** + +- consider whether can be used for Ar Dv Er Ev Fa Va. + from bentley@ Wed, 13 Aug 2014 09:17:55 -0600 + - check https://github.com/trentm/mdocml ************************************************************************ * formatting issues: gratuitous differences ************************************************************************ +- .Fn reopens a new scope after punctuation in mandoc, + but closes its scope for good in groff. + Do we want to change mandoc or groff? + Steffen Nurpmeso Sat, 08 Nov 2014 13:34:59 +0100 + loc * exist ** algo ** size * imp ** + - .Rv (and probably .Ex) print different text if an `Nm' has been named or not (run a manual without `Nm blah' to see this). I'm not sure that this exists in the wild, but it's still an error. + loc * exist * algo * size * imp * (already done?) - In .Bl -bullet, the groff bullet is "+\b+\bo\bo", the mandoc bullet - is just "o\bo". + is just "o\bo". The problem is to not break ps/pdf when fixing. see for example OpenBSD ksh(1) + loc ** exist ** algo ** size * imp ** - In .Bl -enum -width 0n, groff continues one the same line after the number, mandoc breaks the line. mail to kristaps@ Mon, 20 Jul 2009 02:21:39 +0200 + loc * exist ** algo ** size * imp ** - .Pp between two .It in .Bl -column should produce one, not two blank lines, see e.g. login.conf(5). reported by jmc@ Sun, 17 Apr 2011 14:04:58 +0059 reported again by sthen@ Wed, 18 Jan 2012 02:09:39 +0000 (UTC) + loc * exist *** algo ** size * imp ** - If the *first* line after .It is .Pp, break the line right after the tag, do not pad with space characters before breaking. See the description of the a, c, and i commands in sed(1). + loc * exist ** algo ** size * imp ** - If the first line after .It is .D1, do not assert a blank line in between, see for example tmux(1). reported by nicm@ 13 Jan 2011 00:18:57 +0000 + loc * exist ** algo ** size * imp ** - Trailing punctuation after .It should trigger EOS spacing. reported by Nicolas Joly Sat, 17 Nov 2012 11:49:54 +0100 Probably, this should be fixed somewhere in termp_it_pre(), not sure. + loc * exist ** algo ** size * imp ** - .Nx 1.0a should be "NetBSD 1.0A", not "NetBSD 1.0a", see OpenBSD ccdconfig(8). + loc * exist * algo * size * imp ** - In .Bl -tag, if a tag exceeds the right margin and must be continued on the next line, it must be indented by -width, not width+1; see "rule block|pass" in OpenBSD ifconfig(8). + loc * exist *** algo ** size * imp ** - When the -width string contains macros, the macros must be rendered before measuring the width, for example .Bl -tag -width ".Dv message" in magic(5), located in src/usr.bin/file, is the same as -width 7n, not -width 11n. The same applies to .Bl -column column widths; reported again by Nicolas Joly Thu, 1 Mar 2012 13:41:26 +0100 via wiz@ 5 Mar reported again by Franco Fichtner Fri, 27 Sep 2013 21:02:28 +0200 + loc *** exist *** algo *** size ** imp *** An easy partial fix would be to just skip the first word if it starts with a dot, including any following white space, when measuring. + loc * exist * algo * size * imp *** - The \& zero-width character counts as output. That is, when it is alone on a line between two .Pp, we want three blank lines, not two as in mandoc. + loc ** exist ** algo ** size * imp ** - Header lines of excessive length: Port OpenBSD man_term.c rev. 1.25 to mdoc_term.c and document it in mdoc(7) and man(7) COMPATIBILITY found while talking to Chris Bennett + loc * exist * algo * size * imp * - trailing whitespace must be ignored even when followed by a font escape, see for example makes \fBdig \fR operate in batch mode in dig(1). + loc ** exist ** algo ** size * imp ** ************************************************************************ * warning issues ************************************************************************ - check that MANDOCERR_BADTAB is thrown in the right cases, i.e. when finding a literal tab character in fill mode, and possibly change the wording of the warning message to refer to fill mode, not literal mode See the mail from Werner LEMBERG on the groff list, Fri, 14 Feb 2014 18:54:42 +0100 (CET) + loc * exist ** algo ** size * imp ** +- warn about attempts to call non-callable macros + Steffen Nurpmeso Tue, 11 Nov 2014 22:55:16 +0100 + Note that formatting is inconsistent in groff. + .Fn Po prints "Po()", .Ar Sh prints "file ..." and no "Sh". + Relatively hard because the relevant code is scattered + all over mdoc_macro.c and all subtly different. + loc ** exist ** algo ** size ** imp ** + - warn about "new sentence, new line" + loc ** exist ** algo *** size * imp ** - mandoc_special does not really check the escape sequence, but just the overall format + loc ** exist ** algo *** size ** imp ** - integrate mdoclint into mandoc ("end-of-line whitespace" thread) from jmc@ Mon, 13 Jul 2009 17:12:09 +0100 from kristaps@ Mon, 13 Jul 2009 18:34:53 +0200 from jmc@ Mon, 13 Jul 2009 17:45:37 +0059 from kristaps@ Mon, 13 Jul 2009 19:02:03 +0200 + (mostly done, check what remains) - -Tlint parser errors and warnings to stdout to tech@mdocml, naddy@ Wed, 28 Sep 2011 11:21:46 +0200 wait! kristaps@ Sun, 02 Oct 2011 17:12:52 +0200 - for system errors, use errno/strerror/warn/err ************************************************************************ * documentation issues ************************************************************************ - mention hyphenation rules: breaking at letter-letter in text mode (not macro args) proper hyphenation is unimplemented - talk about spacing around delimiters to jmc@, kristaps@ Sat, 23 Apr 2011 17:41:27 +0200 - mark macros as: page structure domain, manual domain, general text domain is this useful? - mention /usr/share/misc/mdoc.template in mdoc(7)? +- Is all the content from http://www.std.com/obi/BSD/doc/usd/28.tbl/tbl + covered in tbl(7)? + ************************************************************************ * performance issues ************************************************************************ - Why are we using MAP_SHARED, not MAP_PRIVATE for mmap(2)? How does SQLITE_CONFIG_PAGECACHE actually work? Document it! from kristaps@ Sat, 09 Aug 2014 13:51:36 +0200 Several areas can be cleaned up to make mandoc even faster. These are - improve hashing mechanism for macros (quite important: performance) - improve hashing mechanism for characters (not as important) - the PDF file is HUGE: this can be reduced by using relative offsets - instead of re-initialising the roff predefined-strings set before each parse, create a read-only version the first time and copy it + loc * exist ** algo ** size * imp ** ************************************************************************ * structural issues ************************************************************************ +- Use libz directly instead of forking gunzip(1). + Suggested by bapt at FreeBSD among others. + - We use the input line number at several places to distinguish same-line from different-line input. That plainly doesn't work with user-defined macros, leading to random breakage. - Find better ways to prevent endless loops in roff(7) macro and string expansion. - Finish cleanup of date handling. Decide which formats should be recognized where. Update both mdoc(7) and man(7) documentation. Triggered by Tim van der Molen Tue, 22 Feb 2011 20:30:45 +0100 +- struct mparse refactoring + Steffen Nurpmeso Thu, 04 Sep 2014 12:50:00 +0200 + - Consider creating some views that will make the database more readable from the sqlite3 shell. Consider using them to abstract from the database structure, too. suggested by espie@ Sat, 19 Apr 2014 14:52:57 +0200 + +************************************************************************ +* CGI issues +************************************************************************ + + - Enable HTTP compression by detecting gzip encoding and filtering + output through libz. + - Sandbox (see OpenSSH). + - Enable caching support via HTTP 304 and If-Modified-Since. + - Allow for cgi.h to be overridden by CGI environment variables. + Otherwise, binary distributions will inherit the compile-time + behaviour, which is not optimal. + - Have Mac OSX systems automatically disable -static compilation of the + CGI: -static isn't supported. Index: vendor/mdocml/dist/apropos.1 =================================================================== --- vendor/mdocml/dist/apropos.1 (revision 275396) +++ vendor/mdocml/dist/apropos.1 (revision 275397) @@ -1,387 +1,486 @@ -.\" $Id: apropos.1,v 1.29 2014/04/24 00:28:19 schwarze Exp $ +.\" $Id: apropos.1,v 1.36 2014/10/25 01:03:52 schwarze Exp $ .\" .\" Copyright (c) 2011, 2012 Kristaps Dzonsons .\" Copyright (c) 2011, 2012, 2014 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 24 2014 $ +.Dd $Mdocdate: October 25 2014 $ .Dt APROPOS 1 .Os .Sh NAME .Nm apropos , .Nm whatis .Nd search manual page databases .Sh SYNOPSIS .Nm +.Op Fl acfhklVw .Op Fl C Ar file .Op Fl M Ar path .Op Fl m Ar path .Op Fl O Ar outkey .Op Fl S Ar arch .Op Fl s Ar section .Ar expression ... .Sh DESCRIPTION The .Nm apropos and .Nm whatis utilities query manual page databases generated by .Xr makewhatis 8 , evaluating .Ar expression for each file in each database. -By default, it displays the names, section numbers, and description lines +By default, they display the names, section numbers, and description lines of all matching manuals. .Pp By default, .Nm searches for .Xr makewhatis 8 databases in the default paths stipulated by .Xr man 1 and uses case-insensitive substring matching .Pq the Cm = No operator over manual names and descriptions .Pq the Li \&Nm No and Li \&Nd No macro keys . Multiple terms imply pairwise .Fl o . +.Pp .Nm whatis -maps terms only to case-sensitive manual names. +is a synonym for +.Nm +.Fl f . .Pp -Its arguments are as follows: +The options are as follows: .Bl -tag -width Ds +.It Fl a +Instead of showing only the title lines, show the complete manual pages, +just like +.Xr man 1 +.Fl a +would. +If the standard output is a terminal device and +.Fl c +is not specified, use +.Xr more 1 +to paginate them. +In +.Fl a +mode, the options +.Fl IKOTW +described in the +.Xr mandoc 1 +manual are also available. .It Fl C Ar file Specify an alternative configuration .Ar file in .Xr man.conf 5 format. +.It Fl c +In +.Fl a +mode, copy the formatted manual pages to the standard output without using +.Xr more 1 +to paginate them. +.It Fl f +Search for all words in +.Ar expression +in manual page names only. +The search is case insensitive and matches whole words only. +In this mode, macro keys, comparison operators, and logical operators +are not available. +This overrides any earlier +.Fl k +and +.Fl l +options. +.It Fl h +Instead of showing the title lines, show the SYNOPSIS sections, just like +.Xr man 1 +.Fl h +would. +.It Fl k +Support the full +.Ar expression +syntax. +This overrides any earlier +.Fl f +and +.Fl l +options. +It is the default for +.Nm . +.It Fl l +An alias for +.Xr mandoc 1 +.Fl a . +This overrides any earlier +.Fl f , +.Fl k , +and +.Fl w +options. .It Fl M Ar path Use the colon-separated path instead of the default list of paths searched for .Xr makewhatis 8 databases. Invalid paths, or paths without manual databases, are ignored. .It Fl m Ar path Prepend the colon-separated paths to the list of paths searched for .Xr makewhatis 8 databases. Invalid paths, or paths without manual databases, are ignored. .It Fl O Ar outkey Show the values associated with the key .Ar outkey instead of the manual descriptions. .It Fl S Ar arch Restrict the search to pages for the specified .Xr machine 1 architecture. .Ar arch is case insensitive. By default, pages for all architectures are shown. .It Fl s Ar section Restrict the search to the specified section of the manual. By default, pages from all sections are shown. See .Xr man 1 for a listing of sections. +.It Fl V +Print version and exit. +.It Fl w +Instead of showing title lines, show the pathnames of the matching +manual pages, just like +.Xr man 1 +.Fl w +would. .El .Pp An .Ar expression consists of search terms joined by logical operators .Fl a .Pq and and .Fl o .Pq or . The .Fl a operator has precedence over .Fl o and both are evaluated left-to-right. .Bl -tag -width Ds .It \&( Ar expr No \&) True if the subexpression .Ar expr is true. .It Ar expr1 Fl a Ar expr2 True if both .Ar expr1 and .Ar expr2 are true (logical .Sq and ) . .It Ar expr1 Oo Fl o Oc Ar expr2 True if .Ar expr1 and/or .Ar expr2 evaluate to true (logical .Sq or ) . .It Ar term True if .Ar term is satisfied. This has syntax .Sm off .Oo .Op Ar key Op , Ar key ... .Pq Cm = | ~ .Oc .Ar val , .Sm on where .Ar key is an .Xr mdoc 7 macro to query and .Ar val is its value. See .Sx Macro Keys for a list of available keys. Operator .Cm = evaluates a substring, while .Cm ~ evaluates a regular expression. .It Fl i Ar term If .Ar term is a regular expression, it is evaluated case-insensitively. Has no effect on substring terms. .El .Pp -.Nm whatis -considers an -.Ar expression -to consist of an opaque keyword. -.Pp Results are sorted by manual sections and names, with output formatted as .Pp .D1 name[, name...](sec) \- description .Pp Where .Dq name is the manual's name, .Dq sec is the manual section, and .Dq description is the manual's short description. If an architecture is specified for the manual, it is displayed as .Pp .D1 name(sec/arch) \- description .Pp Resulting manuals may be accessed as .Pp .Dl $ man \-s sec name .Pp If an architecture is specified in the output, use .Pp .Dl $ man \-s sec \-S arch name .Ss Macro Keys Queries evaluate over a subset of .Xr mdoc 7 macros indexed by .Xr makewhatis 8 . In addition to the macro keys listed below, the special key .Cm any may be used to match any available macro key. .Pp Names and description: .Bl -column "xLix" description -offset indent -compact .It Li \&Nm Ta manual name .It Li \&Nd Ta one-line manual description .It Li arch Ta machine architecture (case-insensitive) .It Li sec Ta manual section number .El .Pp Sections and cross references: .Bl -column "xLix" description -offset indent -compact .It Li \&Sh Ta section header (excluding standard sections) .It Li \&Ss Ta subsection header .It Li \&Xr Ta cross reference to another manual page .It Li \&Rs Ta bibliographic reference .El .Pp Semantic markup for command line utilities: .Bl -column "xLix" description -offset indent -compact .It Li \&Fl Ta command line options (flags) .It Li \&Cm Ta command modifier .It Li \&Ar Ta command argument .It Li \&Ic Ta internal or interactive command .It Li \&Ev Ta environmental variable .It Li \&Pa Ta file system path .El .Pp Semantic markup for function libraries: .Bl -column "xLix" description -offset indent -compact .It Li \&Lb Ta function library name .It Li \&In Ta include file .It Li \&Ft Ta function return type .It Li \&Fn Ta function name .It Li \&Fa Ta function argument type and name .It Li \&Vt Ta variable type .It Li \&Va Ta variable name .It Li \&Dv Ta defined variable or preprocessor constant .It Li \&Er Ta error constant .It Li \&Ev Ta environmental variable .El .Pp Various semantic markup: .Bl -column "xLix" description -offset indent -compact .It Li \&An Ta author name .It Li \&Lk Ta hyperlink .It Li \&Mt Ta Do mailto Dc hyperlink .It Li \&Cd Ta kernel configuration declaration .It Li \&Ms Ta mathematical symbol .It Li \&Tn Ta tradename .El .Pp Physical markup: .Bl -column "xLix" description -offset indent -compact .It Li \&Em Ta italic font or underline .It Li \&Sy Ta boldface font .It Li \&Li Ta typewriter font .El .Pp Text production: .Bl -column "xLix" description -offset indent -compact .It Li \&St Ta reference to a standards document .It Li \&At Ta At No version reference .It Li \&Bx Ta Bx No version reference .It Li \&Bsx Ta Bsx No version reference .It Li \&Nx Ta Nx No version reference .It Li \&Fx Ta Fx No version reference .It Li \&Ox Ta Ox No version reference .It Li \&Dx Ta Dx No version reference .El .Sh ENVIRONMENT -.Bl -tag -width MANPATH +.Bl -tag -width MANPAGER +.It Ev MANPAGER +Any non-empty value of the environment variable +.Ev MANPAGER +will be used instead of the standard pagination program, +.Xr more 1 . .It Ev MANPATH The standard search path used by .Xr man 1 may be changed by specifying a path in the .Ev MANPATH environment variable. Invalid paths, or paths without manual databases, are ignored. Overridden by .Fl M . If .Ev MANPATH begins with a colon, it is appended to the default list; if it ends with a colon, it is prepended to the default list; or if it contains two adjacent colons, the standard search path is inserted between the colons. If none of these conditions are met, it overrides the standard search path. +.It Ev PAGER +Specifies the pagination program to use when +.Ev MANPAGER +is not defined. +If neither PAGER nor MANPAGER is defined, +.Pa /usr/bin/more Fl s +will be used. .El .Sh FILES .Bl -tag -width "/etc/man.conf" -compact .It Pa mandoc.db name of the .Xr makewhatis 8 keyword database .It Pa /etc/man.conf default .Xr man 1 configuration file .El .Sh EXIT STATUS .Ex -std .Sh EXAMPLES Search for .Qq .cf as a substring of manual names and descriptions: .Pp .Dl $ apropos .cf .Pp Include matches for .Qq .cnf and .Qq .conf as well: .Pp .Dl $ apropos .cf .cnf .conf .Pp Search in names and descriptions using a regular expression: .Pp .Dl $ apropos '~set.?[ug]id' .Pp Search for manuals in the library section mentioning both the .Qq optind and the .Qq optarg variables: .Pp .Dl $ apropos \-s 3 Va=optind \-a Va=optarg .Pp Do exactly the same as calling .Xr whatis 1 with the argument .Qq ssh : .Pp .Dl $ apropos \-\- \-i 'Nm~[[:<:]]ssh[[:>:]]' .Pp The following two invocations are equivalent: .Pp .D1 Li $ apropos -S Ar arch Li -s Ar section expression .Bd -ragged -offset indent .Li $ apropos \e( Ar expression Li \e) .Li -a arch~^( Ns Ar arch Ns Li |any)$ .Li -a sec~^ Ns Ar section Ns Li $ .Ed .Sh SEE ALSO .Xr man 1 , .Xr re_format 7 , .Xr makewhatis 8 .Sh HISTORY -An +Part of the functionality of +.Nm whatis +was already provided by the former +.Nm manwhere +utility in +.Bx 1 . +The .Nm -utility first appeared in +and +.Nm whatis +utilities first appeared in .Bx 2 . -It was rewritten from scratch for +They were rewritten from scratch for .Ox 5.6 . .Pp The .Fl M option and the .Ev MANPATH variable first appeared in .Bx 4.3 ; .Fl m in .Bx 4.3 Reno ; .Fl C in .Bx 4.4 Lite1 ; and .Fl S and .Fl s in -.Ox 4.5 . +.Ox 4.5 +for +.Nm +and in +.Ox 5.6 +for +.Nm whatis . .Sh AUTHORS .An -nosplit .An Bill Joy -wrote the original +wrote +.Nm manwhere +in 1977 and the original .Bx .Nm +and +.Nm whatis in February 1979. The current version was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv and .An Ingo Schwarze Aq Mt schwarze@openbsd.org . Index: vendor/mdocml/dist/att.c =================================================================== --- vendor/mdocml/dist/att.c (revision 275396) +++ vendor/mdocml/dist/att.c (revision 275397) @@ -1,37 +1,49 @@ -/* $Id: att.c,v 1.11 2014/04/20 16:46:04 schwarze Exp $ */ +/* $Id: att.c,v 1.13 2014/11/28 18:57:31 schwarze Exp $ */ /* * Copyright (c) 2009 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include #include #include "mdoc.h" #include "libmdoc.h" #define LINE(x, y) \ - if (0 == strcmp(p, x)) return(y); + if (0 == strcmp(p, x)) return(y) const char * mdoc_a2att(const char *p) { -#include "att.in" + LINE("v1", "Version\\~1 AT&T UNIX"); + LINE("v2", "Version\\~2 AT&T UNIX"); + LINE("v3", "Version\\~3 AT&T UNIX"); + LINE("v4", "Version\\~4 AT&T UNIX"); + LINE("v5", "Version\\~5 AT&T UNIX"); + LINE("v6", "Version\\~6 AT&T UNIX"); + LINE("v7", "Version\\~7 AT&T UNIX"); + LINE("32v", "Version\\~32V AT&T UNIX"); + LINE("III", "AT&T System\\~III UNIX"); + LINE("V", "AT&T System\\~V UNIX"); + LINE("V.1", "AT&T System\\~V Release\\~1 UNIX"); + LINE("V.2", "AT&T System\\~V Release\\~2 UNIX"); + LINE("V.3", "AT&T System\\~V Release\\~3 UNIX"); + LINE("V.4", "AT&T System\\~V Release\\~4 UNIX"); return(NULL); } Index: vendor/mdocml/dist/cgi.c =================================================================== --- vendor/mdocml/dist/cgi.c (revision 275396) +++ vendor/mdocml/dist/cgi.c (revision 275397) @@ -1,1150 +1,1159 @@ -/* $Id: cgi.c,v 1.92 2014/08/05 15:29:30 schwarze Exp $ */ +/* $Id: cgi.c,v 1.102 2014/11/26 17:55:27 schwarze Exp $ */ /* * Copyright (c) 2011, 2012 Kristaps Dzonsons * Copyright (c) 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include +#include + #include #include #include #include #include #include #include #include #include #include "mandoc.h" #include "mandoc_aux.h" #include "main.h" #include "manpath.h" #include "mansearch.h" #include "cgi.h" /* * A query as passed to the search function. */ struct query { char *manpath; /* desired manual directory */ char *arch; /* architecture */ char *sec; /* manual section */ char *query; /* unparsed query expression */ int equal; /* match whole names, not substrings */ }; struct req { struct query q; char **p; /* array of available manpaths */ size_t psz; /* number of available manpaths */ }; static void catman(const struct req *, const char *); static void format(const struct req *, const char *); static void html_print(const char *); static void html_putchar(char); static int http_decode(char *); static void http_parse(struct req *, const char *); static void http_print(const char *); static void http_putchar(char); static void http_printquery(const struct req *, const char *); static void pathgen(struct req *); static void pg_error_badrequest(const char *); static void pg_error_internal(void); static void pg_index(const struct req *); static void pg_noresult(const struct req *, const char *); static void pg_search(const struct req *); static void pg_searchres(const struct req *, struct manpage *, size_t); static void pg_show(struct req *, const char *); static void resp_begin_html(int, const char *); static void resp_begin_http(int, const char *); static void resp_end_html(void); static void resp_searchform(const struct req *); static void resp_show(const struct req *, const char *); static void set_query_attr(char **, char **); static int validate_filename(const char *); static int validate_manpath(const struct req *, const char *); static int validate_urifrag(const char *); static const char *scriptname; /* CGI script name */ static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; static const char *const sec_numbers[] = { "0", "1", "2", "3", "3p", "4", "5", "6", "7", "8", "9" }; static const char *const sec_names[] = { "All Sections", "1 - General Commands", "2 - System Calls", - "3 - Subroutines", - "3p - Perl Subroutines", - "4 - Special Files", + "3 - Library Functions", + "3p - Perl Library", + "4 - Device Drivers", "5 - File Formats", "6 - Games", - "7 - Macros and Conventions", - "8 - Maintenance Commands", - "9 - Kernel Interface" + "7 - Miscellaneous Information", + "8 - System Manager\'s Manual", + "9 - Kernel Developer\'s Manual" }; static const int sec_MAX = sizeof(sec_names) / sizeof(char *); static const char *const arch_names[] = { "amd64", "alpha", "armish", "armv7", "aviion", "hppa", "hppa64", "i386", "ia64", "landisk", "loongson", "luna88k", "macppc", "mips64", "octeon", "sgi", "socppc", "solbourne", "sparc", "sparc64", "vax", "zaurus", "amiga", "arc", "arm32", "atari", "beagle", "cats", "hp300", "mac68k", "mvme68k", "mvme88k", "mvmeppc", "palm", "pc532", "pegasos", "pmax", "powerpc", "sun3", "wgrisc", "x68k" }; static const int arch_MAX = sizeof(arch_names) / sizeof(char *); /* * Print a character, escaping HTML along the way. * This will pass non-ASCII straight to output: be warned! */ static void html_putchar(char c) { switch (c) { case ('"'): printf(""e;"); break; case ('&'): printf("&"); break; case ('>'): printf(">"); break; case ('<'): printf("<"); break; default: putchar((unsigned char)c); break; } } static void http_printquery(const struct req *req, const char *sep) { if (NULL != req->q.query) { printf("query="); http_print(req->q.query); } if (0 == req->q.equal) printf("%sapropos=1", sep); if (NULL != req->q.sec) { printf("%ssec=", sep); http_print(req->q.sec); } if (NULL != req->q.arch) { printf("%sarch=", sep); http_print(req->q.arch); } - if (NULL != req->q.manpath && - strcmp(req->q.manpath, req->p[0])) { + if (strcmp(req->q.manpath, req->p[0])) { printf("%smanpath=", sep); http_print(req->q.manpath); } } static void http_print(const char *p) { if (NULL == p) return; while ('\0' != *p) http_putchar(*p++); } /* * Call through to html_putchar(). * Accepts NULL strings. */ static void html_print(const char *p) { if (NULL == p) return; while ('\0' != *p) html_putchar(*p++); } /* * Transfer the responsibility for the allocated string *val * to the query structure. */ static void set_query_attr(char **attr, char **val) { free(*attr); if (**val == '\0') { *attr = NULL; free(*val); } else *attr = *val; *val = NULL; } /* * Parse the QUERY_STRING for key-value pairs * and store the values into the query structure. */ static void http_parse(struct req *req, const char *qs) { char *key, *val; size_t keysz, valsz; req->q.manpath = NULL; req->q.arch = NULL; req->q.sec = NULL; req->q.query = NULL; req->q.equal = 1; key = val = NULL; while (*qs != '\0') { /* Parse one key. */ keysz = strcspn(qs, "=;&"); key = mandoc_strndup(qs, keysz); qs += keysz; if (*qs != '=') goto next; /* Parse one value. */ valsz = strcspn(++qs, ";&"); val = mandoc_strndup(qs, valsz); qs += valsz; /* Decode and catch encoding errors. */ if ( ! (http_decode(key) && http_decode(val))) goto next; /* Handle key-value pairs. */ if ( ! strcmp(key, "query")) set_query_attr(&req->q.query, &val); else if ( ! strcmp(key, "apropos")) req->q.equal = !strcmp(val, "0"); else if ( ! strcmp(key, "manpath")) { #ifdef COMPAT_OLDURI if ( ! strncmp(val, "OpenBSD ", 8)) { val[7] = '-'; if ('C' == val[8]) val[8] = 'c'; } #endif set_query_attr(&req->q.manpath, &val); } else if ( ! (strcmp(key, "sec") #ifdef COMPAT_OLDURI && strcmp(key, "sektion") #endif )) { if ( ! strcmp(val, "0")) *val = '\0'; set_query_attr(&req->q.sec, &val); } else if ( ! strcmp(key, "arch")) { if ( ! strcmp(val, "default")) *val = '\0'; set_query_attr(&req->q.arch, &val); } /* * The key must be freed in any case. * The val may have been handed over to the query * structure, in which case it is now NULL. */ next: free(key); key = NULL; free(val); val = NULL; if (*qs != '\0') qs++; } - - /* Fall back to the default manpath. */ - - if (req->q.manpath == NULL) - req->q.manpath = mandoc_strdup(req->p[0]); } static void http_putchar(char c) { if (isalnum((unsigned char)c)) { putchar((unsigned char)c); return; } else if (' ' == c) { putchar('+'); return; } printf("%%%.2x", c); } /* * HTTP-decode a string. The standard explanation is that this turns * "%4e+foo" into "n foo" in the regular way. This is done in-place * over the allocated string. */ static int http_decode(char *p) { char hex[3]; char *q; int c; hex[2] = '\0'; q = p; for ( ; '\0' != *p; p++, q++) { if ('%' == *p) { if ('\0' == (hex[0] = *(p + 1))) return(0); if ('\0' == (hex[1] = *(p + 2))) return(0); if (1 != sscanf(hex, "%x", &c)) return(0); if ('\0' == c) return(0); *q = (char)c; p += 2; } else *q = '+' == *p ? ' ' : *p; } *q = '\0'; return(1); } static void resp_begin_http(int code, const char *msg) { if (200 != code) printf("Status: %d %s\r\n", code, msg); printf("Content-Type: text/html; charset=utf-8\r\n" "Cache-Control: no-cache\r\n" "Pragma: no-cache\r\n" "\r\n"); fflush(stdout); } static void resp_begin_html(int code, const char *msg) { resp_begin_http(code, msg); - printf("\n" + printf("\n" "\n" "\n" - "\n" + "\n" "\n" "\n" "%s\n" "\n" "\n" "\n", CSS_DIR, CSS_DIR, CUSTOMIZE_TITLE); } static void resp_end_html(void) { puts("\n" ""); } static void resp_searchform(const struct req *req) { int i; puts(CUSTOMIZE_BEGIN); puts(""); printf("
\n" "
\n" "
\n" "Manual Page Search Parameters\n", scriptname); /* Write query input box. */ printf( "
\n" "q.query) html_print(req->q.query); puts("\" SIZE=\"40\">"); /* Write submission and reset buttons. */ printf( "\n" "\n"); /* Write show radio button */ printf( "\n" "q.equal) printf("CHECKED=\"checked\" "); printf( "NAME=\"apropos\" ID=\"show\" VALUE=\"0\">\n" "\n"); /* Write section selector. */ puts( "
\n" ""); /* Write architecture selector. */ printf( ""); /* Write manpath selector. */ if (req->psz > 1) { puts(""); } /* Write search radio button */ printf( "\n" "q.equal) printf("CHECKED=\"checked\" "); printf( "NAME=\"apropos\" ID=\"search\" VALUE=\"1\">\n" "\n"); puts("
\n" "
\n" "
\n" "
"); puts(""); } static int validate_urifrag(const char *frag) { while ('\0' != *frag) { if ( ! (isalnum((unsigned char)*frag) || '-' == *frag || '.' == *frag || '/' == *frag || '_' == *frag)) return(0); frag++; } return(1); } static int validate_manpath(const struct req *req, const char* manpath) { size_t i; if ( ! strcmp(manpath, "mandoc")) return(1); for (i = 0; i < req->psz; i++) if ( ! strcmp(manpath, req->p[i])) return(1); return(0); } static int validate_filename(const char *file) { if ('.' == file[0] && '/' == file[1]) file += 2; return ( ! (strstr(file, "../") || strstr(file, "/..") || (strncmp(file, "man", 3) && strncmp(file, "cat", 3)))); } static void pg_index(const struct req *req) { resp_begin_html(200, NULL); resp_searchform(req); printf("

\n" "This web interface is documented in the\n" "man.cgi\n" "manual, and the\n" "apropos\n" "manual explains the query syntax.\n" "

\n", scriptname, scriptname); resp_end_html(); } static void pg_noresult(const struct req *req, const char *msg) { resp_begin_html(200, NULL); resp_searchform(req); puts("

"); puts(msg); puts("

"); resp_end_html(); } static void pg_error_badrequest(const char *msg) { resp_begin_html(400, "Bad Request"); puts("

Bad Request

\n" "

\n"); puts(msg); printf("Try again from the\n" "main page.\n" "

", scriptname); resp_end_html(); } static void pg_error_internal(void) { resp_begin_html(500, "Internal Server Error"); puts("

Internal Server Error

"); resp_end_html(); } static void pg_searchres(const struct req *req, struct manpage *r, size_t sz) { char *arch, *archend; size_t i, iuse, isec; int archprio, archpriouse; int prio, priouse; char sec; for (i = 0; i < sz; i++) { if (validate_filename(r[i].file)) continue; fprintf(stderr, "invalid filename %s in %s database\n", r[i].file, req->q.manpath); pg_error_internal(); return; } if (1 == sz) { /* * If we have just one result, then jump there now * without any delay. */ printf("Status: 303 See Other\r\n"); printf("Location: http://%s%s/%s/%s?", HTTP_HOST, scriptname, req->q.manpath, r[0].file); http_printquery(req, "&"); printf("\r\n" "Content-Type: text/html; charset=utf-8\r\n" "\r\n"); return; } resp_begin_html(200, NULL); resp_searchform(req); puts("
"); puts(""); for (i = 0; i < sz; i++) { printf("\n" "\n" "\n" ""); } puts("
\n" "q.manpath, r[i].file); http_printquery(req, "&"); printf("\">"); html_print(r[i].names); printf("\n" ""); html_print(r[i].output); puts("
\n" "
"); /* * In man(1) mode, show one of the pages * even if more than one is found. */ if (req->q.equal) { puts("
"); iuse = 0; priouse = 10; archpriouse = 3; for (i = 0; i < sz; i++) { isec = strcspn(r[i].file, "123456789"); sec = r[i].file[isec]; if ('\0' == sec) continue; prio = sec_prios[sec - '1']; if (NULL == req->q.arch) { archprio = (NULL == (arch = strchr( r[i].file + isec, '/'))) ? 3 : (NULL == (archend = strchr( arch + 1, '/'))) ? 0 : strncmp(arch, "amd64/", archend - arch) ? 2 : 1; if (archprio < archpriouse) { archpriouse = archprio; priouse = prio; iuse = i; continue; } if (archprio > archpriouse) continue; } if (prio >= priouse) continue; priouse = prio; iuse = i; } resp_show(req, r[iuse].file); } resp_end_html(); } static void catman(const struct req *req, const char *file) { FILE *f; size_t len; int i; char *p; int italic, bold; if (NULL == (f = fopen(file, "r"))) { puts("

You specified an invalid manual file.

"); return; } puts("
\n" "
");
 
 	while (NULL != (p = fgetln(f, &len))) {
 		bold = italic = 0;
 		for (i = 0; i < (int)len - 1; i++) {
 			/* 
 			 * This means that the catpage is out of state.
 			 * Ignore it and keep going (although the
 			 * catpage is bogus).
 			 */
 
 			if ('\b' == p[i] || '\n' == p[i])
 				continue;
 
 			/*
 			 * Print a regular character.
 			 * Close out any bold/italic scopes.
 			 * If we're in back-space mode, make sure we'll
 			 * have something to enter when we backspace.
 			 */
 
 			if ('\b' != p[i + 1]) {
 				if (italic)
 					printf("");
 				if (bold)
 					printf("");
 				italic = bold = 0;
 				html_putchar(p[i]);
 				continue;
 			} else if (i + 2 >= (int)len)
 				continue;
 
 			/* Italic mode. */
 
 			if ('_' == p[i]) {
 				if (bold)
 					printf("");
 				if ( ! italic)
 					printf("");
 				bold = 0;
 				italic = 1;
 				i += 2;
 				html_putchar(p[i]);
 				continue;
 			}
 
 			/* 
 			 * Handle funny behaviour troff-isms.
 			 * These grok'd from the original man2html.c.
 			 */
 
 			if (('+' == p[i] && 'o' == p[i + 2]) ||
 					('o' == p[i] && '+' == p[i + 2]) ||
 					('|' == p[i] && '=' == p[i + 2]) ||
 					('=' == p[i] && '|' == p[i + 2]) ||
 					('*' == p[i] && '=' == p[i + 2]) ||
 					('=' == p[i] && '*' == p[i + 2]) ||
 					('*' == p[i] && '|' == p[i + 2]) ||
 					('|' == p[i] && '*' == p[i + 2]))  {
 				if (italic)
 					printf("");
 				if (bold)
 					printf("");
 				italic = bold = 0;
 				putchar('*');
 				i += 2;
 				continue;
 			} else if (('|' == p[i] && '-' == p[i + 2]) ||
 					('-' == p[i] && '|' == p[i + 1]) ||
 					('+' == p[i] && '-' == p[i + 1]) ||
 					('-' == p[i] && '+' == p[i + 1]) ||
 					('+' == p[i] && '|' == p[i + 1]) ||
 					('|' == p[i] && '+' == p[i + 1]))  {
 				if (italic)
 					printf("");
 				if (bold)
 					printf("");
 				italic = bold = 0;
 				putchar('+');
 				i += 2;
 				continue;
 			}
 
 			/* Bold mode. */
 			
 			if (italic)
 				printf("");
 			if ( ! bold)
 				printf("");
 			bold = 1;
 			italic = 0;
 			i += 2;
 			html_putchar(p[i]);
 		}
 
 		/* 
 		 * Clean up the last character.
 		 * We can get to a newline; don't print that. 
 		 */
 
 		if (italic)
 			printf("");
 		if (bold)
 			printf("");
 
 		if (i == (int)len - 1 && '\n' != p[i])
 			html_putchar(p[i]);
 
 		putchar('\n');
 	}
 
 	puts("
\n" "
"); fclose(f); } static void format(const struct req *req, const char *file) { struct mparse *mp; + struct mchars *mchars; struct mdoc *mdoc; struct man *man; void *vp; char *opts; enum mandoclevel rc; int fd; int usepath; if (-1 == (fd = open(file, O_RDONLY, 0))) { puts("

You specified an invalid manual file.

"); return; } + mchars = mchars_alloc(); mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL, - req->q.manpath); + mchars, req->q.manpath); rc = mparse_readfd(mp, fd, file); close(fd); if (rc >= MANDOCLEVEL_FATAL) { fprintf(stderr, "fatal mandoc error: %s/%s\n", req->q.manpath, file); pg_error_internal(); return; } usepath = strcmp(req->q.manpath, req->p[0]); mandoc_asprintf(&opts, "fragment,man=%s?query=%%N&sec=%%S%s%s%s%s", scriptname, req->q.arch ? "&arch=" : "", req->q.arch ? req->q.arch : "", usepath ? "&manpath=" : "", usepath ? req->q.manpath : ""); mparse_result(mp, &mdoc, &man, NULL); if (NULL == man && NULL == mdoc) { fprintf(stderr, "fatal mandoc error: %s/%s\n", req->q.manpath, file); pg_error_internal(); mparse_free(mp); + mchars_free(mchars); return; } - vp = html_alloc(opts); + vp = html_alloc(mchars, opts); if (NULL != mdoc) html_mdoc(vp, mdoc); else html_man(vp, man); html_free(vp); mparse_free(mp); + mchars_free(mchars); free(opts); } static void resp_show(const struct req *req, const char *file) { if ('.' == file[0] && '/' == file[1]) file += 2; if ('c' == *file) catman(req, file); else format(req, file); } static void pg_show(struct req *req, const char *fullpath) { char *manpath; const char *file; if ((file = strchr(fullpath, '/')) == NULL) { pg_error_badrequest( "You did not specify a page to show."); return; } manpath = mandoc_strndup(fullpath, file - fullpath); file++; if ( ! validate_manpath(req, manpath)) { pg_error_badrequest( "You specified an invalid manpath."); free(manpath); return; } /* * Begin by chdir()ing into the manpath. * This way we can pick up the database files, which are * relative to the manpath root. */ if (chdir(manpath) == -1) { fprintf(stderr, "chdir %s: %s\n", manpath, strerror(errno)); pg_error_internal(); free(manpath); return; } if (strcmp(manpath, "mandoc")) { free(req->q.manpath); req->q.manpath = manpath; } else free(manpath); if ( ! validate_filename(file)) { pg_error_badrequest( "You specified an invalid manual file."); return; } resp_begin_html(200, NULL); resp_searchform(req); resp_show(req, file); resp_end_html(); } static void pg_search(const struct req *req) { struct mansearch search; struct manpaths paths; struct manpage *res; - char **cp; - const char *ep, *start; + char **argv; + char *query, *rp, *wp; size_t ressz; - int i, sz; + int argc; /* * Begin by chdir()ing into the root of the manpath. * This way we can pick up the database files, which are * relative to the manpath root. */ if (-1 == (chdir(req->q.manpath))) { fprintf(stderr, "chdir %s: %s\n", req->q.manpath, strerror(errno)); pg_error_internal(); return; } search.arch = req->q.arch; search.sec = req->q.sec; - search.deftype = req->q.equal ? TYPE_Nm : (TYPE_Nm | TYPE_Nd); - search.flags = req->q.equal ? MANSEARCH_MAN : 0; + search.outkey = "Nd"; + search.argmode = req->q.equal ? ARG_NAME : ARG_EXPR; + search.firstmatch = 1; paths.sz = 1; paths.paths = mandoc_malloc(sizeof(char *)); paths.paths[0] = mandoc_strdup("."); /* - * Poor man's tokenisation: just break apart by spaces. - * Yes, this is half-ass. But it works for now. + * Break apart at spaces with backslash-escaping. */ - ep = req->q.query; - while (ep && isspace((unsigned char)*ep)) - ep++; - - sz = 0; - cp = NULL; - while (ep && '\0' != *ep) { - cp = mandoc_reallocarray(cp, sz + 1, sizeof(char *)); - start = ep; - while ('\0' != *ep && ! isspace((unsigned char)*ep)) - ep++; - cp[sz] = mandoc_malloc((ep - start) + 1); - memcpy(cp[sz], start, ep - start); - cp[sz++][ep - start] = '\0'; - while (isspace((unsigned char)*ep)) - ep++; + argc = 0; + argv = NULL; + rp = query = mandoc_strdup(req->q.query); + for (;;) { + while (isspace((unsigned char)*rp)) + rp++; + if (*rp == '\0') + break; + argv = mandoc_reallocarray(argv, argc + 1, sizeof(char *)); + argv[argc++] = wp = rp; + for (;;) { + if (isspace((unsigned char)*rp)) { + *wp = '\0'; + rp++; + break; + } + if (rp[0] == '\\' && rp[1] != '\0') + rp++; + if (wp != rp) + *wp = *rp; + if (*rp == '\0') + break; + wp++; + rp++; + } } - if (0 == mansearch(&search, &paths, sz, cp, "Nd", &res, &ressz)) + if (0 == mansearch(&search, &paths, argc, argv, &res, &ressz)) pg_noresult(req, "You entered an invalid query."); else if (0 == ressz) pg_noresult(req, "No results found."); else pg_searchres(req, res, ressz); - for (i = 0; i < sz; i++) - free(cp[i]); - free(cp); - - for (i = 0; i < (int)ressz; i++) { - free(res[i].file); - free(res[i].names); - free(res[i].output); - } - free(res); - + free(query); + mansearch_free(res, ressz); free(paths.paths[0]); free(paths.paths); } int main(void) { struct req req; + struct itimerval itimer; const char *path; const char *querystring; int i; + /* Poor man's ReDoS mitigation. */ + + itimer.it_value.tv_sec = 2; + itimer.it_value.tv_usec = 0; + itimer.it_interval.tv_sec = 2; + itimer.it_interval.tv_usec = 0; + if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) == -1) { + fprintf(stderr, "setitimer: %s\n", strerror(errno)); + pg_error_internal(); + return(EXIT_FAILURE); + } + /* Scan our run-time environment. */ if (NULL == (scriptname = getenv("SCRIPT_NAME"))) scriptname = ""; if ( ! validate_urifrag(scriptname)) { fprintf(stderr, "unsafe SCRIPT_NAME \"%s\"\n", scriptname); pg_error_internal(); return(EXIT_FAILURE); } /* * First we change directory into the MAN_DIR so that * subsequent scanning for manpath directories is rooted * relative to the same position. */ if (-1 == chdir(MAN_DIR)) { fprintf(stderr, "MAN_DIR: %s: %s\n", MAN_DIR, strerror(errno)); pg_error_internal(); return(EXIT_FAILURE); } memset(&req, 0, sizeof(struct req)); pathgen(&req); /* Next parse out the query string. */ if (NULL != (querystring = getenv("QUERY_STRING"))) http_parse(&req, querystring); - if ( ! (NULL == req.q.manpath || - validate_manpath(&req, req.q.manpath))) { + if (req.q.manpath == NULL) + req.q.manpath = mandoc_strdup(req.p[0]); + else if ( ! validate_manpath(&req, req.q.manpath)) { pg_error_badrequest( "You specified an invalid manpath."); return(EXIT_FAILURE); } if ( ! (NULL == req.q.arch || validate_urifrag(req.q.arch))) { pg_error_badrequest( "You specified an invalid architecture."); return(EXIT_FAILURE); } /* Dispatch to the three different pages. */ path = getenv("PATH_INFO"); if (NULL == path) path = ""; else if ('/' == *path) path++; if ('\0' != *path) pg_show(&req, path); else if (NULL != req.q.query) pg_search(&req); else pg_index(&req); free(req.q.manpath); free(req.q.arch); free(req.q.sec); free(req.q.query); for (i = 0; i < (int)req.psz; i++) free(req.p[i]); free(req.p); return(EXIT_SUCCESS); } /* * Scan for indexable paths. */ static void pathgen(struct req *req) { FILE *fp; char *dp; size_t dpsz; if (NULL == (fp = fopen("manpath.conf", "r"))) { fprintf(stderr, "%s/manpath.conf: %s\n", MAN_DIR, strerror(errno)); pg_error_internal(); exit(EXIT_FAILURE); } while (NULL != (dp = fgetln(fp, &dpsz))) { if ('\n' == dp[dpsz - 1]) dpsz--; req->p = mandoc_realloc(req->p, (req->psz + 1) * sizeof(char *)); dp = mandoc_strndup(dp, dpsz); if ( ! validate_urifrag(dp)) { fprintf(stderr, "%s/manpath.conf contains " "unsafe path \"%s\"\n", MAN_DIR, dp); pg_error_internal(); exit(EXIT_FAILURE); } if (NULL != strchr(dp, '/')) { fprintf(stderr, "%s/manpath.conf contains " "path with slash \"%s\"\n", MAN_DIR, dp); pg_error_internal(); exit(EXIT_FAILURE); } req->p[req->psz++] = dp; } if ( req->p == NULL ) { fprintf(stderr, "%s/manpath.conf is empty\n", MAN_DIR); pg_error_internal(); exit(EXIT_FAILURE); } } Index: vendor/mdocml/dist/chars.c =================================================================== --- vendor/mdocml/dist/chars.c (revision 275396) +++ vendor/mdocml/dist/chars.c (revision 275397) @@ -1,180 +1,175 @@ -/* $Id: chars.c,v 1.58 2014/07/23 15:00:08 schwarze Exp $ */ +/* $Id: chars.c,v 1.65 2014/10/29 00:17:43 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons - * Copyright (c) 2011 Ingo Schwarze + * Copyright (c) 2011, 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include + #include #include #include #include #include "mandoc.h" #include "mandoc_aux.h" #include "libmandoc.h" #define PRINT_HI 126 #define PRINT_LO 32 struct ln { struct ln *next; const char *code; const char *ascii; int unicode; }; #define LINES_MAX 330 #define CHAR(in, ch, code) \ { NULL, (in), (ch), (code) }, #define CHAR_TBL_START static struct ln lines[LINES_MAX] = { #define CHAR_TBL_END }; #include "chars.in" struct mchars { struct ln **htab; }; static const struct ln *find(const struct mchars *, const char *, size_t); void mchars_free(struct mchars *arg) { free(arg->htab); free(arg); } struct mchars * mchars_alloc(void) { struct mchars *tab; struct ln **htab; struct ln *pp; int i, hash; /* * Constructs a very basic chaining hashtable. The hash routine * is simply the integral value of the first character. * Subsequent entries are chained in the order they're processed. */ tab = mandoc_malloc(sizeof(struct mchars)); 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; if (NULL == (pp = htab[hash])) { htab[hash] = &lines[i]; continue; } for ( ; pp->next; pp = pp->next) /* Scan ahead. */ ; pp->next = &lines[i]; } tab->htab = htab; return(tab); } int mchars_spec2cp(const struct mchars *arg, const char *p, size_t sz) { const struct ln *ln; ln = find(arg, p, sz); - if (NULL == ln) - return(-1); - return(ln->unicode); + return(ln != NULL ? ln->unicode : sz == 1 ? (unsigned char)*p : -1); } -char +int mchars_num2char(const char *p, size_t sz) { int i; - if ((i = mandoc_strntoi(p, sz, 10)) < 0) - return('\0'); - - return(i > 0 && i < 256 && isprint(i) ? i : '\0'); + i = mandoc_strntoi(p, sz, 10); + return(i >= 0 && i < 256 ? i : -1); } int mchars_num2uc(const char *p, size_t sz) { int i; - if ((i = mandoc_strntoi(p, sz, 16)) < 0) - return('\0'); - - /* - * Security warning: - * Never extend the range of accepted characters - * to overlap with the ASCII range, 0x00-0x7F - * without re-auditing the callers of this function. - * Some callers might relay on the fact that we never - * return ASCII characters for their escaping decisions. - * - * XXX Code is missing here to exclude bogus ranges. - */ - - return(i > 0x80 && i <= 0x10FFFF ? i : '\0'); + i = mandoc_strntoi(p, sz, 16); + assert(i >= 0 && i <= 0x10FFFF); + return(i); } const char * mchars_spec2str(const struct mchars *arg, const char *p, size_t sz, size_t *rsz) { const struct ln *ln; ln = find(arg, p, sz); - if (NULL == ln) { + if (ln == NULL) { *rsz = 1; - return(NULL); + return(sz == 1 ? p : NULL); } *rsz = strlen(ln->ascii); return(ln->ascii); +} + +const char * +mchars_uc2str(int uc) +{ + int i; + + for (i = 0; i < LINES_MAX; i++) + if (uc == lines[i].unicode) + return(lines[i].ascii); + return(""); } static const struct ln * find(const struct mchars *tab, const char *p, size_t sz) { const struct ln *pp; int hash; assert(p); if (0 == sz || p[0] < PRINT_LO || p[0] > PRINT_HI) return(NULL); hash = (int)p[0] - PRINT_LO; for (pp = tab->htab[hash]; pp; pp = pp->next) if (0 == strncmp(pp->code, p, sz) && '\0' == pp->code[(int)sz]) return(pp); return(NULL); } Index: vendor/mdocml/dist/chars.in =================================================================== --- vendor/mdocml/dist/chars.in (revision 275396) +++ vendor/mdocml/dist/chars.in (revision 275397) @@ -1,402 +1,402 @@ -/* $Id: chars.in,v 1.46 2014/04/20 16:46:04 schwarze Exp $ */ +/* $Id: chars.in,v 1.49 2014/11/06 22:28:36 schwarze Exp $ */ /* * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * The ASCII translation tables. * * The left-hand side corresponds to the input sequence (\x, \(xx, \*(xx * and so on) whose length is listed second element. The right-hand * side is what's produced by the front-end, with the fourth element * being its length. * * XXX - C-escape strings! * XXX - update LINES_MAX if adding more! */ /* Special break control characters. */ static const char ascii_nbrsp[2] = { ASCII_NBRSP, '\0' }; static const char ascii_break[2] = { ASCII_BREAK, '\0' }; CHAR_TBL_START /* Spacing. */ CHAR(" ", ascii_nbrsp, 160) CHAR("~", ascii_nbrsp, 160) CHAR("0", " ", 8194) CHAR("|", "", 0) CHAR("^", "", 0) CHAR("&", "", 0) CHAR("%", "", 0) CHAR(":", ascii_break, 0) /* XXX The following three do not really belong into this file. */ CHAR("t", "", 0) CHAR("c", "", 0) CHAR("}", "", 0) /* Accents. */ CHAR("a\"", "\"", 733) CHAR("a-", "-", 175) CHAR("a.", ".", 729) CHAR("a^", "^", 94) CHAR("\'", "\'", 180) CHAR("aa", "\'", 180) CHAR("ga", "`", 96) CHAR("`", "`", 96) -CHAR("ab", "`", 728) +CHAR("ab", "'\b`", 728) CHAR("ac", ",", 184) CHAR("ad", "\"", 168) CHAR("ah", "v", 711) CHAR("ao", "o", 730) CHAR("a~", "~", 126) CHAR("ho", ",", 731) CHAR("ha", "^", 94) CHAR("ti", "~", 126) /* Quotes. */ CHAR("Bq", ",,", 8222) CHAR("bq", ",", 8218) CHAR("lq", "``", 8220) CHAR("rq", "\'\'", 8221) CHAR("oq", "`", 8216) CHAR("cq", "\'", 8217) CHAR("aq", "\'", 39) CHAR("dq", "\"", 34) CHAR("Fo", "<<", 171) CHAR("Fc", ">>", 187) CHAR("fo", "<", 8249) CHAR("fc", ">", 8250) /* Brackets. */ CHAR("lB", "[", 91) CHAR("rB", "]", 93) CHAR("lC", "{", 123) CHAR("rC", "}", 125) -CHAR("la", "<", 60) -CHAR("ra", ">", 62) +CHAR("la", "<", 10216) +CHAR("ra", ">", 10217) CHAR("bv", "|", 9130) CHAR("braceex", "|", 9130) CHAR("bracketlefttp", "|", 9121) CHAR("bracketleftbp", "|", 9123) CHAR("bracketleftex", "|", 9122) CHAR("bracketrighttp", "|", 9124) CHAR("bracketrightbp", "|", 9126) CHAR("bracketrightex", "|", 9125) CHAR("lt", ",-", 9127) CHAR("bracelefttp", ",-", 9127) CHAR("lk", "{", 9128) CHAR("braceleftmid", "{", 9128) -CHAR("lb", ",-", 9129) +CHAR("lb", "`-", 9129) CHAR("braceleftbp", "`-", 9129) CHAR("braceleftex", "|", 9130) CHAR("rt", "-.", 9131) CHAR("bracerighttp", "-.", 9131) CHAR("rk", "}", 9132) CHAR("bracerightmid", "}", 9132) CHAR("rb", "-\'", 9133) CHAR("bracerightbp", "-\'", 9133) CHAR("bracerightex", "|", 9130) CHAR("parenlefttp", "/", 9115) CHAR("parenleftbp", "\\", 9117) CHAR("parenleftex", "|", 9116) CHAR("parenrighttp", "\\", 9118) CHAR("parenrightbp", "/", 9120) CHAR("parenrightex", "|", 9119) /* Greek characters. */ CHAR("*A", "A", 913) CHAR("*B", "B", 914) -CHAR("*G", "|", 915) -CHAR("*D", "/\\", 916) +CHAR("*G", "G", 915) +CHAR("*D", "_\b/_\b\\", 916) CHAR("*E", "E", 917) CHAR("*Z", "Z", 918) CHAR("*Y", "H", 919) -CHAR("*H", "O", 920) +CHAR("*H", "-\bO", 920) CHAR("*I", "I", 921) CHAR("*K", "K", 922) CHAR("*L", "/\\", 923) CHAR("*M", "M", 924) CHAR("*N", "N", 925) -CHAR("*C", "H", 926) +CHAR("*C", "_\bH", 926) CHAR("*O", "O", 927) CHAR("*P", "TT", 928) CHAR("*R", "P", 929) -CHAR("*S", ">", 931) +CHAR("*S", "S", 931) CHAR("*T", "T", 932) CHAR("*U", "Y", 933) -CHAR("*F", "O_", 934) +CHAR("*F", "I\bO", 934) CHAR("*X", "X", 935) -CHAR("*Q", "Y", 936) -CHAR("*W", "O", 937) +CHAR("*Q", "I\bY", 936) +CHAR("*W", "_\bO", 937) CHAR("*a", "a", 945) CHAR("*b", "B", 946) CHAR("*g", "y", 947) CHAR("*d", "d", 948) CHAR("*e", "e", 949) -CHAR("*z", "C", 950) +CHAR("*z", ",\bC", 950) CHAR("*y", "n", 951) -CHAR("*h", "0", 952) +CHAR("*h", "-\b0", 952) CHAR("*i", "i", 953) CHAR("*k", "k", 954) -CHAR("*l", "\\", 955) -CHAR("*m", "u", 956) +CHAR("*l", ">\b\\", 955) +CHAR("*m", ",\bu", 956) CHAR("*n", "v", 957) -CHAR("*c", "E", 958) +CHAR("*c", ",\bE", 958) CHAR("*o", "o", 959) -CHAR("*p", "n", 960) +CHAR("*p", "-\bn", 960) CHAR("*r", "p", 961) -CHAR("*s", "o", 963) -CHAR("*t", "t", 964) +CHAR("*s", "-\bo", 963) +CHAR("*t", "~\bt", 964) CHAR("*u", "u", 965) -CHAR("*f", "o", 981) +CHAR("*f", "|\bo", 981) CHAR("*x", "x", 967) -CHAR("*q", "u", 968) +CHAR("*q", "|\bu", 968) CHAR("*w", "w", 969) -CHAR("+h", "0", 977) -CHAR("+f", "o", 966) -CHAR("+p", "w", 982) +CHAR("+h", "-\b0", 977) +CHAR("+f", "|\bo", 966) +CHAR("+p", "-\bw", 982) CHAR("+e", "e", 1013) CHAR("ts", "s", 962) /* Accented letters. */ -CHAR(",C", "C", 199) -CHAR(",c", "c", 231) -CHAR("/L", "L", 321) -CHAR("/O", "O", 216) -CHAR("/l", "l", 322) -CHAR("/o", "o", 248) -CHAR("oA", "A", 197) -CHAR("oa", "a", 229) -CHAR(":A", "A", 196) -CHAR(":E", "E", 203) -CHAR(":I", "I", 207) -CHAR(":O", "O", 214) -CHAR(":U", "U", 220) -CHAR(":a", "a", 228) -CHAR(":e", "e", 235) -CHAR(":i", "i", 239) -CHAR(":o", "o", 246) -CHAR(":u", "u", 252) -CHAR(":y", "y", 255) -CHAR("\'A", "A", 193) -CHAR("\'E", "E", 201) -CHAR("\'I", "I", 205) -CHAR("\'O", "O", 211) -CHAR("\'U", "U", 218) -CHAR("\'a", "a", 225) -CHAR("\'e", "e", 233) -CHAR("\'i", "i", 237) -CHAR("\'o", "o", 243) -CHAR("\'u", "u", 250) -CHAR("^A", "A", 194) -CHAR("^E", "E", 202) -CHAR("^I", "I", 206) -CHAR("^O", "O", 212) -CHAR("^U", "U", 219) -CHAR("^a", "a", 226) -CHAR("^e", "e", 234) -CHAR("^i", "i", 238) -CHAR("^o", "o", 244) -CHAR("^u", "u", 251) -CHAR("`A", "A", 192) -CHAR("`E", "E", 200) -CHAR("`I", "I", 204) -CHAR("`O", "O", 210) -CHAR("`U", "U", 217) -CHAR("`a", "a", 224) -CHAR("`e", "e", 232) -CHAR("`i", "i", 236) -CHAR("`o", "o", 242) -CHAR("`u", "u", 249) -CHAR("~A", "A", 195) -CHAR("~N", "N", 209) -CHAR("~O", "O", 213) -CHAR("~a", "a", 227) -CHAR("~n", "n", 241) -CHAR("~o", "o", 245) +CHAR(",C", ",\bC", 199) +CHAR(",c", ",\bc", 231) +CHAR("/L", "/\bL", 321) +CHAR("/O", "/\bO", 216) +CHAR("/l", "/\bl", 322) +CHAR("/o", "/\bo", 248) +CHAR("oA", "o\bA", 197) +CHAR("oa", "o\ba", 229) +CHAR(":A", "\"\bA", 196) +CHAR(":E", "\"\bE", 203) +CHAR(":I", "\"\bI", 207) +CHAR(":O", "\"\bO", 214) +CHAR(":U", "\"\bU", 220) +CHAR(":a", "\"\ba", 228) +CHAR(":e", "\"\be", 235) +CHAR(":i", "\"\bi", 239) +CHAR(":o", "\"\bo", 246) +CHAR(":u", "\"\bu", 252) +CHAR(":y", "\"\by", 255) +CHAR("'A", "'\bA", 193) +CHAR("'E", "'\bE", 201) +CHAR("'I", "'\bI", 205) +CHAR("'O", "'\bO", 211) +CHAR("'U", "'\bU", 218) +CHAR("'a", "'\ba", 225) +CHAR("'e", "'\be", 233) +CHAR("'i", "'\bi", 237) +CHAR("'o", "'\bo", 243) +CHAR("'u", "'\bu", 250) +CHAR("^A", "^\bA", 194) +CHAR("^E", "^\bE", 202) +CHAR("^I", "^\bI", 206) +CHAR("^O", "^\bO", 212) +CHAR("^U", "^\bU", 219) +CHAR("^a", "^\ba", 226) +CHAR("^e", "^\be", 234) +CHAR("^i", "^\bi", 238) +CHAR("^o", "^\bo", 244) +CHAR("^u", "^\bu", 251) +CHAR("`A", "`\bA", 192) +CHAR("`E", "`\bE", 200) +CHAR("`I", "`\bI", 204) +CHAR("`O", "`\bO", 210) +CHAR("`U", "`\bU", 217) +CHAR("`a", "`\ba", 224) +CHAR("`e", "`\be", 232) +CHAR("`i", "`\bi", 236) +CHAR("`o", "`\bo", 242) +CHAR("`u", "`\bu", 249) +CHAR("~A", "~\bA", 195) +CHAR("~N", "~\bN", 209) +CHAR("~O", "~\bO", 213) +CHAR("~a", "~\ba", 227) +CHAR("~n", "~\bn", 241) +CHAR("~o", "~\bo", 245) /* Arrows and lines. */ CHAR("<-", "<-", 8592) CHAR("->", "->", 8594) -CHAR("<>", "<>", 8596) -CHAR("da", "v", 8595) -CHAR("ua", "^", 8593) +CHAR("<>", "<->", 8596) +CHAR("da", "|\bv", 8595) +CHAR("ua", "|\b^", 8593) CHAR("va", "^v", 8597) CHAR("lA", "<=", 8656) CHAR("rA", "=>", 8658) CHAR("hA", "<=>", 8660) -CHAR("dA", "v", 8659) -CHAR("uA", "^", 8657) +CHAR("dA", "=\bv", 8659) +CHAR("uA", "=\b^", 8657) CHAR("vA", "^=v", 8661) /* Logic. */ CHAR("AN", "^", 8743) CHAR("OR", "v", 8744) CHAR("no", "~", 172) CHAR("tno", "~", 172) CHAR("te", "3", 8707) -CHAR("fa", "V", 8704) +CHAR("fa", "-\bV", 8704) CHAR("st", "-)", 8715) CHAR("tf", ".:.", 8756) CHAR("3d", ".:.", 8756) CHAR("or", "|", 124) /* Mathematicals. */ CHAR("pl", "+", 43) CHAR("mi", "-", 8722) CHAR("-", "-", 45) CHAR("-+", "-+", 8723) CHAR("+-", "+-", 177) CHAR("t+-", "+-", 177) CHAR("pc", ".", 183) CHAR("md", ".", 8901) CHAR("mu", "x", 215) CHAR("tmu", "x", 215) -CHAR("c*", "x", 8855) -CHAR("c+", "+", 8853) +CHAR("c*", "O\bx", 8855) +CHAR("c+", "O\b+", 8853) CHAR("di", "-:-", 247) CHAR("tdi", "-:-", 247) CHAR("f/", "/", 8260) CHAR("**", "*", 8727) CHAR("<=", "<=", 8804) CHAR(">=", ">=", 8805) CHAR("<<", "<<", 8810) CHAR(">>", ">>", 8811) CHAR("eq", "=", 61) CHAR("!=", "!=", 8800) CHAR("==", "==", 8801) CHAR("ne", "!==", 8802) CHAR("=~", "=~", 8773) CHAR("-~", "-~", 8771) CHAR("ap", "~", 8764) CHAR("~~", "~~", 8776) CHAR("~=", "~=", 8780) CHAR("pt", "oc", 8733) CHAR("es", "{}", 8709) CHAR("mo", "E", 8712) CHAR("nm", "!E", 8713) CHAR("sb", "(=", 8834) CHAR("nb", "(!=", 8836) CHAR("sp", "=)", 8835) CHAR("nc", "!=)", 8837) -CHAR("ib", "(=", 8838) -CHAR("ip", "=)", 8839) +CHAR("ib", "(=\b_", 8838) +CHAR("ip", "=\b_)", 8839) CHAR("ca", "(^)", 8745) CHAR("cu", "U", 8746) -CHAR("/_", "/_", 8736) -CHAR("pp", "_|_", 8869) -CHAR("is", "I", 8747) -CHAR("integral", "I", 8747) +CHAR("/_", "_\b/", 8736) +CHAR("pp", "_\b|", 8869) +CHAR("is", "'\b,\bI", 8747) +CHAR("integral", "'\b,\bI", 8747) CHAR("sum", "E", 8721) CHAR("product", "TT", 8719) CHAR("coproduct", "U", 8720) CHAR("gr", "V", 8711) CHAR("sr", "\\/", 8730) CHAR("sqrt", "\\/", 8730) CHAR("lc", "|~", 8968) CHAR("rc", "~|", 8969) CHAR("lf", "|_", 8970) CHAR("rf", "_|", 8971) CHAR("if", "oo", 8734) CHAR("Ah", "N", 8501) CHAR("Im", "I", 8465) CHAR("Re", "R", 8476) CHAR("pd", "a", 8706) CHAR("-h", "/h", 8463) CHAR("12", "1/2", 189) CHAR("14", "1/4", 188) CHAR("34", "3/4", 190) /* Ligatures. */ CHAR("ff", "ff", 64256) CHAR("fi", "fi", 64257) CHAR("fl", "fl", 64258) CHAR("Fi", "ffi", 64259) CHAR("Fl", "ffl", 64260) CHAR("AE", "AE", 198) CHAR("ae", "ae", 230) CHAR("OE", "OE", 338) CHAR("oe", "oe", 339) CHAR("ss", "ss", 223) CHAR("IJ", "IJ", 306) CHAR("ij", "ij", 307) /* Special letters. */ -CHAR("-D", "D", 208) -CHAR("Sd", "o", 240) -CHAR("TP", "b", 222) -CHAR("Tp", "b", 254) +CHAR("-D", "-\bD", 208) +CHAR("Sd", "d", 240) +CHAR("TP", "Th", 222) +CHAR("Tp", "th", 254) CHAR(".i", "i", 305) CHAR(".j", "j", 567) /* Currency. */ CHAR("Do", "$", 36) -CHAR("ct", "c", 162) +CHAR("ct", "/\bc", 162) CHAR("Eu", "EUR", 8364) CHAR("eu", "EUR", 8364) -CHAR("Ye", "Y", 165) -CHAR("Po", "L", 163) -CHAR("Cs", "x", 164) -CHAR("Fn", "f", 402) +CHAR("Ye", "=\bY", 165) +CHAR("Po", "GBP", 163) +CHAR("Cs", "o\bx", 164) +CHAR("Fn", ",\bf", 402) /* Lines. */ CHAR("ba", "|", 124) CHAR("br", "|", 9474) CHAR("ul", "_", 95) CHAR("rl", "-", 8254) CHAR("bb", "|", 166) CHAR("sl", "/", 47) CHAR("rs", "\\", 92) /* Text markers. */ -CHAR("ci", "o", 9675) -CHAR("bu", "o", 8226) -CHAR("dd", "=", 8225) -CHAR("dg", "-", 8224) +CHAR("ci", "O", 9675) +CHAR("bu", "+\bo", 8226) +CHAR("dd", "|\b=", 8225) +CHAR("dg", "|\b-", 8224) CHAR("lz", "<>", 9674) CHAR("sq", "[]", 9633) -CHAR("ps", "9|", 182) -CHAR("sc", "S", 167) +CHAR("ps", "", 182) +CHAR("sc", "", 167) CHAR("lh", "<=", 9756) CHAR("rh", "=>", 9758) CHAR("at", "@", 64) CHAR("sh", "#", 35) CHAR("CR", "_|", 8629) CHAR("OK", "\\/", 10003) /* Legal symbols. */ CHAR("co", "(C)", 169) CHAR("rg", "(R)", 174) CHAR("tm", "tm", 8482) /* Punctuation. */ CHAR(".", ".", 46) -CHAR("r!", "i", 161) -CHAR("r?", "c", 191) +CHAR("r!", "!", 161) +CHAR("r?", "?", 191) CHAR("em", "--", 8212) CHAR("en", "-", 8211) CHAR("hy", "-", 8208) CHAR("e", "\\", 92) /* Units. */ -CHAR("de", "o", 176) +CHAR("de", "", 176) CHAR("%0", "%o", 8240) CHAR("fm", "\'", 8242) -CHAR("sd", "\"", 8243) -CHAR("mc", "mu", 181) +CHAR("sd", "''", 8243) +CHAR("mc", ",\bu", 181) CHAR_TBL_END Index: vendor/mdocml/dist/compat_fgetln.c =================================================================== --- vendor/mdocml/dist/compat_fgetln.c (revision 275396) +++ vendor/mdocml/dist/compat_fgetln.c (revision 275397) @@ -1,93 +1,94 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_FGETLN +#if HAVE_FGETLN int dummy; #else /* $NetBSD: fgetln.c,v 1.3 2006/09/25 07:18:17 lukem Exp $ */ /*- * Copyright (c) 1998 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Christos Zoulas. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of The NetBSD Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ + +#include + #include #include #include #include char * fgetln(fp, len) FILE *fp; size_t *len; { static char *buf = NULL; static size_t bufsiz = 0; char *ptr; if (buf == NULL) { bufsiz = BUFSIZ; if ((buf = malloc(bufsiz)) == NULL) return NULL; } if (fgets(buf, bufsiz, fp) == NULL) return NULL; *len = 0; while ((ptr = strchr(&buf[*len], '\n')) == NULL) { size_t nbufsiz = bufsiz + BUFSIZ; char *nbuf = realloc(buf, nbufsiz); if (nbuf == NULL) { int oerrno = errno; free(buf); errno = oerrno; buf = NULL; return NULL; } else buf = nbuf; *len = bufsiz; if (fgets(&buf[bufsiz], BUFSIZ, fp) == NULL) return buf; bufsiz = nbufsiz; } *len = (ptr - buf) + 1; return buf; } #endif Index: vendor/mdocml/dist/compat_fts.c =================================================================== --- vendor/mdocml/dist/compat_fts.c (nonexistent) +++ vendor/mdocml/dist/compat_fts.c (revision 275397) @@ -0,0 +1,826 @@ +#include "config.h" + +#if HAVE_FTS + +int dummy; + +#else + +/* $Id: compat_fts.c,v 1.4 2014/08/17 20:45:59 schwarze Exp $ */ +/* $OpenBSD: fts.c,v 1.46 2014/05/25 17:47:04 tedu Exp $ */ + +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "compat_fts.h" + +static FTSENT *fts_alloc(FTS *, const char *, size_t); +static FTSENT *fts_build(FTS *); +static void fts_lfree(FTSENT *); +static void fts_load(FTS *, FTSENT *); +static size_t fts_maxarglen(char * const *); +static void fts_padjust(FTS *, FTSENT *); +static int fts_palloc(FTS *, size_t); +static unsigned short fts_stat(FTS *, FTSENT *); +static int fts_safe_changedir(FTS *, FTSENT *, int, const char *); + +#define ISDOT(a) (a[0] == '.' && (!a[1] || (a[1] == '.' && !a[2]))) + +#define CLR(opt) (sp->fts_options &= ~(opt)) +#define ISSET(opt) (sp->fts_options & (opt)) +#define SET(opt) (sp->fts_options |= (opt)) + +#define FCHDIR(sp, fd) (!ISSET(FTS_NOCHDIR) && fchdir(fd)) + +FTS * +fts_open(char * const *argv, int options, void *dummy) +{ + FTS *sp; + FTSENT *p, *root; + int nitems; + FTSENT *parent, *tmp; + size_t len; + + /* Options check. */ + if (options & ~FTS_OPTIONMASK) { + errno = EINVAL; + return (NULL); + } + + /* Allocate/initialize the stream */ + if ((sp = calloc(1, sizeof(FTS))) == NULL) + return (NULL); + sp->fts_options = options; + + /* + * Start out with 1K of path space, and enough, in any case, + * to hold the user's paths. + */ + if (fts_palloc(sp, MAX(fts_maxarglen(argv), PATH_MAX))) + goto mem1; + + /* Allocate/initialize root's parent. */ + if ((parent = fts_alloc(sp, "", 0)) == NULL) + goto mem2; + parent->fts_level = FTS_ROOTPARENTLEVEL; + + /* Allocate/initialize root(s). */ + for (root = NULL, nitems = 0; *argv; ++argv, ++nitems) { + /* Don't allow zero-length paths. */ + if ((len = strlen(*argv)) == 0) { + errno = ENOENT; + goto mem3; + } + + if ((p = fts_alloc(sp, *argv, len)) == NULL) + goto mem3; + p->fts_level = FTS_ROOTLEVEL; + p->fts_parent = parent; + p->fts_accpath = p->fts_name; + p->fts_info = fts_stat(sp, p); + + /* Command-line "." and ".." are real directories. */ + if (p->fts_info == FTS_DOT) + p->fts_info = FTS_D; + + p->fts_link = NULL; + if (root == NULL) + tmp = root = p; + else { + tmp->fts_link = p; + tmp = p; + } + } + + /* + * Allocate a dummy pointer and make fts_read think that we've just + * finished the node before the root(s); set p->fts_info to FTS_INIT + * so that everything about the "current" node is ignored. + */ + if ((sp->fts_cur = fts_alloc(sp, "", 0)) == NULL) + goto mem3; + sp->fts_cur->fts_link = root; + sp->fts_cur->fts_info = FTS_INIT; + + /* + * If using chdir(2), grab a file descriptor pointing to dot to ensure + * that we can get back here; this could be avoided for some paths, + * but almost certainly not worth the effort. Slashes, symbolic links, + * and ".." are all fairly nasty problems. Note, if we can't get the + * descriptor we run anyway, just more slowly. + */ + if (!ISSET(FTS_NOCHDIR) && (sp->fts_rfd = open(".", O_RDONLY, 0)) < 0) + SET(FTS_NOCHDIR); + + if (nitems == 0) + free(parent); + + return (sp); + +mem3: fts_lfree(root); + free(parent); +mem2: free(sp->fts_path); +mem1: free(sp); + return (NULL); +} + +static void +fts_load(FTS *sp, FTSENT *p) +{ + size_t len; + char *cp; + + /* + * Load the stream structure for the next traversal. Since we don't + * actually enter the directory until after the preorder visit, set + * the fts_accpath field specially so the chdir gets done to the right + * place and the user can access the first node. From fts_open it's + * known that the path will fit. + */ + len = p->fts_pathlen = p->fts_namelen; + memmove(sp->fts_path, p->fts_name, len + 1); + if ((cp = strrchr(p->fts_name, '/')) && (cp != p->fts_name || cp[1])) { + len = strlen(++cp); + memmove(p->fts_name, cp, len + 1); + p->fts_namelen = len; + } + p->fts_accpath = p->fts_path = sp->fts_path; + sp->fts_dev = p->fts_dev; +} + +int +fts_close(FTS *sp) +{ + FTSENT *freep, *p; + int rfd, error = 0; + + /* + * This still works if we haven't read anything -- the dummy structure + * points to the root list, so we step through to the end of the root + * list which has a valid parent pointer. + */ + if (sp->fts_cur) { + for (p = sp->fts_cur; p->fts_level >= FTS_ROOTLEVEL;) { + freep = p; + p = p->fts_link ? p->fts_link : p->fts_parent; + free(freep); + } + free(p); + } + + /* Stash the original directory fd if needed. */ + rfd = ISSET(FTS_NOCHDIR) ? -1 : sp->fts_rfd; + + /* Free up child linked list, sort array, path buffer, stream ptr.*/ + if (sp->fts_child) + fts_lfree(sp->fts_child); + free(sp->fts_path); + free(sp); + + /* Return to original directory, checking for error. */ + if (rfd != -1) { + int saved_errno; + error = fchdir(rfd); + saved_errno = errno; + (void)close(rfd); + errno = saved_errno; + } + + return (error); +} + +/* + * Special case of "/" at the end of the path so that slashes aren't + * appended which would cause paths to be written as "....//foo". + */ +#define NAPPEND(p) \ + (p->fts_path[p->fts_pathlen - 1] == '/' \ + ? p->fts_pathlen - 1 : p->fts_pathlen) + +FTSENT * +fts_read(FTS *sp) +{ + FTSENT *p, *tmp; + int instr; + char *t; + + /* If finished or unrecoverable error, return NULL. */ + if (sp->fts_cur == NULL || ISSET(FTS_STOP)) + return (NULL); + + /* Set current node pointer. */ + p = sp->fts_cur; + + /* Save and zero out user instructions. */ + instr = p->fts_instr; + p->fts_instr = FTS_NOINSTR; + + /* Directory in pre-order. */ + if (p->fts_info == FTS_D) { + /* If skipped or crossed mount point, do post-order visit. */ + if (instr == FTS_SKIP || + (ISSET(FTS_XDEV) && p->fts_dev != sp->fts_dev)) { + if (sp->fts_child) { + fts_lfree(sp->fts_child); + sp->fts_child = NULL; + } + p->fts_info = FTS_DP; + return (p); + } + + /* + * Cd to the subdirectory. + * + * If have already read and now fail to chdir, whack the list + * to make the names come out right, and set the parent errno + * so the application will eventually get an error condition. + * Set the FTS_DONTCHDIR flag so that when we logically change + * directories back to the parent we don't do a chdir. + * + * If haven't read do so. If the read fails, fts_build sets + * FTS_STOP or the fts_info field of the node. + */ + if (sp->fts_child) { + if (fts_safe_changedir(sp, p, -1, p->fts_accpath)) { + p->fts_errno = errno; + p->fts_flags |= FTS_DONTCHDIR; + for (p = sp->fts_child; p; p = p->fts_link) + p->fts_accpath = + p->fts_parent->fts_accpath; + } + } else if ((sp->fts_child = fts_build(sp)) == NULL) { + if (ISSET(FTS_STOP)) + return (NULL); + return (p); + } + p = sp->fts_child; + sp->fts_child = NULL; + goto name; + } + + /* Move to the next node on this level. */ +next: tmp = p; + if ((p = p->fts_link)) { + free(tmp); + + /* + * If reached the top, return to the original directory (or + * the root of the tree), and load the paths for the next root. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + return (NULL); + } + fts_load(sp, p); + return (sp->fts_cur = p); + } + + /* + * User may have called fts_set on the node. If skipped, + * ignore. If followed, get a file descriptor so we can + * get back if necessary. + */ + if (p->fts_instr == FTS_SKIP) + goto next; + +name: t = sp->fts_path + NAPPEND(p->fts_parent); + *t++ = '/'; + memmove(t, p->fts_name, p->fts_namelen + 1); + return (sp->fts_cur = p); + } + + /* Move up to the parent node. */ + p = tmp->fts_parent; + free(tmp); + + if (p->fts_level == FTS_ROOTPARENTLEVEL) { + /* + * Done; free everything up and set errno to 0 so the user + * can distinguish between error and EOF. + */ + free(p); + errno = 0; + return (sp->fts_cur = NULL); + } + + /* NUL terminate the pathname. */ + sp->fts_path[p->fts_pathlen] = '\0'; + + /* + * Return to the parent directory. If at a root node or came through + * a symlink, go back through the file descriptor. Otherwise, cd up + * one directory. + */ + if (p->fts_level == FTS_ROOTLEVEL) { + if (FCHDIR(sp, sp->fts_rfd)) { + SET(FTS_STOP); + sp->fts_cur = p; + return (NULL); + } + } else if (!(p->fts_flags & FTS_DONTCHDIR) && + fts_safe_changedir(sp, p->fts_parent, -1, "..")) { + SET(FTS_STOP); + sp->fts_cur = p; + return (NULL); + } + p->fts_info = p->fts_errno ? FTS_ERR : FTS_DP; + return (sp->fts_cur = p); +} + +/* + * Fts_set takes the stream as an argument although it's not used in this + * implementation; it would be necessary if anyone wanted to add global + * semantics to fts using fts_set. An error return is allowed for similar + * reasons. + */ +/* ARGSUSED */ +int +fts_set(FTS *sp, FTSENT *p, int instr) +{ + if (instr && instr != FTS_NOINSTR && instr != FTS_SKIP) { + errno = EINVAL; + return (1); + } + p->fts_instr = instr; + return (0); +} + +/* + * This is the tricky part -- do not casually change *anything* in here. The + * idea is to build the linked list of entries that are used by fts_children + * and fts_read. There are lots of special cases. + * + * The real slowdown in walking the tree is the stat calls. If FTS_NOSTAT is + * set and it's a physical walk (so that symbolic links can't be directories), + * we can do things quickly. First, if it's a 4.4BSD file system, the type + * of the file is in the directory entry. Otherwise, we assume that the number + * of subdirectories in a node is equal to the number of links to the parent. + * The former skips all stat calls. The latter skips stat calls in any leaf + * directories and for any files after the subdirectories in the directory have + * been found, cutting the stat calls by about 2/3. + */ +static FTSENT * +fts_build(FTS *sp) +{ + struct dirent *dp; + FTSENT *p, *head; + FTSENT *cur, *tail; + DIR *dirp; + void *oldaddr; + size_t dlen, len, maxlen; + int nitems, cderrno, descend, level, nlinks, nostat, doadjust; + int saved_errno; + char *cp; + + /* Set current node pointer. */ + cur = sp->fts_cur; + + /* + * Open the directory for reading. If this fails, we're done. + * If being called from fts_read, set the fts_info field. + */ + if ((dirp = opendir(cur->fts_accpath)) == NULL) { + cur->fts_info = FTS_DNR; + cur->fts_errno = errno; + return (NULL); + } + + /* + * Nlinks is the number of possible entries of type directory in the + * directory if we're cheating on stat calls, 0 if we're not doing + * any stat calls at all, -1 if we're doing stats on everything. + */ + nlinks = -1; + nostat = 0; + + /* + * If we're going to need to stat anything or we want to descend + * and stay in the directory, chdir. If this fails we keep going, + * but set a flag so we don't chdir after the post-order visit. + * We won't be able to stat anything, but we can still return the + * names themselves. Note, that since fts_read won't be able to + * chdir into the directory, it will have to return different path + * names than before, i.e. "a/b" instead of "b". Since the node + * has already been visited in pre-order, have to wait until the + * post-order visit to return the error. There is a special case + * here, if there was nothing to stat then it's not an error to + * not be able to stat. This is all fairly nasty. If a program + * needed sorted entries or stat information, they had better be + * checking FTS_NS on the returned nodes. + */ + cderrno = 0; + if (fts_safe_changedir(sp, cur, dirfd(dirp), NULL)) { + if (nlinks) + cur->fts_errno = errno; + cur->fts_flags |= FTS_DONTCHDIR; + descend = 0; + cderrno = errno; + (void)closedir(dirp); + dirp = NULL; + } else + descend = 1; + + /* + * Figure out the max file name length that can be stored in the + * current path -- the inner loop allocates more path as necessary. + * We really wouldn't have to do the maxlen calculations here, we + * could do them in fts_read before returning the path, but it's a + * lot easier here since the length is part of the dirent structure. + * + * If not changing directories set a pointer so that can just append + * each new name into the path. + */ + len = NAPPEND(cur); + if (ISSET(FTS_NOCHDIR)) { + cp = sp->fts_path + len; + *cp++ = '/'; + } + len++; + maxlen = sp->fts_pathlen - len; + + /* + * fts_level is signed so we must prevent it from wrapping + * around to FTS_ROOTLEVEL and FTS_ROOTPARENTLEVEL. + */ + level = cur->fts_level; + if (level < FTS_MAXLEVEL) + level++; + + /* Read the directory, attaching each entry to the `link' pointer. */ + doadjust = 0; + for (head = tail = NULL, nitems = 0; dirp && (dp = readdir(dirp));) { + if (ISDOT(dp->d_name)) + continue; + +#if HAVE_DIRENT_NAMLEN + dlen = dp->d_namlen; +#else + dlen = strlen(dp->d_name); +#endif + + if (!(p = fts_alloc(sp, dp->d_name, dlen))) + goto mem1; + if (dlen >= maxlen) { /* include space for NUL */ + oldaddr = sp->fts_path; + if (fts_palloc(sp, dlen + len + 1)) { + /* + * No more memory for path or structures. Save + * errno, free up the current structure and the + * structures already allocated. + */ +mem1: saved_errno = errno; + if (p) + free(p); + fts_lfree(head); + (void)closedir(dirp); + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + errno = saved_errno; + return (NULL); + } + /* Did realloc() change the pointer? */ + if (oldaddr != sp->fts_path) { + doadjust = 1; + if (ISSET(FTS_NOCHDIR)) + cp = sp->fts_path + len; + } + maxlen = sp->fts_pathlen - len; + } + + p->fts_level = level; + p->fts_parent = sp->fts_cur; + p->fts_pathlen = len + dlen; + if (p->fts_pathlen < len) { + /* + * If we wrap, free up the current structure and + * the structures already allocated, then error + * out with ENAMETOOLONG. + */ + free(p); + fts_lfree(head); + (void)closedir(dirp); + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + errno = ENAMETOOLONG; + return (NULL); + } + + if (cderrno) { + if (nlinks) { + p->fts_info = FTS_NS; + p->fts_errno = cderrno; + } else + p->fts_info = FTS_NSOK; + p->fts_accpath = cur->fts_accpath; + } else if (nlinks == 0 +#ifdef DT_DIR + || (nostat && + dp->d_type != DT_DIR && dp->d_type != DT_UNKNOWN) +#endif + ) { + p->fts_accpath = + ISSET(FTS_NOCHDIR) ? p->fts_path : p->fts_name; + p->fts_info = FTS_NSOK; + } else { + /* Build a file name for fts_stat to stat. */ + if (ISSET(FTS_NOCHDIR)) { + p->fts_accpath = p->fts_path; + memmove(cp, p->fts_name, p->fts_namelen + 1); + } else + p->fts_accpath = p->fts_name; + /* Stat it. */ + p->fts_info = fts_stat(sp, p); + + /* Decrement link count if applicable. */ + if (nlinks > 0 && (p->fts_info == FTS_D || + p->fts_info == FTS_DC || p->fts_info == FTS_DOT)) + --nlinks; + } + + /* We walk in directory order so "ls -f" doesn't get upset. */ + p->fts_link = NULL; + if (head == NULL) + head = tail = p; + else { + tail->fts_link = p; + tail = p; + } + ++nitems; + } + if (dirp) + (void)closedir(dirp); + + /* + * If realloc() changed the address of the path, adjust the + * addresses for the rest of the tree and the dir list. + */ + if (doadjust) + fts_padjust(sp, head); + + /* + * If not changing directories, reset the path back to original + * state. + */ + if (ISSET(FTS_NOCHDIR)) { + if (len == sp->fts_pathlen || nitems == 0) + --cp; + *cp = '\0'; + } + + /* + * If descended after called from fts_children or after called from + * fts_read and nothing found, get back. At the root level we use + * the saved fd; if one of fts_open()'s arguments is a relative path + * to an empty directory, we wind up here with no other way back. If + * can't get back, we're done. + */ + if (descend && !nitems && + (cur->fts_level == FTS_ROOTLEVEL ? FCHDIR(sp, sp->fts_rfd) : + fts_safe_changedir(sp, cur->fts_parent, -1, ".."))) { + cur->fts_info = FTS_ERR; + SET(FTS_STOP); + return (NULL); + } + + /* If didn't find anything, return NULL. */ + if (!nitems) { + cur->fts_info = FTS_DP; + return (NULL); + } + return (head); +} + +static unsigned short +fts_stat(FTS *sp, FTSENT *p) +{ + FTSENT *t; + dev_t dev; + ino_t ino; + struct stat *sbp; + + /* If user needs stat info, stat buffer already allocated. */ + sbp = p->fts_statp; + + if (lstat(p->fts_accpath, sbp)) { + p->fts_errno = errno; + memset(sbp, 0, sizeof(struct stat)); + return (FTS_NS); + } + + if (S_ISDIR(sbp->st_mode)) { + /* + * Set the device/inode. Used to find cycles and check for + * crossing mount points. Also remember the link count, used + * in fts_build to limit the number of stat calls. It is + * understood that these fields are only referenced if fts_info + * is set to FTS_D. + */ + dev = p->fts_dev = sbp->st_dev; + ino = p->fts_ino = sbp->st_ino; + p->fts_nlink = sbp->st_nlink; + + if (ISDOT(p->fts_name)) + return (FTS_DOT); + + /* + * Cycle detection is done by brute force when the directory + * is first encountered. If the tree gets deep enough or the + * number of symbolic links to directories is high enough, + * something faster might be worthwhile. + */ + for (t = p->fts_parent; + t->fts_level >= FTS_ROOTLEVEL; t = t->fts_parent) + if (ino == t->fts_ino && dev == t->fts_dev) { + p->fts_cycle = t; + return (FTS_DC); + } + return (FTS_D); + } + if (S_ISLNK(sbp->st_mode)) + return (FTS_SL); + if (S_ISREG(sbp->st_mode)) + return (FTS_F); + return (FTS_DEFAULT); +} + +static FTSENT * +fts_alloc(FTS *sp, const char *name, size_t namelen) +{ + FTSENT *p; + size_t len; + + len = sizeof(FTSENT) + namelen; + if ((p = calloc(1, len)) == NULL) + return (NULL); + + p->fts_path = sp->fts_path; + p->fts_namelen = namelen; + p->fts_instr = FTS_NOINSTR; + p->fts_statp = malloc(sizeof(struct stat)); + if (p->fts_statp == NULL) { + free(p); + return (NULL); + } + memcpy(p->fts_name, name, namelen); + + return (p); +} + +static void +fts_lfree(FTSENT *head) +{ + FTSENT *p; + + /* Free a linked list of structures. */ + while ((p = head)) { + head = head->fts_link; + free(p); + } +} + +/* + * Allow essentially unlimited paths; find, rm, ls should all work on any tree. + * Most systems will allow creation of paths much longer than PATH_MAX, even + * though the kernel won't resolve them. Add the size (not just what's needed) + * plus 256 bytes so don't realloc the path 2 bytes at a time. + */ +static int +fts_palloc(FTS *sp, size_t more) +{ + char *p; + + /* + * Check for possible wraparound. + */ + more += 256; + if (sp->fts_pathlen + more < sp->fts_pathlen) { + if (sp->fts_path) + free(sp->fts_path); + sp->fts_path = NULL; + errno = ENAMETOOLONG; + return (1); + } + sp->fts_pathlen += more; + p = realloc(sp->fts_path, sp->fts_pathlen); + if (p == NULL) { + if (sp->fts_path) + free(sp->fts_path); + sp->fts_path = NULL; + return (1); + } + sp->fts_path = p; + return (0); +} + +/* + * When the path is realloc'd, have to fix all of the pointers in structures + * already returned. + */ +static void +fts_padjust(FTS *sp, FTSENT *head) +{ + FTSENT *p; + char *addr = sp->fts_path; + +#define ADJUST(p) { \ + if ((p)->fts_accpath != (p)->fts_name) { \ + (p)->fts_accpath = \ + (char *)addr + ((p)->fts_accpath - (p)->fts_path); \ + } \ + (p)->fts_path = addr; \ +} + /* Adjust the current set of children. */ + for (p = sp->fts_child; p; p = p->fts_link) + ADJUST(p); + + /* Adjust the rest of the tree, including the current level. */ + for (p = head; p->fts_level >= FTS_ROOTLEVEL;) { + ADJUST(p); + p = p->fts_link ? p->fts_link : p->fts_parent; + } +} + +static size_t +fts_maxarglen(char * const *argv) +{ + size_t len, max; + + for (max = 0; *argv; ++argv) + if ((len = strlen(*argv)) > max) + max = len; + return (max + 1); +} + +/* + * Change to dir specified by fd or p->fts_accpath without getting + * tricked by someone changing the world out from underneath us. + * Assumes p->fts_dev and p->fts_ino are filled in. + */ +static int +fts_safe_changedir(FTS *sp, FTSENT *p, int fd, const char *path) +{ + int ret, oerrno, newfd; + struct stat sb; + + newfd = fd; + if (ISSET(FTS_NOCHDIR)) + return (0); + if (fd < 0 && (newfd = open(path, O_RDONLY, 0)) < 0) + return (-1); + if (fstat(newfd, &sb)) { + ret = -1; + goto bail; + } + if (p->fts_dev != sb.st_dev || p->fts_ino != sb.st_ino) { + errno = ENOENT; /* disinformation */ + ret = -1; + goto bail; + } + ret = fchdir(newfd); +bail: + oerrno = errno; + if (fd < 0) + (void)close(newfd); + errno = oerrno; + return (ret); +} + +#endif Property changes on: vendor/mdocml/dist/compat_fts.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/mdocml/dist/compat_fts.h =================================================================== --- vendor/mdocml/dist/compat_fts.h (nonexistent) +++ vendor/mdocml/dist/compat_fts.h (revision 275397) @@ -0,0 +1,106 @@ +/* $OpenBSD: fts.h,v 1.14 2012/12/05 23:19:57 deraadt Exp $ */ +/* $NetBSD: fts.h,v 1.7 2012/03/01 16:18:51 hans Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)fts.h 8.3 (Berkeley) 8/14/94 + */ + +#ifndef _FTS_H_ +#define _FTS_H_ + +typedef struct { + struct _ftsent *fts_cur; /* current node */ + struct _ftsent *fts_child; /* linked list of children */ + dev_t fts_dev; /* starting device # */ + char *fts_path; /* path for this descent */ + int fts_rfd; /* fd for root */ + size_t fts_pathlen; /* sizeof(path) */ + +#define FTS_NOCHDIR 0x0004 /* don't change directories */ +#define FTS_PHYSICAL 0x0010 /* physical walk */ +#define FTS_XDEV 0x0040 /* don't cross devices */ +#define FTS_OPTIONMASK 0x00ff /* valid user option mask */ + +#define FTS_STOP 0x2000 /* (private) unrecoverable error */ + int fts_options; /* fts_open options, global flags */ +} FTS; + +typedef struct _ftsent { + struct _ftsent *fts_cycle; /* cycle node */ + struct _ftsent *fts_parent; /* parent directory */ + struct _ftsent *fts_link; /* next file in directory */ + char *fts_accpath; /* access path */ + char *fts_path; /* root path */ + int fts_errno; /* errno for this node */ + size_t fts_pathlen; /* strlen(fts_path) */ + size_t fts_namelen; /* strlen(fts_name) */ + + ino_t fts_ino; /* inode */ + dev_t fts_dev; /* device */ + nlink_t fts_nlink; /* link count */ + +#define FTS_ROOTPARENTLEVEL -1 +#define FTS_ROOTLEVEL 0 +#define FTS_MAXLEVEL 0x7fffffff + int fts_level; /* depth (-1 to N) */ + +#define FTS_D 1 /* preorder directory */ +#define FTS_DC 2 /* directory that causes cycles */ +#define FTS_DEFAULT 3 /* none of the above */ +#define FTS_DNR 4 /* unreadable directory */ +#define FTS_DOT 5 /* dot or dot-dot */ +#define FTS_DP 6 /* postorder directory */ +#define FTS_ERR 7 /* error; errno is set */ +#define FTS_F 8 /* regular file */ +#define FTS_INIT 9 /* initialized only */ +#define FTS_NS 10 /* stat(2) failed */ +#define FTS_NSOK 11 /* no stat(2) requested */ +#define FTS_SL 12 /* symbolic link */ + unsigned short fts_info; /* user flags for FTSENT structure */ + +#define FTS_DONTCHDIR 0x01 /* don't chdir .. to the parent */ + unsigned short fts_flags; /* private flags for FTSENT structure */ + +#define FTS_NOINSTR 3 /* no instructions */ +#define FTS_SKIP 4 /* discard node */ + unsigned short fts_instr; /* fts_set() instructions */ + + struct stat *fts_statp; /* stat(2) information */ + char fts_name[1]; /* file name */ +} FTSENT; + +__BEGIN_DECLS +int fts_close(FTS *); +FTS *fts_open(char * const *, int, void *); +FTSENT *fts_read(FTS *); +int fts_set(FTS *, FTSENT *, int); +__END_DECLS + +#endif /* !_FTS_H_ */ Property changes on: vendor/mdocml/dist/compat_fts.h ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/mdocml/dist/compat_getsubopt.c =================================================================== --- vendor/mdocml/dist/compat_getsubopt.c (revision 275396) +++ vendor/mdocml/dist/compat_getsubopt.c (revision 275397) @@ -1,104 +1,96 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_GETSUBOPT +#if HAVE_GETSUBOPT int dummy; #else +/* $Id: compat_getsubopt.c,v 1.5 2014/08/17 20:53:50 schwarze Exp $ */ /* $OpenBSD: getsubopt.c,v 1.4 2005/08/08 08:05:36 espie Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include -/* - * The SVID interface to getsubopt provides no way of figuring out which - * part of the suboptions list wasn't matched. This makes error messages - * tricky... The extern variable suboptarg is a pointer to the token - * which didn't match. - */ -char *suboptarg; - int getsubopt(char **optionp, char * const *tokens, char **valuep) { int cnt; + char *suboptarg; char *p; suboptarg = *valuep = NULL; if (!optionp || !*optionp) return(-1); /* skip leading white-space, commas */ for (p = *optionp; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p); if (!*p) { *optionp = p; return(-1); } /* save the start of the token, and skip the rest of the token. */ for (suboptarg = p; *++p && *p != ',' && *p != '=' && *p != ' ' && *p != '\t';); if (*p) { /* * If there's an equals sign, set the value pointer, and * skip over the value part of the token. Terminate the * token. */ if (*p == '=') { *p = '\0'; for (*valuep = ++p; *p && *p != ',' && *p != ' ' && *p != '\t'; ++p); if (*p) *p++ = '\0'; } else *p++ = '\0'; /* Skip any whitespace or commas after this token. */ for (; *p && (*p == ',' || *p == ' ' || *p == '\t'); ++p); } /* set optionp for next round. */ *optionp = p; for (cnt = 0; *tokens; ++tokens, ++cnt) if (!strcmp(suboptarg, *tokens)) return(cnt); return(-1); } #endif Index: vendor/mdocml/dist/compat_ohash.c =================================================================== --- vendor/mdocml/dist/compat_ohash.c (revision 275396) +++ vendor/mdocml/dist/compat_ohash.c (revision 275397) @@ -1,339 +1,339 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_OHASH +#if HAVE_OHASH int dummy; #else /* $OpenBSD: ohash.c,v 1.1 2014/06/02 18:52:03 deraadt Exp $ */ /* Copyright (c) 1999, 2004 Marc Espie * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ + +#include #include #include #include #include #include #include "compat_ohash.h" struct _ohash_record { uint32_t hv; const char *p; }; #define DELETED ((const char *)h) #define NONE (h->size) /* Don't bother changing the hash table if the change is small enough. */ #define MINSIZE (1UL << 4) #define MINDELETED 4 static void ohash_resize(struct ohash *); /* This handles the common case of variable length keys, where the * key is stored at the end of the record. */ void * ohash_create_entry(struct ohash_info *i, const char *start, const char **end) { char *p; if (!*end) *end = start + strlen(start); p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data); if (p) { memcpy(p+i->key_offset, start, *end-start); p[i->key_offset + (*end - start)] = '\0'; } return (void *)p; } /* hash_delete only frees the hash structure. Use hash_first/hash_next * to free entries as well. */ void ohash_delete(struct ohash *h) { (h->info.free)(h->t, h->info.data); #ifndef NDEBUG h->t = NULL; #endif } static void ohash_resize(struct ohash *h) { struct _ohash_record *n; size_t ns; unsigned int j; unsigned int i, incr; if (4 * h->deleted < h->total) { if (h->size >= (UINT_MAX >> 1U)) ns = UINT_MAX; else ns = h->size << 1U; } else if (3 * h->deleted > 2 * h->total) ns = h->size >> 1U; else ns = h->size; if (ns < MINSIZE) ns = MINSIZE; #ifdef STATS_HASH STAT_HASH_EXPAND++; STAT_HASH_SIZE += ns - h->size; #endif n = (h->info.calloc)(ns, sizeof(struct _ohash_record), h->info.data); if (!n) return; for (j = 0; j < h->size; j++) { if (h->t[j].p != NULL && h->t[j].p != DELETED) { i = h->t[j].hv % ns; incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1; while (n[i].p != NULL) { i += incr; if (i >= ns) i -= ns; } n[i].hv = h->t[j].hv; n[i].p = h->t[j].p; } } (h->info.free)(h->t, h->info.data); h->t = n; h->size = ns; h->total -= h->deleted; h->deleted = 0; } void * ohash_remove(struct ohash *h, unsigned int i) { void *result = (void *)h->t[i].p; if (result == NULL || result == DELETED) return NULL; #ifdef STATS_HASH STAT_HASH_ENTRIES--; #endif h->t[i].p = DELETED; h->deleted++; if (h->deleted >= MINDELETED && 4 * h->deleted > h->total) ohash_resize(h); return result; } void * ohash_find(struct ohash *h, unsigned int i) { if (h->t[i].p == DELETED) return NULL; else return (void *)h->t[i].p; } void * ohash_insert(struct ohash *h, unsigned int i, void *p) { #ifdef STATS_HASH STAT_HASH_ENTRIES++; #endif if (h->t[i].p == DELETED) { h->deleted--; h->t[i].p = p; } else { h->t[i].p = p; /* Arbitrary resize boundary. Tweak if not efficient enough. */ if (++h->total * 4 > h->size * 3) ohash_resize(h); } return p; } unsigned int ohash_entries(struct ohash *h) { return h->total - h->deleted; } void * ohash_first(struct ohash *h, unsigned int *pos) { *pos = 0; return ohash_next(h, pos); } void * ohash_next(struct ohash *h, unsigned int *pos) { for (; *pos < h->size; (*pos)++) if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL) return (void *)h->t[(*pos)++].p; return NULL; } void ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info) { h->size = 1UL << size; if (h->size < MINSIZE) h->size = MINSIZE; #ifdef STATS_HASH STAT_HASH_CREATION++; STAT_HASH_SIZE += h->size; #endif /* Copy info so that caller may free it. */ h->info.key_offset = info->key_offset; h->info.calloc = info->calloc; h->info.free = info->free; h->info.alloc = info->alloc; h->info.data = info->data; h->t = (h->info.calloc)(h->size, sizeof(struct _ohash_record), h->info.data); h->total = h->deleted = 0; } uint32_t ohash_interval(const char *s, const char **e) { uint32_t k; if (!*e) *e = s + strlen(s); if (s == *e) k = 0; else k = *s++; while (s != *e) k = ((k << 2) | (k >> 30)) ^ *s++; return k; } unsigned int ohash_lookup_interval(struct ohash *h, const char *start, const char *end, uint32_t hv) { unsigned int i, incr; unsigned int empty; #ifdef STATS_HASH STAT_HASH_LOOKUP++; #endif empty = NONE; i = hv % h->size; incr = ((hv % (h->size-2)) & ~1) + 1; while (h->t[i].p != NULL) { #ifdef STATS_HASH STAT_HASH_LENGTH++; #endif if (h->t[i].p == DELETED) { if (empty == NONE) empty = i; } else if (h->t[i].hv == hv && strncmp(h->t[i].p+h->info.key_offset, start, end - start) == 0 && (h->t[i].p+h->info.key_offset)[end-start] == '\0') { if (empty != NONE) { h->t[empty].hv = hv; h->t[empty].p = h->t[i].p; h->t[i].p = DELETED; return empty; } else { #ifdef STATS_HASH STAT_HASH_POSITIVE++; #endif return i; } } i += incr; if (i >= h->size) i -= h->size; } /* Found an empty position. */ if (empty != NONE) i = empty; h->t[i].hv = hv; return i; } unsigned int ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv) { unsigned int i, incr; unsigned int empty; #ifdef STATS_HASH STAT_HASH_LOOKUP++; #endif empty = NONE; i = hv % h->size; incr = ((hv % (h->size-2)) & ~1) + 1; while (h->t[i].p != NULL) { #ifdef STATS_HASH STAT_HASH_LENGTH++; #endif if (h->t[i].p == DELETED) { if (empty == NONE) empty = i; } else if (h->t[i].hv == hv && memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) { if (empty != NONE) { h->t[empty].hv = hv; h->t[empty].p = h->t[i].p; h->t[i].p = DELETED; return empty; } else { #ifdef STATS_HASH STAT_HASH_POSITIVE++; #endif } return i; } i += incr; if (i >= h->size) i -= h->size; } /* Found an empty position. */ if (empty != NONE) i = empty; h->t[i].hv = hv; return i; } unsigned int ohash_qlookup(struct ohash *h, const char *s) { const char *e = NULL; return ohash_qlookupi(h, s, &e); } unsigned int ohash_qlookupi(struct ohash *h, const char *s, const char **e) { uint32_t hv; hv = ohash_interval(s, e); return ohash_lookup_interval(h, s, *e, hv); } #endif /*!HAVE_OHASH*/ Index: vendor/mdocml/dist/compat_reallocarray.c =================================================================== --- vendor/mdocml/dist/compat_reallocarray.c (revision 275396) +++ vendor/mdocml/dist/compat_reallocarray.c (revision 275397) @@ -1,45 +1,43 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_REALLOCARRAY +#if HAVE_REALLOCARRAY int dummy; #else /* $OpenBSD: malloc.c,v 1.158 2014/04/23 15:07:27 tedu Exp $ */ /* * Copyright (c) 2008 Otto Moerbeek * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #define MUL_NO_OVERFLOW (1UL << (sizeof(size_t) * 4)) void * reallocarray(void *optr, size_t nmemb, size_t size) { if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && nmemb > 0 && SIZE_MAX / nmemb < size) { errno = ENOMEM; return NULL; } return realloc(optr, size * nmemb); } #endif /*!HAVE_REALLOCARRAY*/ Index: vendor/mdocml/dist/compat_sqlite3_errstr.c =================================================================== --- vendor/mdocml/dist/compat_sqlite3_errstr.c (revision 275396) +++ vendor/mdocml/dist/compat_sqlite3_errstr.c (revision 275397) @@ -1,18 +1,16 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_SQLITE3_ERRSTR +#if HAVE_SQLITE3_ERRSTR int dummy; #else const char * sqlite3_errstr(int rc) { return(rc ? "unknown error" : "not an error"); } #endif Index: vendor/mdocml/dist/compat_strcasestr.c =================================================================== --- vendor/mdocml/dist/compat_strcasestr.c (revision 275396) +++ vendor/mdocml/dist/compat_strcasestr.c (revision 275397) @@ -1,74 +1,72 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_STRCASESTR +#if HAVE_STRCASESTR int dummy; #else /* ($)NetBSD: strcasestr.c,v 1.2 2005/02/09 21:35:47 kleink Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Chris Torek. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include #include #include #define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) /* * Find the first occurrence of find in s, ignore case. */ char * strcasestr(const char *s, const char *find) { char c, sc; size_t len; if ((c = *find++) != 0) { c = tolower((unsigned char)c); len = strlen(find); do { do { if ((sc = *s++) == 0) return (NULL); } while ((char)tolower((unsigned char)sc) != c); } while (strncasecmp(s, find, len) != 0); s--; } return __UNCONST(s); } #endif Index: vendor/mdocml/dist/compat_strlcat.c =================================================================== --- vendor/mdocml/dist/compat_strlcat.c (revision 275396) +++ vendor/mdocml/dist/compat_strlcat.c (revision 275397) @@ -1,67 +1,65 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_STRLCAT +#if HAVE_STRLCAT int dummy; #else /* $OpenBSD: strlcat.c,v 1.13 2005/08/08 08:05:37 espie Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* * Appends src to string dst of size siz (unlike strncat, siz is the * full size of dst, not space left). At most siz-1 characters * will be copied. Always NUL terminates (unless siz <= strlen(dst)). * Returns strlen(src) + MIN(siz, strlen(initial dst)). * If retval >= siz, truncation occurred. */ size_t strlcat(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; size_t dlen; /* Find the end of dst and adjust bytes left but don't go past end */ while (n-- != 0 && *d != '\0') d++; dlen = d - dst; n = siz - dlen; if (n == 0) return(dlen + strlen(s)); while (*s != '\0') { if (n != 1) { *d++ = *s; n--; } s++; } *d = '\0'; return(dlen + (s - src)); /* count does not include NUL */ } #endif Index: vendor/mdocml/dist/compat_strlcpy.c =================================================================== --- vendor/mdocml/dist/compat_strlcpy.c (revision 275396) +++ vendor/mdocml/dist/compat_strlcpy.c (revision 275397) @@ -1,63 +1,61 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_STRLCPY +#if HAVE_STRLCPY int dummy; #else /* $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $ */ /* * Copyright (c) 1998 Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include /* * Copy src to string dst of size siz. At most siz-1 characters * will be copied. Always NUL terminates (unless siz == 0). * Returns strlen(src); if retval >= siz, truncation occurred. */ size_t strlcpy(char *dst, const char *src, size_t siz) { char *d = dst; const char *s = src; size_t n = siz; /* Copy as many bytes as will fit */ if (n != 0) { while (--n != 0) { if ((*d++ = *s++) == '\0') break; } } /* Not enough room in dst, add NUL and traverse rest of src */ if (n == 0) { if (siz != 0) *d = '\0'; /* NUL-terminate dst */ while (*s++) ; } return(s - src - 1); /* count does not include NUL */ } #endif Index: vendor/mdocml/dist/compat_strsep.c =================================================================== --- vendor/mdocml/dist/compat_strsep.c (revision 275396) +++ vendor/mdocml/dist/compat_strsep.c (revision 275397) @@ -1,80 +1,78 @@ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif -#ifdef HAVE_STRSEP +#if HAVE_STRSEP int dummy; #else /* ($)OpenBSD: strsep.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */ /*- * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Get next token from string *stringp, where tokens are possibly-empty * strings separated by characters from delim. * * Writes NULs into the string at *stringp to end tokens. * delim need not remain constant from call to call. * On return, *stringp points past the last NUL written (if there might * be further tokens), or is NULL (if there are definitely no more tokens). * * If *stringp is NULL, strsep returns NULL. */ char * strsep(char **stringp, const char *delim) { char *s; const char *spanp; int c, sc; char *tok; if ((s = *stringp) == NULL) return (NULL); for (tok = s;;) { c = *s++; spanp = delim; do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; *stringp = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ } #endif Index: vendor/mdocml/dist/configure =================================================================== --- vendor/mdocml/dist/configure (revision 275396) +++ vendor/mdocml/dist/configure (revision 275397) @@ -1,49 +1,393 @@ #!/bin/sh # # Copyright (c) 2014 Ingo Schwarze # # Permission to use, copy, modify, and distribute this software for any # purpose with or without fee is hereby granted, provided that the above # copyright notice and this permission notice appear in all copies. # # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -echo "/* RUNNING ./CONFIGURE - SHOULD BE USED ONLY VIA MAKE, READ INSTALL */" - set -e -exec > config.h 2> config.log -CFLAGS="${CFLAGS} -Wno-unused -Werror" +[ -e config.log ] && mv config.log config.log.old +[ -e config.h ] && mv config.h config.h.old +# Output file descriptor usage: +# 1 (stdout): config.h, Makefile.local +# 2 (stderr): original stderr, usually to the console +# 3: config.log + +exec 3> config.log +echo "config.log: writing..." + +# --- default settings ------------------------------------------------- +# Initialize all variables here, +# such that nothing can leak in from the environment. + +VERSION="1.13.1" +echo "VERSION=\"${VERSION}\"" 1>&2 +echo "VERSION=\"${VERSION}\"" 1>&3 + +OSNAME= + +CC=`printf "all:\\n\\t@echo \\\$(CC)\\n" | make -f -` +CFLAGS="-g -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings" +DBLIB= +STATIC="-static" + +BUILD_DB=1 +BUILD_CGI=0 + +HAVE_DIRENT_NAMLEN= +HAVE_FGETLN= +HAVE_FTS= +HAVE_GETSUBOPT= +HAVE_MMAP= +HAVE_REALLOCARRAY= +HAVE_STRCASESTR= +HAVE_STRLCAT= +HAVE_STRLCPY= +HAVE_STRPTIME= +HAVE_STRSEP= +HAVE_WCHAR= + +HAVE_SQLITE3= +HAVE_SQLITE3_ERRSTR= +HAVE_OHASH= +HAVE_MANPATH= + +PREFIX="/usr/local" +BINDIR= +SBINDIR= +INCLUDEDIR= +LIBDIR= +MANDIR= +EXAMPLEDIR= + +WWWPREFIX="/var/www" +HTDOCDIR= +CGIBINDIR= + +INSTALL="install" +INSTALL_PROGRAM= +INSTALL_LIB= +INSTALL_MAN= +INSTALL_DATA= + +# --- manual settings from configure.local ----------------------------- + +if [ -e ./configure.local ]; then + echo "configure.local: reading..." 1>&2 + echo "configure.local: reading..." 1>&3 + cat ./configure.local 1>&3 + . ./configure.local +else + echo "configure.local: no (fully automatic configuration)" 1>&2 + echo "configure.local: no (fully automatic configuration)" 1>&3 +fi +echo 1>&3 + +# --- tests for config.h ---------------------------------------------- + +COMP="${CC} ${CFLAGS} -Wno-unused -Werror" + +# Check whether this HAVE_ setting is manually overridden. +# If yes, use the override, if no, do not decide anything yet. +# Arguments: lower-case test name, manual value +ismanual() { + [ -z "${2}" ] && return 1 + echo "${1}: manual (${2})" 1>&2 + echo "${1}: manual (${2})" 1>&3 + echo 1>&3 + return 0 +} + +# Run a single autoconfiguration test. +# In case of success, enable the feature. +# In case of failure, do not decide anything yet. +# Arguments: lower-case test name, upper-case test name, additional CFLAGS +singletest() { + cat 1>&3 << __HEREDOC__ +${1}: testing... +${COMP} ${3} -o test-${1} test-${1}.c +__HEREDOC__ + + if ${COMP} ${3} -o "test-${1}" "test-${1}.c" 1>&3 2>&3; then + echo "${1}: ${CC} succeeded" 1>&3 + else + echo "${1}: ${CC} failed with $?" 1>&3 + echo 1>&3 + return 1 + fi + + if ./test-${1} 1>&3 2>&3; then + echo "${1}: yes" 1>&2 + echo "${1}: yes" 1>&3 + echo 1>&3 + eval HAVE_${2}=1 + rm "test-${1}" + return 0 + else + echo "${1}: execution failed with $?" 1>&3 + echo 1>&3 + rm "test-${1}" + return 1 + fi +} + +# Run a complete autoconfiguration test, including the check for +# a manual override and disabling the feature on failure. +# Arguments: lower case name, upper case name, additional CFLAGS runtest() { - echo ${CC} ${CFLAGS} ${3} -o test-${1} test-${1}.c 1>&2 - ${CC} ${CFLAGS} ${3} -o "test-${1}" "test-${1}.c" 1>&2 || return 0 - "./test-${1}" && echo "#define HAVE_${2}" \ - || echo FAILURE: test-${1} returned $? 1>&2 - rm "test-${1}" + eval _manual=\${HAVE_${2}} + ismanual "${1}" "${_manual}" && return 0 + singletest "${1}" "${2}" "${3}" && return 0 + echo "${1}: no" 1>&2 + eval HAVE_${2}=0 + return 1 } -cat config.h.pre +# --- library functions --- +runtest dirent-namlen DIRENT_NAMLEN || true +runtest fgetln FGETLN || true +runtest fts FTS || true +runtest getsubopt GETSUBOPT || true +runtest mmap MMAP || true +runtest reallocarray REALLOCARRAY || true +runtest strcasestr STRCASESTR || true +runtest strlcat STRLCAT || true +runtest strlcpy STRLCPY || true +runtest strptime STRPTIME || true +runtest strsep STRSEP || true +runtest wchar WCHAR || true + +# --- sqlite3 --- +DETECTLIB= +if [ ${BUILD_DB} -eq 0 ]; then + echo "BUILD_DB=0 (manual)" 1>&2 + echo "BUILD_DB=0 (manual)" 1>&3 + echo 1>&3 + HAVE_SQLITE3=0 +elif ismanual sqlite3 "${HAVE_SQLITE3}"; then + DETECTLIB="-lsqlite3" +elif [ -n "${DBLIB}" ]; then + runtest sqlite3 SQLITE3 "${DBLIB}" || true +elif singletest sqlite3 SQLITE3 "-lsqlite3"; then + DETECTLIB="-lsqlite3" +elif runtest sqlite3 SQLITE3 \ + "-I/usr/local/include -L/usr/local/lib -lsqlite3"; then + DETECTLIB="-L/usr/local/lib -lsqlite3" + CFLAGS="${CFLAGS} -I/usr/local/include" +fi +if [ ${BUILD_DB} -gt 0 -a ${HAVE_SQLITE3} -eq 0 ]; then + echo "BUILD_DB=0 (no sqlite3)" 1>&2 + echo "BUILD_DB=0 (no sqlite3)" 1>&3 + echo 1>&3 + BUILD_DB=0 +fi + +# --- sqlite3_errstr --- +if [ ${BUILD_DB} -eq 0 ]; then + HAVE_SQLITE3_ERRSTR=1 +elif ismanual sqlite3_errstr "${HAVE_SQLITE3_ERRSTR}"; then + : +elif [ -n "${DBLIB}" ]; then + runtest sqlite3_errstr SQLITE3_ERRSTR "${DBLIB}" || true +else + runtest sqlite3_errstr SQLITE3_ERRSTR "${DETECTLIB}" || true +fi + +# --- ohash --- +if [ ${BUILD_DB} -eq 0 ]; then + HAVE_OHASH=1 +elif ismanual ohash "${HAVE_OHASH}"; then + : +elif [ -n "${DBLIB}" ]; then + runtest ohash OHASH "${DBLIB}" || true +elif singletest ohash OHASH; then + : +elif runtest ohash OHASH "-lutil"; then + DETECTLIB="${DETECTLIB} -lutil" +fi + +# --- DBLIB --- +if [ ${BUILD_DB} -eq 0 ]; then + DBLIB= +elif [ -z "${DBLIB}" ]; then + DBLIB="${DETECTLIB}" + echo "DBLIB=\"${DBLIB}\"" 1>&2 + echo "DBLIB=\"${DBLIB}\"" 1>&3 + echo 1>&3 +fi + +# --- manpath --- +if [ ${BUILD_DB} -eq 0 ]; then + HAVE_MANPATH=0 +elif ismanual manpath "${HAVE_MANPATH}"; then + : +elif manpath 1>&3 2>&3; then + echo "manpath: yes" 1>&2 + echo "manpath: yes" 1>&3 + echo 1>&3 + HAVE_MANPATH=1 +else + echo "manpath: no" 1>&2 + echo "manpath: no" 1>&3 + echo 1>&3 + HAVE_MANPATH=0 +fi + +# --- write config.h --- + +exec > config.h + +cat << __HEREDOC__ +#ifndef MANDOC_CONFIG_H +#define MANDOC_CONFIG_H + +#if defined(__linux__) || defined(__MINT__) +#define _GNU_SOURCE /* See test-*.c what needs this. */ +#endif + +__HEREDOC__ + +[ ${HAVE_FGETLN} -eq 0 -o ${HAVE_REALLOCARRAY} -eq 0 -o \ + ${HAVE_STRLCAT} -eq 0 -o ${HAVE_STRLCPY} -eq 0 ] \ + && echo "#include " +[ ${HAVE_FGETLN} -eq 0 ] && echo "#include " + echo echo "#define VERSION \"${VERSION}\"" -runtest fgetln FGETLN -runtest getsubopt GETSUBOPT -runtest mmap MMAP -runtest ohash OHASH "${DBLIB}" -runtest reallocarray REALLOCARRAY -runtest sqlite3_errstr SQLITE3_ERRSTR "${DBLIB}" -runtest strcasestr STRCASESTR -runtest strlcat STRLCAT -runtest strlcpy STRLCPY -runtest strptime STRPTIME -runtest strsep STRSEP +[ -n "${OSNAME}" ] && echo "#define OSNAME \"${OSNAME}\"" + +cat << __HEREDOC__ +#define HAVE_DIRENT_NAMLEN ${HAVE_DIRENT_NAMLEN} +#define HAVE_FGETLN ${HAVE_FGETLN} +#define HAVE_FTS ${HAVE_FTS} +#define HAVE_GETSUBOPT ${HAVE_GETSUBOPT} +#define HAVE_MMAP ${HAVE_MMAP} +#define HAVE_REALLOCARRAY ${HAVE_REALLOCARRAY} +#define HAVE_STRCASESTR ${HAVE_STRCASESTR} +#define HAVE_STRLCAT ${HAVE_STRLCAT} +#define HAVE_STRLCPY ${HAVE_STRLCPY} +#define HAVE_STRPTIME ${HAVE_STRPTIME} +#define HAVE_STRSEP ${HAVE_STRSEP} +#define HAVE_WCHAR ${HAVE_WCHAR} +#define HAVE_SQLITE3 ${HAVE_SQLITE3} +#define HAVE_SQLITE3_ERRSTR ${HAVE_SQLITE3_ERRSTR} +#define HAVE_OHASH ${HAVE_OHASH} +#define HAVE_MANPATH ${HAVE_MANPATH} + +#if !defined(__BEGIN_DECLS) +# ifdef __cplusplus +# define __BEGIN_DECLS extern "C" { +# else +# define __BEGIN_DECLS +# endif +#endif +#if !defined(__END_DECLS) +# ifdef __cplusplus +# define __END_DECLS } +# else +# define __END_DECLS +# endif +#endif + +__HEREDOC__ + +[ ${HAVE_FGETLN} -eq 0 ] && \ + echo "extern char *fgetln(FILE *, size_t *);" + +[ ${HAVE_GETSUBOPT} -eq 0 ] && \ + echo "extern int getsubopt(char **, char * const *, char **);" + +[ ${HAVE_REALLOCARRAY} -eq 0 ] && \ + echo "extern void *reallocarray(void *, size_t, size_t);" + +[ ${BUILD_DB} -gt 0 -a ${HAVE_SQLITE3_ERRSTR} -eq 0 ] && + echo "extern const char *sqlite3_errstr(int);" + +[ ${HAVE_STRCASESTR} -eq 0 ] && \ + echo "extern char *strcasestr(const char *, const char *);" + +[ ${HAVE_STRLCAT} -eq 0 ] && \ + echo "extern size_t strlcat(char *, const char *, size_t);" + +[ ${HAVE_STRLCPY} -eq 0 ] && \ + echo "extern size_t strlcpy(char *, const char *, size_t);" + +[ ${HAVE_STRSEP} -eq 0 ] && \ + echo "extern char *strsep(char **, const char *);" + echo -cat config.h.post +echo "#endif /* MANDOC_CONFIG_H */" + +echo "config.h: written" 1>&2 +echo "config.h: written" 1>&3 + +# --- tests for Makefile.local ----------------------------------------- + +exec > Makefile.local + +[ -z "${BINDIR}" ] && BINDIR="${PREFIX}/bin" +[ -z "${SBINDIR}" ] && SBINDIR="${PREFIX}/sbin" +[ -z "${INCLUDEDIR}" ] && INCLUDEDIR="${PREFIX}/include/mandoc" +[ -z "${LIBDIR}" ] && LIBDIR="${PREFIX}/lib/mandoc" +[ -z "${MANDIR}" ] && MANDIR="${PREFIX}/man" +[ -z "${EXAMPLEDIR}" ] && EXAMPLEDIR="${PREFIX}/share/examples/mandoc" + +[ -z "${HTDOCDIR}" ] && HTDOCDIR="${WWWPREFIX}/htdocs" +[ -z "${CGIBINDIR}" ] && CGIBINDIR="${WWWPREFIX}/cgi-bin" + +[ -z "${INSTALL_PROGRAM}" ] && INSTALL_PROGRAM="${INSTALL} -m 0555" +[ -z "${INSTALL_LIB}" ] && INSTALL_LIB="${INSTALL} -m 0444" +[ -z "${INSTALL_MAN}" ] && INSTALL_MAN="${INSTALL} -m 0444" +[ -z "${INSTALL_DATA}" ] && INSTALL_DATA="${INSTALL} -m 0444" + +if [ ${BUILD_DB} -eq 0 -a ${BUILD_CGI} -gt 0 ]; then + echo "BUILD_CGI=0 (no BUILD_DB)" 1>&2 + echo "BUILD_CGI=0 (no BUILD_DB)" 1>&3 + BUILD_CGI=0 +fi + +BUILD_TARGETS="base-build" +[ ${BUILD_DB} -gt 0 ] && BUILD_TARGETS="${BUILD_TARGETS} db-build" +[ ${BUILD_CGI} -gt 0 ] && BUILD_TARGETS="${BUILD_TARGETS} cgi-build" + +cat << __HEREDOC__ +VERSION = ${VERSION} +BUILD_TARGETS = ${BUILD_TARGETS} +CFLAGS = ${CFLAGS} +DBLIB = ${DBLIB} +STATIC = ${STATIC} +PREFIX = ${PREFIX} +BINDIR = ${BINDIR} +SBINDIR = ${SBINDIR} +INCLUDEDIR = ${INCLUDEDIR} +LIBDIR = ${LIBDIR} +MANDIR = ${MANDIR} +EXAMPLEDIR = ${EXAMPLEDIR} +WWWPREFIX = ${WWWPREFIX} +HTDOCDIR = ${HTDOCDIR} +CGIBINDIR = ${CGIBINDIR} +INSTALL = ${INSTALL} +INSTALL_PROGRAM = ${INSTALL_PROGRAM} +INSTALL_LIB = ${INSTALL_LIB} +INSTALL_MAN = ${INSTALL_MAN} +INSTALL_DATA = ${INSTALL_DATA} +__HEREDOC__ + +[ ${BUILD_DB} -gt 0 ] && \ + echo "MAN_OBJS = \$(MANDOC_OBJS) \$(APROPOS_OBJS)" + +echo "Makefile.local: written" 1>&2 +echo "Makefile.local: written" 1>&3 exit 0 Index: vendor/mdocml/dist/configure.local.example =================================================================== --- vendor/mdocml/dist/configure.local.example (nonexistent) +++ vendor/mdocml/dist/configure.local.example (revision 275397) @@ -0,0 +1,189 @@ +# $Id: configure.local.example,v 1.1 2014/08/16 19:00:01 schwarze Exp $ +# +# Copyright (c) 2014 Ingo Schwarze +# +# Permission to use, copy, modify, and distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice appear in all copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +# For all settings documented in this file, there are reasonable +# defaults and/or the ./configure script attempts autodetection. +# Consequently, you only need to create a file ./configure.local +# and put any of these settings into it if ./configure autodetection +# fails or if you want to make different choices for other reasons. + +# If autodetection fails, please tell . + +# We recommend that you write ./configure.local from scratch and +# only put the lines there you need. This file contains examples. +# It is not intended as a template to be copied as a whole. + +# --- user settings relevant for all builds ---------------------------- + +# For -Tutf8 and -Tlocale operation, mandoc(1) requires +# providing setlocale(3) and providing wcwidth(3) and +# putwchar(3) with a wchar_t storing UCS-4 values. Theoretically, +# the latter should be tested with the __STDC_ISO_10646__ feature +# macro. In practice, many headers do not provide that +# macro even though they treat wchar_t as UCS-4. So the automatic +# test only checks that wchar_t is wide enough, that is, at least +# four bytes. + +# The following line forces multi-byte support. +# If your C library does not treat wchar_t as UCS-4, the UTF-8 output +# mode will print garbage. + +HAVE_WCHAR=1 + +# The following line disables multi-byte support. +# The output modes -Tutf8 and -Tlocale will be the same as -Tascii. + +HAVE_WCHAR=0 + +# In manual pages written in the mdoc(7) language, the operating system +# version is displayed in the page footer line. If an operating system +# is specified as an argument to the .Os macro, that is always used. +# If the .Os macro has no argument and an operation system is specified +# with the mandoc(1) -Ios= command line option, that is used. +# Otherwise, the uname(3) library function is called at runtime to find +# the name of the operating system. +# If you do not want uname(3) to be called but instead want a fixed +# string to be used, use the following line: + +OSNAME="OpenBSD 5.5" + +# The following installation directories are used. +# It is possible to set only one or a few of these variables, +# there is no need to copy the whole block. +# Even if you set PREFIX to something else, the other variables +# pick it up without copying them all over. + +PREFIX="/usr/local" +BINDIR="${PREFIX}/bin" +SBINDIR="${PREFIX}/sbin" +INCLUDEDIR="${PREFIX}/include/mandoc" +LIBDIR="${PREFIX}/lib/mandoc" +MANDIR="${PREFIX}/man" +EXAMPLEDIR="${PREFIX}/share/examples/mandoc" + +# It is possible to change the utility program used for installation +# and the modes files are installed with. The defaults are: + +INSTALL="install" +INSTALL_PROGRAM="${INSTALL} -m 0555" +INSTALL_LIB="${INSTALL} -m 0444" +INSTALL_MAN="${INSTALL} -m 0444" +INSTALL_DATA="${INSTALL} -m 0444" + +# --- user settings related to database support ------------------------ + +# By default, building makewhatis(8) and apropos(1) is enabled. +# To disable it, for example to avoid the dependency on SQLite3, +# use the following line. It that case, the remaining settings +# in this section are irrelevant. + +BUILD_DB=0 + +# Two libraries are needed: SQLite3 and ohash(3). +# Autoconfiguration tries the following linker flags to find them. +# If none of these work, add a working DBLIB line to configure.local, +# disabling autodetection for library directories. + +DBLIB="-lsqlite3" +DBLIB="-lsqlite3 -lutil" +DBLIB="-L/usr/local/lib -lsqlite3" + +# When library autodetection decides to use -L/usr/local/lib, +# -I/usr/local/include is automatically added to CFLAGS. +# If you manually set DBLIB to something including -L/usr/local/lib, +# chances are you will also need the following line: + +CFLAGS="${CFLAGS} -I/usr/local/include" + +# The man(1) utility needs to know where the manuals reside. +# We know of two ways to tell it: via manpath(1) or man.conf(5). +# The latter is used by OpenBSD and NetBSD, the former by most +# other systems. + +# Force usage of manpath(1). +# If it is not installed or not operational, +# makewhatis(8) and apropos(1) will not work properly. + +HAVE_MANPATH=1 + +# Force usage of man.conf(5). +# If it does not exist or contains no valid configuration, +# makewhatis(8) and apropos(1) will not work properly. + +HAVE_MANPATH=0 + +# --- user settings related man.cgi ------------------------------------ + +# By default, building man.cgi(8) is disabled. To enable it, copy +# cgi.h.example to cgi.h, edit it, and use the following line. +# Obviously, this requires that BUILD_DB is enabled, too. + +BUILD_CGI=1 + +# The remaining settings in this section are only relevant if BUILD_CGI +# is enabled. Otherwise, they have no effect either way. + +# By default, man.cgi(8) is linked statically. +# Some systems do not support static linking, for example Mac OS X. +# In that case, use the following line: + +STATIC= + +# Some systems, for example Linux, require -pthread for static linking: + +STATIC="-static -pthread" + +# Some directories. +# This works just like PREFIX, see above. + +WWWPREFIX="/var/www" +HTDOCDIR="${WWWPREFIX}/htdocs" +CGIBINDIR="${WWWPREFIX}/cgi-bin" + +# --- settings that rarely need to be touched -------------------------- + +# Do not set these variables unless you really need to. + +# You can manually override the compiler to be used. +# But that's rarely useful because ./configure asks your make(1) +# which compiler to use, and that answer will hardly be wrong. + +CC=cc + +# The default compiler flags are: + +CFLAGS="-g -W -Wall -Wstrict-prototypes -Wno-unused-parameter -Wwrite-strings" + +# In rare cases, it may be required to skip individual automatic tests. +# Each of the following variables can be set to 0 (test will not be run +# and will be regarded as failed) or 1 (test will not be run and will +# be regarded as successful). + +HAVE_DIRENT_NAMLEN=0 +HAVE_FGETLN=0 +HAVE_FTS=0 +HAVE_GETSUBOPT=0 +HAVE_MMAP=0 +HAVE_REALLOCARRAY=0 +HAVE_STRCASESTR=0 +HAVE_STRLCAT=0 +HAVE_STRLCPY=0 +HAVE_STRPTIME=0 +HAVE_STRSEP=0 + +HAVE_SQLITE3=0 +HAVE_SQLITE3_ERRSTR=0 +HAVE_OHASH=0 Index: vendor/mdocml/dist/demandoc.1 =================================================================== --- vendor/mdocml/dist/demandoc.1 (revision 275396) +++ vendor/mdocml/dist/demandoc.1 (revision 275397) @@ -1,108 +1,108 @@ -.\" $Id: demandoc.1,v 1.7 2013/07/13 19:41:16 schwarze Exp $ +.\" $Id: demandoc.1,v 1.8 2014/09/12 00:10:26 schwarze Exp $ .\" .\" Copyright (c) 2011 Kristaps Dzonsons .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 13 2013 $ +.Dd $Mdocdate: September 12 2014 $ .Dt DEMANDOC 1 .Os .Sh NAME .Nm demandoc .Nd emit only text of UNIX manuals .Sh SYNOPSIS .Nm demandoc .Op Fl w .Op Ar .Sh DESCRIPTION The .Nm utility emits only the text portions of well-formed .Xr mdoc 7 and .Xr man 7 .Ux manual files. .Pp By default, .Nm parses standard input and outputs only text nodes, preserving line and column position. Escape sequences are omitted from the output. .Pp Its arguments are as follows: .Bl -tag -width Ds .It Fl w Output a word list. This outputs each word of text on its own line. A .Qq word , in this case, refers to whitespace-delimited terms beginning with at least two letters and not consisting of any escape sequences. Words have their leading and trailing punctuation .Pq double-quotes, sentence punctuation, etc. stripped. .It Ar The input files. .El .Pp If a document is not well-formed, it is skipped. .Pp The .Fl i , .Fl k , .Fl m , and .Fl p flags are silently discarded for calling compatibility with the historical deroff. .Sh EXIT STATUS The .Nm utility exits with one of the following values: .Pp .Bl -tag -width Ds -compact .It 0 No errors occurred. .It 6 An operating system error occurred, for example memory exhaustion or an error accessing input files. Such errors cause .Nm to exit at once, possibly in the middle of parsing or formatting a file. The output databases are corrupt and should be removed . .El .Sh EXAMPLES The traditional usage of .Nm is for spell-checking manuals on .Bx . This is accomplished as follows (assuming British spelling): .Pp .Dl $ demandoc -w file.1 | spell -b .Sh SEE ALSO .Xr mandoc 1 , -.Xr man 7 +.Xr man 7 , .Xr mdoc 7 .Sh HISTORY .Nm replaces the historical deroff utility for handling modern .Xr man 7 and .Xr mdoc 7 documents. .Sh AUTHORS The .Nm utility was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . Index: vendor/mdocml/dist/demandoc.c =================================================================== --- vendor/mdocml/dist/demandoc.c (revision 275396) +++ vendor/mdocml/dist/demandoc.c (revision 275397) @@ -1,257 +1,260 @@ -/* $Id: demandoc.c,v 1.10 2014/03/19 22:20:43 schwarze Exp $ */ +/* $Id: demandoc.c,v 1.12 2014/10/28 17:36:19 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include + #include #include #include #include #include #include #include #include "man.h" #include "mdoc.h" #include "mandoc.h" static void pline(int, int *, int *, int); static void pman(const struct man_node *, int *, int *, int); static void pmandoc(struct mparse *, int, const char *, int); static void pmdoc(const struct mdoc_node *, int *, int *, int); static void pstring(const char *, int, int *, int); static void usage(void); static const char *progname; int main(int argc, char *argv[]) { struct mparse *mp; + struct mchars *mchars; int ch, i, list; extern int optind; progname = strrchr(argv[0], '/'); if (progname == NULL) progname = argv[0]; else ++progname; mp = NULL; list = 0; while (-1 != (ch = getopt(argc, argv, "ikm:pw"))) switch (ch) { case ('i'): /* FALLTHROUGH */ case ('k'): /* FALLTHROUGH */ case ('m'): /* FALLTHROUGH */ case ('p'): break; case ('w'): list = 1; break; default: usage(); return((int)MANDOCLEVEL_BADARG); } argc -= optind; argv += optind; - mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL, NULL); + mchars = mchars_alloc(); + mp = mparse_alloc(MPARSE_SO, MANDOCLEVEL_FATAL, NULL, mchars, NULL); assert(mp); if (0 == argc) pmandoc(mp, STDIN_FILENO, "", list); for (i = 0; i < argc; i++) { mparse_reset(mp); pmandoc(mp, -1, argv[i], list); } mparse_free(mp); + mchars_free(mchars); return((int)MANDOCLEVEL_OK); } static void usage(void) { fprintf(stderr, "usage: %s [-w] [files...]\n", progname); } static void pmandoc(struct mparse *mp, int fd, const char *fn, int list) { struct mdoc *mdoc; struct man *man; int line, col; if (mparse_readfd(mp, fd, fn) >= MANDOCLEVEL_FATAL) { fprintf(stderr, "%s: Parse failure\n", fn); return; } mparse_result(mp, &mdoc, &man, NULL); line = 1; col = 0; if (mdoc) pmdoc(mdoc_node(mdoc), &line, &col, list); else if (man) pman(man_node(man), &line, &col, list); else return; if ( ! list) putchar('\n'); } /* * Strip the escapes out of a string, emitting the results. */ static void pstring(const char *p, int col, int *colp, int list) { enum mandoc_esc esc; const char *start, *end; int emit; /* * Print as many column spaces til we achieve parity with the * input document. */ again: if (list && '\0' != *p) { while (isspace((unsigned char)*p)) p++; while ('\'' == *p || '(' == *p || '"' == *p) p++; emit = isalpha((unsigned char)p[0]) && isalpha((unsigned char)p[1]); for (start = p; '\0' != *p; p++) if ('\\' == *p) { p++; esc = mandoc_escape(&p, NULL, NULL); if (ESCAPE_ERROR == esc) return; emit = 0; } else if (isspace((unsigned char)*p)) break; end = p - 1; while (end > start) if ('.' == *end || ',' == *end || '\'' == *end || '"' == *end || ')' == *end || '!' == *end || '?' == *end || ':' == *end || ';' == *end) end--; else break; if (emit && end - start >= 1) { for ( ; start <= end; start++) if (ASCII_HYPH == *start) putchar('-'); else putchar((unsigned char)*start); putchar('\n'); } if (isspace((unsigned char)*p)) goto again; return; } while (*colp < col) { putchar(' '); (*colp)++; } /* * Print the input word, skipping any special characters. */ while ('\0' != *p) if ('\\' == *p) { p++; esc = mandoc_escape(&p, NULL, NULL); if (ESCAPE_ERROR == esc) break; } else { putchar((unsigned char )*p++); (*colp)++; } } static void pline(int line, int *linep, int *col, int list) { if (list) return; /* * Print out as many lines as needed to reach parity with the * original input. */ while (*linep < line) { putchar('\n'); (*linep)++; } *col = 0; } static void pmdoc(const struct mdoc_node *p, int *line, int *col, int list) { for ( ; p; p = p->next) { if (MDOC_LINE & p->flags) pline(p->line, line, col, list); if (MDOC_TEXT == p->type) pstring(p->string, p->pos, col, list); if (p->child) pmdoc(p->child, line, col, list); } } static void pman(const struct man_node *p, int *line, int *col, int list) { for ( ; p; p = p->next) { if (MAN_LINE & p->flags) pline(p->line, line, col, list); if (MAN_TEXT == p->type) pstring(p->string, p->pos, col, list); if (p->child) pman(p->child, line, col, list); } } Index: vendor/mdocml/dist/eqn.7 =================================================================== --- vendor/mdocml/dist/eqn.7 (revision 275396) +++ vendor/mdocml/dist/eqn.7 (revision 275397) @@ -1,279 +1,505 @@ -.\" $Id: eqn.7,v 1.29 2013/07/13 19:41:16 schwarze Exp $ +.\" $Id: eqn.7,v 1.31 2014/10/12 11:57:38 schwarze Exp $ .\" .\" Copyright (c) 2011 Kristaps Dzonsons +.\" Copyright (c) 2014 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: July 13 2013 $ +.Dd $Mdocdate: October 12 2014 $ .Dt EQN 7 .Os .Sh NAME .Nm eqn .Nd eqn language reference for mandoc .Sh DESCRIPTION The .Nm eqn language is an equation-formatting language. It is used within .Xr mdoc 7 and .Xr man 7 .Ux manual pages. It describes the .Em structure of an equation, not its mathematical meaning. This manual describes the .Nm language accepted by the .Xr mandoc 1 -utility, which corresponds to the Second Edition eqn specification (see +utility, which corresponds to the Second Edition +.Nm +specification (see .Sx SEE ALSO for references). .Pp Equations within .Xr mdoc 7 or .Xr man 7 documents are enclosed by the standalone .Sq \&.EQ and .Sq \&.EN tags. Equations are multi-line blocks consisting of formulas and control statements. .Sh EQUATION STRUCTURE Each equation is bracketed by .Sq \&.EQ and .Sq \&.EN strings. .Em Note : these are not the same as .Xr roff 7 macros, and may only be invoked as .Sq \&.EQ . .Pp The equation grammar is as follows, where quoted strings are case-sensitive literals in the input: .Bd -literal -offset indent eqn : box | eqn box box : text | \*q{\*q eqn \*q}\*q | \*qdefine\*q text text | \*qndefine\*q text text | \*qtdefine\*q text text | \*qgfont\*q text | \*qgsize\*q text | \*qset\*q text text | \*qundef\*q text + | \*qsqrt\*q box | box pos box | box mark | \*qmatrix\*q \*q{\*q [col \*q{\*q list \*q}\*q ]* | pile \*q{\*q list \*q}\*q | font box | \*qsize\*q text box | \*qleft\*q text eqn [\*qright\*q text] col : \*qlcol\*q | \*qrcol\*q | \*qccol\*q | \*qcol\*q text : [^space\e\*q]+ | \e\*q.*\e\*q pile : \*qlpile\*q | \*qcpile\*q | \*qrpile\*q | \*qpile\*q pos : \*qover\*q | \*qsup\*q | \*qsub\*q | \*qto\*q | \*qfrom\*q mark : \*qdot\*q | \*qdotdot\*q | \*qhat\*q | \*qtilde\*q | \*qvec\*q | \*qdyad\*q | \*qbar\*q | \*qunder\*q font : \*qroman\*q | \*qitalic\*q | \*qbold\*q | \*qfat\*q list : eqn | list \*qabove\*q eqn space : [\e^~ \et] .Ed .Pp White-space consists of the space, tab, circumflex, and tilde characters. +It is required to delimit tokens consisting of alphabetic characters +and it is ignored at other places. +Braces and quotes also delimit tokens. If within a quoted string, these space characters are retained. -Quoted strings are also not scanned for replacement definitions. +Quoted strings are also not scanned for keywords, glyph names, +and expansion of definitions. +To print a literal quote character, it can be prepended with a +backslash or expressed with the \e(dq escape sequence. .Pp +Subequations can be enclosed in braces to pass them as arguments +to operation keywords, overriding standard operation precedence. +Braces can be nested. +To set a brace verbatim, it needs to be enclosed in quotes. +.Pp The following text terms are translated into a rendered glyph, if available: alpha, beta, chi, delta, epsilon, eta, gamma, iota, kappa, lambda, mu, nu, omega, omicron, phi, pi, psi, rho, sigma, tau, theta, upsilon, xi, zeta, DELTA, GAMMA, LAMBDA, OMEGA, PHI, PI, PSI, SIGMA, THETA, UPSILON, XI, inter (intersection), union (union), prod (product), int (integral), sum (summation), grad (gradient), del (vector differential), times (multiply), cdot (centre-dot), nothing (zero-width space), approx (approximately equals), prime (prime), half (one-half), partial (partial differential), inf (infinity), >> (much greater), << -(much less), \-> (left arrow), <\- (right arrow), += (plus-minus), != +(much less), \-> (left arrow), <\- (right arrow), +\- (plus-minus), != (not equal), == (equivalence), <= (less-than-equal), and >= (more-than-equal). +The character escape sequences documented in +.Xr mandoc_char 7 +can be used, too. .Pp The following control statements are available: .Bl -tag -width Ds .It Cm define Replace all occurrences of a key with a value. Its syntax is as follows: .Pp -.D1 define Ar key cvalc +.D1 Cm define Ar key cvalc .Pp The first character of the value string, .Ar c , is used as the delimiter for the value .Ar val . This allows for arbitrary enclosure of terms (not just quotes), such as .Pp -.D1 define Ar foo 'bar baz' -.D1 define Ar foo cbar bazc +.D1 Cm define Ar foo 'bar baz' +.D1 Cm define Ar foo cbar bazc .Pp It is an error to have an empty .Ar key or .Ar val . Note that a quoted .Ar key causes errors in some .Nm implementations and should not be considered portable. It is not expanded for replacements. Definitions may refer to other definitions; these are evaluated recursively when text replacement occurs and not when the definition is created. .Pp Definitions can create arbitrary strings, for example, the following is a legal construction. .Bd -literal -offset indent define foo 'define' foo bar 'baz' .Ed .Pp Self-referencing definitions will raise an error. The .Cm ndefine statement is a synonym for .Cm define , while .Cm tdefine is discarded. .It Cm gfont Set the default font of subsequent output. Its syntax is as follows: .Pp -.D1 gfont Ar font +.D1 Cm gfont Ar font .Pp In mandoc, this value is discarded. .It Cm gsize Set the default size of subsequent output. Its syntax is as follows: .Pp -.D1 gsize Ar size +.D1 Cm gsize Oo +|\- Oc Ns Ar size .Pp The .Ar size value should be an integer. +If prepended by a sign, +the font size is changed relative to the current size. .It Cm set Set an equation mode. In mandoc, both arguments are thrown away. Its syntax is as follows: .Pp -.D1 set Ar key val +.D1 Cm set Ar key val .Pp The .Ar key and .Ar val are not expanded for replacements. This statement is a GNU extension. .It Cm undef Unset a previously-defined key. Its syntax is as follows: .Pp -.D1 define Ar key +.D1 Cm define Ar key .Pp Once invoked, the definition for .Ar key is discarded. The .Ar key is not expanded for replacements. This statement is a GNU extension. .El +.Pp +Operation keywords have the following semantics: +.Bl -tag -width Ds +.It Cm above +See +.Cm pile . +.It Cm bar +Draw a line over the preceding box. +.It Cm bold +Set the following box using bold font. +.It Cm ccol +Like +.Cm cpile , +but for use in +.Cm matrix . +.It Cm cpile +Like +.Cm pile , +but with slightly increased vertical spacing. +.It Cm dot +Set a single dot over the preceding box. +.It Cm dotdot +Set two dots (dieresis) over the preceding box. +.It Cm dyad +Set a dyad symbol (left-right arrow) over the preceding box. +.It Cm fat +A synonym for +.Cm bold . +.It Cm font +Set the second argument using the font specified by the first argument; +currently not recognized by the +.Xr mandoc 1 +.Nm +parser. +.It Cm from +Set the following box below the preceding box, +using a slightly smaller font. +Used for sums, integrals, limits, and the like. +.It Cm hat +Set a hat (circumflex) over the preceding box. +.It Cm italic +Set the following box using italic font. +.It Cm lcol +Like +.Cm lpile , +but for use in +.Cm matrix . +.It Cm left +Set the first argument as a big left delimiter before the second argument. +As an optional third argument, +.Cm right +can follow. +In that case, the fourth argument is set as a big right delimiter after +the second argument. +.It Cm lpile +Like +.Cm cpile , +but subequations are left-justified. +.It Cm matrix +Followed by a list of columns enclosed in braces. +All columns need to have the same number of subequations. +The columns are set as a matrix. +The difference compared to multiple subsequent +.Cm pile +operators is that in a +.Cm matrix , +corresponding subequations in all columns line up horizontally, +while each +.Cm pile +does vertical spacing independently. +.It Cm over +Set a fraction. +The preceding box is the numerator, the following box is the denominator. +.It Cm pile +Followed by a list of subequations enclosed in braces, +the subequations being separated by +.Cm above +keywords. +Sets the subequations one above the other, each of them centered. +Typically used to represent vectors in coordinate representation. +.It Cm rcol +Like +.Cm rpile , +but for use in +.Cm matrix . +.It Cm right +See +.Cm left ; +.Cm right +cannot be used without +.Cm left . +To set a big right delimiter without a big left delimiter, the following +construction can be used: +.Pp +.D1 Cm left No \(dq\(dq Ar box Cm right Ar delimiter +.It Cm roman +Set the following box using the default font. +.It Cm rpile +Like +.Cm cpile , +but subequations are right-justified. +.It Cm size +Set the second argument with the font size specified by the first +argument; currently ignored by +.Xr mandoc 1 . +By prepending a plus or minus sign to the first argument, +the font size can be selected relative to the current size. +.It Cm sqrt +Set the square root of the following box. +.It Cm sub +Set the following box as a subscript to the preceding box. +.It Cm sup +Set the following box as a superscript to the preceding box. +As a special case, if a +.Cm sup +clause immediately follows a +.Cm sub +clause as in +.Pp +.D1 Ar mainbox Cm sub Ar subbox Cm sup Ar supbox +.Pp +both are set with respect to the same +.Ar mainbox , +that is, +.Ar supbox +is set above +.Ar subbox . +.It Cm tilde +Set a tilde over the preceding box. +.It Cm to +Set the following box above the preceding box, +using a slightly smaller font. +Used for sums and integrals and the like. +As a special case, if a +.Cm to +clause immediately follows a +.Cm from +clause as in +.Pp +.D1 Ar mainbox Cm from Ar frombox Cm to Ar tobox +.Pp +both are set below and above the same +.Ar mainbox . +.It Cm under +Underline the preceding box. +.It Cm vec +Set a vector symbol (right arrow) over the preceding box. +.El +.Pp +The binary operations +.Cm from , +.Cm to , +.Cm sub , +and +.Cm sup +group to the right, that is, +.Pp +.D1 Ar mainbox Cm sup Ar supbox Cm sub Ar subbox +.Pp +is the same as +.Pp +.D1 Ar mainbox Cm sup Brq Ar supbox Cm sub Ar subbox +.Pp +and different from +.Pp +.D1 Bro Ar mainbox Cm sup Ar supbox Brc Cm sub Ar subbox . +.Pp +By contrast, +.Cm over +groups to the left. +.Pp +In the following list, earlier operations bind more tightly than +later operations: +.Pp +.Bl -enum -compact +.It +.Cm dyad , +.Cm vec , +.Cm under , +.Cm bar , +.Cm tilde , +.Cm hat , +.Cm dot , +.Cm dotdot +.It +.Cm fat , +.Cm roman , +.Cm italic , +.Cm bold , +.Cm size +.It +.Cm sub , +.Cm sup +.It +.Cm sqrt +.It +.Cm over +.It +.Cm from , +.Cm to +.El .Sh COMPATIBILITY This section documents the compatibility of mandoc .Nm and the troff .Nm implementation (including GNU troff). .Pp .Bl -dash -compact .It The text string .Sq \e\*q is interpreted as a literal quote in troff. In mandoc, this is interpreted as a comment. .It In troff, The circumflex and tilde white-space symbols map to fixed-width spaces. In mandoc, these characters are synonyms for the space character. .It The troff implementation of .Nm allows for equation alignment with the .Cm mark and .Cm lineup tokens. mandoc discards these tokens. The .Cm back Ar n , .Cm fwd Ar n , .Cm up Ar n , and .Cm down Ar n commands are also ignored. +.It +Inline equations and the +.Cm delim +control statement are not yet implemented in +.Xr mandoc 1 . .El .Sh SEE ALSO .Xr mandoc 1 , .Xr man 7 , .Xr mandoc_char 7 , .Xr mdoc 7 , .Xr roff 7 .Rs .%A Brian W. Kernighan .%A Lorinda L. Cherry .%T System for Typesetting Mathematics .%J Communications of the ACM .%V 18 .%P 151\(en157 .%D March, 1975 .Re .Rs .%A Brian W. Kernighan .%A Lorinda L. Cherry .%T Typesetting Mathematics, User's Guide .%D 1976 .Re .Rs .%A Brian W. Kernighan .%A Lorinda L. Cherry .%T Typesetting Mathematics, User's Guide (Second Edition) .%D 1978 .Re .Sh HISTORY The eqn utility, a preprocessor for troff, was originally written by Brian W. Kernighan and Lorinda L. Cherry in 1975. The GNU reimplementation of eqn, part of the GNU troff package, was released in 1989 by James Clark. The eqn component of .Xr mandoc 1 was added in 2011. .Sh AUTHORS This .Nm reference was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . Index: vendor/mdocml/dist/eqn.c =================================================================== --- vendor/mdocml/dist/eqn.c (revision 275396) +++ vendor/mdocml/dist/eqn.c (revision 275397) @@ -1,948 +1,1102 @@ -/* $Id: eqn.c,v 1.44 2014/07/06 19:09:00 schwarze Exp $ */ +/* $Id: eqn.c,v 1.56 2014/10/25 15:06:30 schwarze Exp $ */ /* - * Copyright (c) 2011 Kristaps Dzonsons + * Copyright (c) 2011, 2014 Kristaps Dzonsons + * Copyright (c) 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include + #include #include #include #include #include #include #include "mandoc.h" #include "mandoc_aux.h" #include "libmandoc.h" #include "libroff.h" +#define EQN_MSG(t, x) \ + mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL) #define EQN_NEST_MAX 128 /* maximum nesting of defines */ -#define EQN_MSG(t, x) mandoc_msg((t), (x)->parse, (x)->eqn.ln, (x)->eqn.pos, NULL) +#define STRNEQ(p1, sz1, p2, sz2) \ + ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1))) -enum eqn_rest { - EQN_DESCOPE, - EQN_ERR, - EQN_OK, - EQN_EOF +enum eqn_tok { + EQN_TOK_DYAD = 0, + EQN_TOK_VEC, + EQN_TOK_UNDER, + EQN_TOK_BAR, + EQN_TOK_TILDE, + EQN_TOK_HAT, + EQN_TOK_DOT, + EQN_TOK_DOTDOT, + EQN_TOK_FWD, + EQN_TOK_BACK, + EQN_TOK_DOWN, + EQN_TOK_UP, + EQN_TOK_FAT, + EQN_TOK_ROMAN, + EQN_TOK_ITALIC, + EQN_TOK_BOLD, + EQN_TOK_SIZE, + EQN_TOK_SUB, + EQN_TOK_SUP, + EQN_TOK_SQRT, + EQN_TOK_OVER, + EQN_TOK_FROM, + EQN_TOK_TO, + EQN_TOK_BRACE_OPEN, + EQN_TOK_BRACE_CLOSE, + EQN_TOK_GSIZE, + EQN_TOK_GFONT, + EQN_TOK_MARK, + EQN_TOK_LINEUP, + EQN_TOK_LEFT, + EQN_TOK_RIGHT, + EQN_TOK_PILE, + EQN_TOK_LPILE, + EQN_TOK_RPILE, + EQN_TOK_CPILE, + EQN_TOK_MATRIX, + EQN_TOK_CCOL, + EQN_TOK_LCOL, + EQN_TOK_RCOL, + EQN_TOK_DELIM, + EQN_TOK_DEFINE, + EQN_TOK_TDEFINE, + EQN_TOK_NDEFINE, + EQN_TOK_UNDEF, + EQN_TOK_EOF, + EQN_TOK_ABOVE, + EQN_TOK__MAX }; +static const char *eqn_toks[EQN_TOK__MAX] = { + "dyad", /* EQN_TOK_DYAD */ + "vec", /* EQN_TOK_VEC */ + "under", /* EQN_TOK_UNDER */ + "bar", /* EQN_TOK_BAR */ + "tilde", /* EQN_TOK_TILDE */ + "hat", /* EQN_TOK_HAT */ + "dot", /* EQN_TOK_DOT */ + "dotdot", /* EQN_TOK_DOTDOT */ + "fwd", /* EQN_TOK_FWD * */ + "back", /* EQN_TOK_BACK */ + "down", /* EQN_TOK_DOWN */ + "up", /* EQN_TOK_UP */ + "fat", /* EQN_TOK_FAT */ + "roman", /* EQN_TOK_ROMAN */ + "italic", /* EQN_TOK_ITALIC */ + "bold", /* EQN_TOK_BOLD */ + "size", /* EQN_TOK_SIZE */ + "sub", /* EQN_TOK_SUB */ + "sup", /* EQN_TOK_SUP */ + "sqrt", /* EQN_TOK_SQRT */ + "over", /* EQN_TOK_OVER */ + "from", /* EQN_TOK_FROM */ + "to", /* EQN_TOK_TO */ + "{", /* EQN_TOK_BRACE_OPEN */ + "}", /* EQN_TOK_BRACE_CLOSE */ + "gsize", /* EQN_TOK_GSIZE */ + "gfont", /* EQN_TOK_GFONT */ + "mark", /* EQN_TOK_MARK */ + "lineup", /* EQN_TOK_LINEUP */ + "left", /* EQN_TOK_LEFT */ + "right", /* EQN_TOK_RIGHT */ + "pile", /* EQN_TOK_PILE */ + "lpile", /* EQN_TOK_LPILE */ + "rpile", /* EQN_TOK_RPILE */ + "cpile", /* EQN_TOK_CPILE */ + "matrix", /* EQN_TOK_MATRIX */ + "ccol", /* EQN_TOK_CCOL */ + "lcol", /* EQN_TOK_LCOL */ + "rcol", /* EQN_TOK_RCOL */ + "delim", /* EQN_TOK_DELIM */ + "define", /* EQN_TOK_DEFINE */ + "tdefine", /* EQN_TOK_TDEFINE */ + "ndefine", /* EQN_TOK_NDEFINE */ + "undef", /* EQN_TOK_UNDEF */ + NULL, /* EQN_TOK_EOF */ + "above", /* EQN_TOK_ABOVE */ +}; + enum eqn_symt { EQNSYM_alpha, EQNSYM_beta, EQNSYM_chi, EQNSYM_delta, EQNSYM_epsilon, EQNSYM_eta, EQNSYM_gamma, EQNSYM_iota, EQNSYM_kappa, EQNSYM_lambda, EQNSYM_mu, EQNSYM_nu, EQNSYM_omega, EQNSYM_omicron, EQNSYM_phi, EQNSYM_pi, EQNSYM_ps, EQNSYM_rho, EQNSYM_sigma, EQNSYM_tau, EQNSYM_theta, EQNSYM_upsilon, EQNSYM_xi, EQNSYM_zeta, EQNSYM_DELTA, EQNSYM_GAMMA, EQNSYM_LAMBDA, EQNSYM_OMEGA, EQNSYM_PHI, EQNSYM_PI, EQNSYM_PSI, EQNSYM_SIGMA, EQNSYM_THETA, EQNSYM_UPSILON, EQNSYM_XI, EQNSYM_inter, EQNSYM_union, EQNSYM_prod, EQNSYM_int, EQNSYM_sum, EQNSYM_grad, EQNSYM_del, EQNSYM_times, EQNSYM_cdot, EQNSYM_nothing, EQNSYM_approx, EQNSYM_prime, EQNSYM_half, EQNSYM_partial, EQNSYM_inf, EQNSYM_muchgreat, EQNSYM_muchless, EQNSYM_larrow, EQNSYM_rarrow, EQNSYM_pm, EQNSYM_nequal, EQNSYM_equiv, EQNSYM_lessequal, EQNSYM_moreequal, EQNSYM__MAX }; -enum eqnpartt { - EQN_DEFINE = 0, - EQN_NDEFINE, - EQN_TDEFINE, - EQN_SET, - EQN_UNDEF, - EQN_GFONT, - EQN_GSIZE, - EQN_BACK, - EQN_FWD, - EQN_UP, - EQN_DOWN, - EQN__MAX -}; - -struct eqnstr { - const char *name; - size_t sz; -}; - -#define STRNEQ(p1, sz1, p2, sz2) \ - ((sz1) == (sz2) && 0 == strncmp((p1), (p2), (sz1))) -#define EQNSTREQ(x, p, sz) \ - STRNEQ((x)->name, (x)->sz, (p), (sz)) - -struct eqnpart { - struct eqnstr str; - int (*fp)(struct eqn_node *); -}; - struct eqnsym { - struct eqnstr str; + const char *str; const char *sym; }; -static enum eqn_rest eqn_box(struct eqn_node *, struct eqn_box *); -static struct eqn_box *eqn_box_alloc(struct eqn_node *, - struct eqn_box *); -static void eqn_box_free(struct eqn_box *); -static struct eqn_def *eqn_def_find(struct eqn_node *, - const char *, size_t); -static int eqn_do_gfont(struct eqn_node *); -static int eqn_do_gsize(struct eqn_node *); -static int eqn_do_define(struct eqn_node *); -static int eqn_do_ign1(struct eqn_node *); -static int eqn_do_ign2(struct eqn_node *); -static int eqn_do_tdefine(struct eqn_node *); -static int eqn_do_undef(struct eqn_node *); -static enum eqn_rest eqn_eqn(struct eqn_node *, struct eqn_box *); -static enum eqn_rest eqn_list(struct eqn_node *, struct eqn_box *); -static enum eqn_rest eqn_matrix(struct eqn_node *, struct eqn_box *); -static const char *eqn_nexttok(struct eqn_node *, size_t *); -static const char *eqn_nextrawtok(struct eqn_node *, size_t *); -static const char *eqn_next(struct eqn_node *, - char, size_t *, int); -static void eqn_rewind(struct eqn_node *); - -static const struct eqnpart eqnparts[EQN__MAX] = { - { { "define", 6 }, eqn_do_define }, /* EQN_DEFINE */ - { { "ndefine", 7 }, eqn_do_define }, /* EQN_NDEFINE */ - { { "tdefine", 7 }, eqn_do_tdefine }, /* EQN_TDEFINE */ - { { "set", 3 }, eqn_do_ign2 }, /* EQN_SET */ - { { "undef", 5 }, eqn_do_undef }, /* EQN_UNDEF */ - { { "gfont", 5 }, eqn_do_gfont }, /* EQN_GFONT */ - { { "gsize", 5 }, eqn_do_gsize }, /* EQN_GSIZE */ - { { "back", 4 }, eqn_do_ign1 }, /* EQN_BACK */ - { { "fwd", 3 }, eqn_do_ign1 }, /* EQN_FWD */ - { { "up", 2 }, eqn_do_ign1 }, /* EQN_UP */ - { { "down", 4 }, eqn_do_ign1 }, /* EQN_DOWN */ -}; - -static const struct eqnstr eqnmarks[EQNMARK__MAX] = { - { "", 0 }, /* EQNMARK_NONE */ - { "dot", 3 }, /* EQNMARK_DOT */ - { "dotdot", 6 }, /* EQNMARK_DOTDOT */ - { "hat", 3 }, /* EQNMARK_HAT */ - { "tilde", 5 }, /* EQNMARK_TILDE */ - { "vec", 3 }, /* EQNMARK_VEC */ - { "dyad", 4 }, /* EQNMARK_DYAD */ - { "bar", 3 }, /* EQNMARK_BAR */ - { "under", 5 }, /* EQNMARK_UNDER */ -}; - -static const struct eqnstr eqnfonts[EQNFONT__MAX] = { - { "", 0 }, /* EQNFONT_NONE */ - { "roman", 5 }, /* EQNFONT_ROMAN */ - { "bold", 4 }, /* EQNFONT_BOLD */ - { "fat", 3 }, /* EQNFONT_FAT */ - { "italic", 6 }, /* EQNFONT_ITALIC */ -}; - -static const struct eqnstr eqnposs[EQNPOS__MAX] = { - { "", 0 }, /* EQNPOS_NONE */ - { "over", 4 }, /* EQNPOS_OVER */ - { "sup", 3 }, /* EQNPOS_SUP */ - { "sub", 3 }, /* EQNPOS_SUB */ - { "to", 2 }, /* EQNPOS_TO */ - { "from", 4 }, /* EQNPOS_FROM */ -}; - -static const struct eqnstr eqnpiles[EQNPILE__MAX] = { - { "", 0 }, /* EQNPILE_NONE */ - { "pile", 4 }, /* EQNPILE_PILE */ - { "cpile", 5 }, /* EQNPILE_CPILE */ - { "rpile", 5 }, /* EQNPILE_RPILE */ - { "lpile", 5 }, /* EQNPILE_LPILE */ - { "col", 3 }, /* EQNPILE_COL */ - { "ccol", 4 }, /* EQNPILE_CCOL */ - { "rcol", 4 }, /* EQNPILE_RCOL */ - { "lcol", 4 }, /* EQNPILE_LCOL */ -}; - static const struct eqnsym eqnsyms[EQNSYM__MAX] = { - { { "alpha", 5 }, "*a" }, /* EQNSYM_alpha */ - { { "beta", 4 }, "*b" }, /* EQNSYM_beta */ - { { "chi", 3 }, "*x" }, /* EQNSYM_chi */ - { { "delta", 5 }, "*d" }, /* EQNSYM_delta */ - { { "epsilon", 7 }, "*e" }, /* EQNSYM_epsilon */ - { { "eta", 3 }, "*y" }, /* EQNSYM_eta */ - { { "gamma", 5 }, "*g" }, /* EQNSYM_gamma */ - { { "iota", 4 }, "*i" }, /* EQNSYM_iota */ - { { "kappa", 5 }, "*k" }, /* EQNSYM_kappa */ - { { "lambda", 6 }, "*l" }, /* EQNSYM_lambda */ - { { "mu", 2 }, "*m" }, /* EQNSYM_mu */ - { { "nu", 2 }, "*n" }, /* EQNSYM_nu */ - { { "omega", 5 }, "*w" }, /* EQNSYM_omega */ - { { "omicron", 7 }, "*o" }, /* EQNSYM_omicron */ - { { "phi", 3 }, "*f" }, /* EQNSYM_phi */ - { { "pi", 2 }, "*p" }, /* EQNSYM_pi */ - { { "psi", 2 }, "*q" }, /* EQNSYM_psi */ - { { "rho", 3 }, "*r" }, /* EQNSYM_rho */ - { { "sigma", 5 }, "*s" }, /* EQNSYM_sigma */ - { { "tau", 3 }, "*t" }, /* EQNSYM_tau */ - { { "theta", 5 }, "*h" }, /* EQNSYM_theta */ - { { "upsilon", 7 }, "*u" }, /* EQNSYM_upsilon */ - { { "xi", 2 }, "*c" }, /* EQNSYM_xi */ - { { "zeta", 4 }, "*z" }, /* EQNSYM_zeta */ - { { "DELTA", 5 }, "*D" }, /* EQNSYM_DELTA */ - { { "GAMMA", 5 }, "*G" }, /* EQNSYM_GAMMA */ - { { "LAMBDA", 6 }, "*L" }, /* EQNSYM_LAMBDA */ - { { "OMEGA", 5 }, "*W" }, /* EQNSYM_OMEGA */ - { { "PHI", 3 }, "*F" }, /* EQNSYM_PHI */ - { { "PI", 2 }, "*P" }, /* EQNSYM_PI */ - { { "PSI", 3 }, "*Q" }, /* EQNSYM_PSI */ - { { "SIGMA", 5 }, "*S" }, /* EQNSYM_SIGMA */ - { { "THETA", 5 }, "*H" }, /* EQNSYM_THETA */ - { { "UPSILON", 7 }, "*U" }, /* EQNSYM_UPSILON */ - { { "XI", 2 }, "*C" }, /* EQNSYM_XI */ - { { "inter", 5 }, "ca" }, /* EQNSYM_inter */ - { { "union", 5 }, "cu" }, /* EQNSYM_union */ - { { "prod", 4 }, "product" }, /* EQNSYM_prod */ - { { "int", 3 }, "integral" }, /* EQNSYM_int */ - { { "sum", 3 }, "sum" }, /* EQNSYM_sum */ - { { "grad", 4 }, "gr" }, /* EQNSYM_grad */ - { { "del", 3 }, "gr" }, /* EQNSYM_del */ - { { "times", 5 }, "mu" }, /* EQNSYM_times */ - { { "cdot", 4 }, "pc" }, /* EQNSYM_cdot */ - { { "nothing", 7 }, "&" }, /* EQNSYM_nothing */ - { { "approx", 6 }, "~~" }, /* EQNSYM_approx */ - { { "prime", 5 }, "aq" }, /* EQNSYM_prime */ - { { "half", 4 }, "12" }, /* EQNSYM_half */ - { { "partial", 7 }, "pd" }, /* EQNSYM_partial */ - { { "inf", 3 }, "if" }, /* EQNSYM_inf */ - { { ">>", 2 }, ">>" }, /* EQNSYM_muchgreat */ - { { "<<", 2 }, "<<" }, /* EQNSYM_muchless */ - { { "<-", 2 }, "<-" }, /* EQNSYM_larrow */ - { { "->", 2 }, "->" }, /* EQNSYM_rarrow */ - { { "+-", 2 }, "+-" }, /* EQNSYM_pm */ - { { "!=", 2 }, "!=" }, /* EQNSYM_nequal */ - { { "==", 2 }, "==" }, /* EQNSYM_equiv */ - { { "<=", 2 }, "<=" }, /* EQNSYM_lessequal */ - { { ">=", 2 }, ">=" }, /* EQNSYM_moreequal */ + { "alpha", "*a" }, /* EQNSYM_alpha */ + { "beta", "*b" }, /* EQNSYM_beta */ + { "chi", "*x" }, /* EQNSYM_chi */ + { "delta", "*d" }, /* EQNSYM_delta */ + { "epsilon", "*e" }, /* EQNSYM_epsilon */ + { "eta", "*y" }, /* EQNSYM_eta */ + { "gamma", "*g" }, /* EQNSYM_gamma */ + { "iota", "*i" }, /* EQNSYM_iota */ + { "kappa", "*k" }, /* EQNSYM_kappa */ + { "lambda", "*l" }, /* EQNSYM_lambda */ + { "mu", "*m" }, /* EQNSYM_mu */ + { "nu", "*n" }, /* EQNSYM_nu */ + { "omega", "*w" }, /* EQNSYM_omega */ + { "omicron", "*o" }, /* EQNSYM_omicron */ + { "phi", "*f" }, /* EQNSYM_phi */ + { "pi", "*p" }, /* EQNSYM_pi */ + { "psi", "*q" }, /* EQNSYM_psi */ + { "rho", "*r" }, /* EQNSYM_rho */ + { "sigma", "*s" }, /* EQNSYM_sigma */ + { "tau", "*t" }, /* EQNSYM_tau */ + { "theta", "*h" }, /* EQNSYM_theta */ + { "upsilon", "*u" }, /* EQNSYM_upsilon */ + { "xi", "*c" }, /* EQNSYM_xi */ + { "zeta", "*z" }, /* EQNSYM_zeta */ + { "DELTA", "*D" }, /* EQNSYM_DELTA */ + { "GAMMA", "*G" }, /* EQNSYM_GAMMA */ + { "LAMBDA", "*L" }, /* EQNSYM_LAMBDA */ + { "OMEGA", "*W" }, /* EQNSYM_OMEGA */ + { "PHI", "*F" }, /* EQNSYM_PHI */ + { "PI", "*P" }, /* EQNSYM_PI */ + { "PSI", "*Q" }, /* EQNSYM_PSI */ + { "SIGMA", "*S" }, /* EQNSYM_SIGMA */ + { "THETA", "*H" }, /* EQNSYM_THETA */ + { "UPSILON", "*U" }, /* EQNSYM_UPSILON */ + { "XI", "*C" }, /* EQNSYM_XI */ + { "inter", "ca" }, /* EQNSYM_inter */ + { "union", "cu" }, /* EQNSYM_union */ + { "prod", "product" }, /* EQNSYM_prod */ + { "int", "integral" }, /* EQNSYM_int */ + { "sum", "sum" }, /* EQNSYM_sum */ + { "grad", "gr" }, /* EQNSYM_grad */ + { "del", "gr" }, /* EQNSYM_del */ + { "times", "mu" }, /* EQNSYM_times */ + { "cdot", "pc" }, /* EQNSYM_cdot */ + { "nothing", "&" }, /* EQNSYM_nothing */ + { "approx", "~~" }, /* EQNSYM_approx */ + { "prime", "aq" }, /* EQNSYM_prime */ + { "half", "12" }, /* EQNSYM_half */ + { "partial", "pd" }, /* EQNSYM_partial */ + { "inf", "if" }, /* EQNSYM_inf */ + { ">>", ">>" }, /* EQNSYM_muchgreat */ + { "<<", "<<" }, /* EQNSYM_muchless */ + { "<-", "<-" }, /* EQNSYM_larrow */ + { "->", "->" }, /* EQNSYM_rarrow */ + { "+-", "+-" }, /* EQNSYM_pm */ + { "!=", "!=" }, /* EQNSYM_nequal */ + { "==", "==" }, /* EQNSYM_equiv */ + { "<=", "<=" }, /* EQNSYM_lessequal */ + { ">=", ">=" }, /* EQNSYM_moreequal */ }; - enum rofferr eqn_read(struct eqn_node **epp, int ln, const char *p, int pos, int *offs) { size_t sz; struct eqn_node *ep; enum rofferr er; ep = *epp; /* * If we're the terminating mark, unset our equation status and * validate the full equation. */ if (0 == strncmp(p, ".EN", 3)) { er = eqn_end(epp); p += 3; while (' ' == *p || '\t' == *p) p++; if ('\0' == *p) return(er); mandoc_vmsg(MANDOCERR_ARG_SKIP, ep->parse, ln, pos, "EN %s", p); return(er); } /* * Build up the full string, replacing all newlines with regular * whitespace. */ sz = strlen(p + pos) + 1; ep->data = mandoc_realloc(ep->data, ep->sz + sz + 1); /* First invocation: nil terminate the string. */ if (0 == ep->sz) *ep->data = '\0'; ep->sz += sz; strlcat(ep->data, p + pos, ep->sz + 1); strlcat(ep->data, " ", ep->sz + 1); return(ROFF_IGN); } struct eqn_node * -eqn_alloc(const char *name, int pos, int line, struct mparse *parse) +eqn_alloc(int pos, int line, struct mparse *parse) { struct eqn_node *p; - size_t sz; - const char *end; p = mandoc_calloc(1, sizeof(struct eqn_node)); - if (name && '\0' != *name) { - sz = strlen(name); - assert(sz); - do { - sz--; - end = name + (int)sz; - } while (' ' == *end || '\t' == *end); - p->eqn.name = mandoc_strndup(name, sz + 1); - } - p->parse = parse; p->eqn.ln = line; p->eqn.pos = pos; p->gsize = EQN_DEFSIZE; return(p); } -enum rofferr -eqn_end(struct eqn_node **epp) +/* + * Find the key "key" of the give size within our eqn-defined values. + */ +static struct eqn_def * +eqn_def_find(struct eqn_node *ep, const char *key, size_t sz) { - struct eqn_node *ep; - struct eqn_box *root; - enum eqn_rest c; - - ep = *epp; - *epp = NULL; - - ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box)); - - root = ep->eqn.root; - root->type = EQN_ROOT; - - if (0 == ep->sz) - return(ROFF_IGN); - - if (EQN_DESCOPE == (c = eqn_eqn(ep, root))) { - EQN_MSG(MANDOCERR_EQNNSCOPE, ep); - c = EQN_ERR; - } - - return(EQN_EOF == c ? ROFF_EQN : ROFF_IGN); -} - -static enum eqn_rest -eqn_eqn(struct eqn_node *ep, struct eqn_box *last) -{ - struct eqn_box *bp; - enum eqn_rest c; - - bp = eqn_box_alloc(ep, last); - bp->type = EQN_SUBEXPR; - - while (EQN_OK == (c = eqn_box(ep, bp))) - /* Spin! */ ; - - return(c); -} - -static enum eqn_rest -eqn_matrix(struct eqn_node *ep, struct eqn_box *last) -{ - struct eqn_box *bp; - const char *start; - size_t sz; - enum eqn_rest c; - - bp = eqn_box_alloc(ep, last); - bp->type = EQN_MATRIX; - - if (NULL == (start = eqn_nexttok(ep, &sz))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } - if ( ! STRNEQ(start, sz, "{", 1)) { - EQN_MSG(MANDOCERR_EQNSYNT, ep); - return(EQN_ERR); - } - - while (EQN_OK == (c = eqn_box(ep, bp))) - switch (bp->last->pile) { - case EQNPILE_LCOL: - /* FALLTHROUGH */ - case EQNPILE_CCOL: - /* FALLTHROUGH */ - case EQNPILE_RCOL: - continue; - default: - EQN_MSG(MANDOCERR_EQNSYNT, ep); - return(EQN_ERR); - }; - - if (EQN_DESCOPE != c) { - if (EQN_EOF == c) - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } - - eqn_rewind(ep); - start = eqn_nexttok(ep, &sz); - assert(start); - if (STRNEQ(start, sz, "}", 1)) - return(EQN_OK); - - EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); - return(EQN_ERR); -} - -static enum eqn_rest -eqn_list(struct eqn_node *ep, struct eqn_box *last) -{ - struct eqn_box *bp; - const char *start; - size_t sz; - enum eqn_rest c; - - bp = eqn_box_alloc(ep, last); - bp->type = EQN_LIST; - - if (NULL == (start = eqn_nexttok(ep, &sz))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } - if ( ! STRNEQ(start, sz, "{", 1)) { - EQN_MSG(MANDOCERR_EQNSYNT, ep); - return(EQN_ERR); - } - - while (EQN_DESCOPE == (c = eqn_eqn(ep, bp))) { - eqn_rewind(ep); - start = eqn_nexttok(ep, &sz); - assert(start); - if ( ! STRNEQ(start, sz, "above", 5)) - break; - } - - if (EQN_DESCOPE != c) { - if (EQN_ERR != c) - EQN_MSG(MANDOCERR_EQNSCOPE, ep); - return(EQN_ERR); - } - - eqn_rewind(ep); - start = eqn_nexttok(ep, &sz); - assert(start); - if (STRNEQ(start, sz, "}", 1)) - return(EQN_OK); - - EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); - return(EQN_ERR); -} - -static enum eqn_rest -eqn_box(struct eqn_node *ep, struct eqn_box *last) -{ - size_t sz; - const char *start; - char *left; - char sym[64]; - enum eqn_rest c; - int i, size; - struct eqn_box *bp; - - if (NULL == (start = eqn_nexttok(ep, &sz))) - return(EQN_EOF); - - if (STRNEQ(start, sz, "}", 1)) - return(EQN_DESCOPE); - else if (STRNEQ(start, sz, "right", 5)) - return(EQN_DESCOPE); - else if (STRNEQ(start, sz, "above", 5)) - return(EQN_DESCOPE); - else if (STRNEQ(start, sz, "mark", 4)) - return(EQN_OK); - else if (STRNEQ(start, sz, "lineup", 6)) - return(EQN_OK); - - for (i = 0; i < (int)EQN__MAX; i++) { - if ( ! EQNSTREQ(&eqnparts[i].str, start, sz)) - continue; - return((*eqnparts[i].fp)(ep) ? EQN_OK : EQN_ERR); - } - - if (STRNEQ(start, sz, "{", 1)) { - if (EQN_DESCOPE != (c = eqn_eqn(ep, last))) { - if (EQN_ERR != c) - EQN_MSG(MANDOCERR_EQNSCOPE, ep); - return(EQN_ERR); - } - eqn_rewind(ep); - start = eqn_nexttok(ep, &sz); - assert(start); - if (STRNEQ(start, sz, "}", 1)) - return(EQN_OK); - EQN_MSG(MANDOCERR_EQNBADSCOPE, ep); - return(EQN_ERR); - } - - for (i = 0; i < (int)EQNPILE__MAX; i++) { - if ( ! EQNSTREQ(&eqnpiles[i], start, sz)) - continue; - if (EQN_OK == (c = eqn_list(ep, last))) - last->last->pile = (enum eqn_pilet)i; - return(c); - } - - if (STRNEQ(start, sz, "matrix", 6)) - return(eqn_matrix(ep, last)); - - if (STRNEQ(start, sz, "left", 4)) { - if (NULL == (start = eqn_nexttok(ep, &sz))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } - left = mandoc_strndup(start, sz); - c = eqn_eqn(ep, last); - if (last->last) - last->last->left = left; - else - free(left); - if (EQN_DESCOPE != c) - return(c); - assert(last->last); - eqn_rewind(ep); - start = eqn_nexttok(ep, &sz); - assert(start); - if ( ! STRNEQ(start, sz, "right", 5)) - return(EQN_DESCOPE); - if (NULL == (start = eqn_nexttok(ep, &sz))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } - last->last->right = mandoc_strndup(start, sz); - return(EQN_OK); - } - - for (i = 0; i < (int)EQNPOS__MAX; i++) { - if ( ! EQNSTREQ(&eqnposs[i], start, sz)) - continue; - if (NULL == last->last) { - EQN_MSG(MANDOCERR_EQNSYNT, ep); - return(EQN_ERR); - } - last->last->pos = (enum eqn_post)i; - if (EQN_EOF == (c = eqn_box(ep, last))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } - return(c); - } - - for (i = 0; i < (int)EQNMARK__MAX; i++) { - if ( ! EQNSTREQ(&eqnmarks[i], start, sz)) - continue; - if (NULL == last->last) { - EQN_MSG(MANDOCERR_EQNSYNT, ep); - return(EQN_ERR); - } - last->last->mark = (enum eqn_markt)i; - if (EQN_EOF == (c = eqn_box(ep, last))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } - return(c); - } - - for (i = 0; i < (int)EQNFONT__MAX; i++) { - if ( ! EQNSTREQ(&eqnfonts[i], start, sz)) - continue; - if (EQN_EOF == (c = eqn_box(ep, last))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } else if (EQN_OK == c) - last->last->font = (enum eqn_fontt)i; - return(c); - } - - if (STRNEQ(start, sz, "size", 4)) { - if (NULL == (start = eqn_nexttok(ep, &sz))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } - size = mandoc_strntoi(start, sz, 10); - if (EQN_EOF == (c = eqn_box(ep, last))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(EQN_ERR); - } else if (EQN_OK != c) - return(c); - last->last->size = size; - } - - bp = eqn_box_alloc(ep, last); - bp->type = EQN_TEXT; - for (i = 0; i < (int)EQNSYM__MAX; i++) - if (EQNSTREQ(&eqnsyms[i].str, start, sz)) { - sym[63] = '\0'; - (void)snprintf(sym, 62, "\\[%s]", eqnsyms[i].sym); - bp->text = mandoc_strdup(sym); - return(EQN_OK); - } - - bp->text = mandoc_strndup(start, sz); - return(EQN_OK); -} - -void -eqn_free(struct eqn_node *p) -{ int i; - eqn_box_free(p->eqn.root); + for (i = 0; i < (int)ep->defsz; i++) + if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, + ep->defs[i].keysz, key, sz)) + return(&ep->defs[i]); - for (i = 0; i < (int)p->defsz; i++) { - free(p->defs[i].key); - free(p->defs[i].val); - } - - free(p->eqn.name); - free(p->data); - free(p->defs); - free(p); + return(NULL); } -static struct eqn_box * -eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) -{ - struct eqn_box *bp; - - bp = mandoc_calloc(1, sizeof(struct eqn_box)); - bp->parent = parent; - bp->size = ep->gsize; - - if (NULL == parent->first) - parent->first = bp; - else - parent->last->next = bp; - - parent->last = bp; - return(bp); -} - -static void -eqn_box_free(struct eqn_box *bp) -{ - - if (bp->first) - eqn_box_free(bp->first); - if (bp->next) - eqn_box_free(bp->next); - - free(bp->text); - free(bp->left); - free(bp->right); - free(bp); -} - +/* + * Get the next token from the input stream using the given quote + * character. + * Optionally make any replacements. + */ static const char * -eqn_nextrawtok(struct eqn_node *ep, size_t *sz) -{ - - return(eqn_next(ep, '"', sz, 0)); -} - -static const char * -eqn_nexttok(struct eqn_node *ep, size_t *sz) -{ - - return(eqn_next(ep, '"', sz, 1)); -} - -static void -eqn_rewind(struct eqn_node *ep) -{ - - ep->cur = ep->rew; -} - -static const char * eqn_next(struct eqn_node *ep, char quote, size_t *sz, int repl) { char *start, *next; int q, diff, lim; size_t ssz, dummy; struct eqn_def *def; if (NULL == sz) sz = &dummy; lim = 0; ep->rew = ep->cur; again: /* Prevent self-definitions. */ if (lim >= EQN_NEST_MAX) { EQN_MSG(MANDOCERR_ROFFLOOP, ep); return(NULL); } ep->cur = ep->rew; start = &ep->data[(int)ep->cur]; q = 0; if ('\0' == *start) return(NULL); if (quote == *start) { ep->cur++; q = 1; } start = &ep->data[(int)ep->cur]; if ( ! q) { if ('{' == *start || '}' == *start) ssz = 1; else ssz = strcspn(start + 1, " ^~\"{}\t") + 1; next = start + (int)ssz; if ('\0' == *next) next = NULL; } else next = strchr(start, quote); if (NULL != next) { *sz = (size_t)(next - start); ep->cur += *sz; if (q) ep->cur++; while (' ' == ep->data[(int)ep->cur] || '\t' == ep->data[(int)ep->cur] || '^' == ep->data[(int)ep->cur] || '~' == ep->data[(int)ep->cur]) ep->cur++; } else { if (q) EQN_MSG(MANDOCERR_ARG_QUOTE, ep); next = strchr(start, '\0'); *sz = (size_t)(next - start); ep->cur += *sz; } /* Quotes aren't expanded for values. */ if (q || ! repl) return(start); if (NULL != (def = eqn_def_find(ep, start, *sz))) { diff = def->valsz - *sz; if (def->valsz > *sz) { ep->sz += diff; ep->data = mandoc_realloc(ep->data, ep->sz + 1); ep->data[ep->sz] = '\0'; start = &ep->data[(int)ep->rew]; } diff = def->valsz - *sz; memmove(start + *sz + diff, start + *sz, (strlen(start) - *sz) + 1); memcpy(start, def->val, def->valsz); goto again; } return(start); } -static int -eqn_do_ign1(struct eqn_node *ep) +/* + * Get the next delimited token using the default current quote + * character. + */ +static const char * +eqn_nexttok(struct eqn_node *ep, size_t *sz) { - if (NULL == eqn_nextrawtok(ep, NULL)) - EQN_MSG(MANDOCERR_EQNEOF, ep); - else - return(1); + return(eqn_next(ep, '"', sz, 1)); +} - return(0); +/* + * Get next token without replacement. + */ +static const char * +eqn_nextrawtok(struct eqn_node *ep, size_t *sz) +{ + + return(eqn_next(ep, '"', sz, 0)); } -static int -eqn_do_ign2(struct eqn_node *ep) +/* + * Parse a token from the stream of text. + * A token consists of one of the recognised eqn(7) strings. + * Strings are separated by delimiting marks. + * This returns EQN_TOK_EOF when there are no more tokens. + * If the token is an unrecognised string literal, then it returns + * EQN_TOK__MAX and sets the "p" pointer to an allocated, nil-terminated + * string. + * This must be later freed with free(3). + */ +static enum eqn_tok +eqn_tok_parse(struct eqn_node *ep, char **p) { + const char *start; + size_t i, sz; + int quoted; - if (NULL == eqn_nextrawtok(ep, NULL)) - EQN_MSG(MANDOCERR_EQNEOF, ep); - else if (NULL == eqn_nextrawtok(ep, NULL)) - EQN_MSG(MANDOCERR_EQNEOF, ep); - else - return(1); + if (NULL != p) + *p = NULL; - return(0); + quoted = ep->data[ep->cur] == '"'; + + if (NULL == (start = eqn_nexttok(ep, &sz))) + return(EQN_TOK_EOF); + + if (quoted) { + if (p != NULL) + *p = mandoc_strndup(start, sz); + return(EQN_TOK__MAX); + } + + for (i = 0; i < EQN_TOK__MAX; i++) { + if (NULL == eqn_toks[i]) + continue; + if (STRNEQ(start, sz, eqn_toks[i], strlen(eqn_toks[i]))) + break; + } + + if (i == EQN_TOK__MAX && NULL != p) + *p = mandoc_strndup(start, sz); + + return(i); } +static void +eqn_box_free(struct eqn_box *bp) +{ + + if (bp->first) + eqn_box_free(bp->first); + if (bp->next) + eqn_box_free(bp->next); + + free(bp->text); + free(bp->left); + free(bp->right); + free(bp->top); + free(bp->bottom); + free(bp); +} + +/* + * Allocate a box as the last child of the parent node. + */ +static struct eqn_box * +eqn_box_alloc(struct eqn_node *ep, struct eqn_box *parent) +{ + struct eqn_box *bp; + + bp = mandoc_calloc(1, sizeof(struct eqn_box)); + bp->parent = parent; + bp->parent->args++; + bp->expectargs = UINT_MAX; + bp->size = ep->gsize; + + if (NULL != parent->first) { + parent->last->next = bp; + bp->prev = parent->last; + } else + parent->first = bp; + + parent->last = bp; + return(bp); +} + +/* + * Reparent the current last node (of the current parent) under a new + * EQN_SUBEXPR as the first element. + * Then return the new parent. + * The new EQN_SUBEXPR will have a two-child limit. + */ +static struct eqn_box * +eqn_box_makebinary(struct eqn_node *ep, + enum eqn_post pos, struct eqn_box *parent) +{ + struct eqn_box *b, *newb; + + assert(NULL != parent->last); + b = parent->last; + if (parent->last == parent->first) + parent->first = NULL; + parent->args--; + parent->last = b->prev; + b->prev = NULL; + newb = eqn_box_alloc(ep, parent); + newb->pos = pos; + newb->type = EQN_SUBEXPR; + newb->expectargs = 2; + newb->args = 1; + newb->first = newb->last = b; + newb->first->next = NULL; + b->parent = newb; + return(newb); +} + +/* + * Parse the "delim" control statement. + */ +static void +eqn_delim(struct eqn_node *ep) +{ + const char *start; + size_t sz; + + if ((start = eqn_nextrawtok(ep, &sz)) == NULL) + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + ep->eqn.ln, ep->eqn.pos, "delim"); + else if (strncmp(start, "off", 3) == 0) + ep->delim = 0; + else if (strncmp(start, "on", 2) == 0) { + if (ep->odelim && ep->cdelim) + ep->delim = 1; + } else if (start[1] != '\0') { + ep->odelim = start[0]; + ep->cdelim = start[1]; + ep->delim = 1; + } +} + +/* + * Undefine a previously-defined string. + */ static int -eqn_do_tdefine(struct eqn_node *ep) +eqn_undef(struct eqn_node *ep) { + const char *start; + struct eqn_def *def; + size_t sz; - if (NULL == eqn_nextrawtok(ep, NULL)) + if (NULL == (start = eqn_nextrawtok(ep, &sz))) { EQN_MSG(MANDOCERR_EQNEOF, ep); - else if (NULL == eqn_next(ep, ep->data[(int)ep->cur], NULL, 0)) - EQN_MSG(MANDOCERR_EQNEOF, ep); - else - return(1); + return(0); + } else if (NULL != (def = eqn_def_find(ep, start, sz))) + def->keysz = 0; - return(0); + return(1); } static int -eqn_do_define(struct eqn_node *ep) +eqn_def(struct eqn_node *ep) { const char *start; size_t sz; struct eqn_def *def; int i; if (NULL == (start = eqn_nextrawtok(ep, &sz))) { EQN_MSG(MANDOCERR_EQNEOF, ep); return(0); } /* * Search for a key that already exists. * Create a new key if none is found. */ - if (NULL == (def = eqn_def_find(ep, start, sz))) { /* Find holes in string array. */ for (i = 0; i < (int)ep->defsz; i++) if (0 == ep->defs[i].keysz) break; if (i == (int)ep->defsz) { ep->defsz++; ep->defs = mandoc_reallocarray(ep->defs, ep->defsz, sizeof(struct eqn_def)); ep->defs[i].key = ep->defs[i].val = NULL; } ep->defs[i].keysz = sz; ep->defs[i].key = mandoc_realloc( ep->defs[i].key, sz + 1); memcpy(ep->defs[i].key, start, sz); ep->defs[i].key[(int)sz] = '\0'; def = &ep->defs[i]; } start = eqn_next(ep, ep->data[(int)ep->cur], &sz, 0); if (NULL == start) { EQN_MSG(MANDOCERR_EQNEOF, ep); - return(0); + return(-1); } def->valsz = sz; def->val = mandoc_realloc(def->val, sz + 1); memcpy(def->val, start, sz); def->val[(int)sz] = '\0'; return(1); } +/* + * Recursively parse an eqn(7) expression. + */ static int -eqn_do_gfont(struct eqn_node *ep) +eqn_parse(struct eqn_node *ep, struct eqn_box *parent) { + char *p; + enum eqn_tok tok, subtok; + enum eqn_post pos; + struct eqn_box *cur; + int rc, size; + size_t i, sz; + char sym[64]; + const char *start; - if (NULL == eqn_nextrawtok(ep, NULL)) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(0); - } - return(1); -} + assert(parent != NULL); + if (ep->data == NULL) + return(-1); -static int -eqn_do_gsize(struct eqn_node *ep) -{ - const char *start; - size_t sz; +next_tok: + tok = eqn_tok_parse(ep, &p); - if (NULL == (start = eqn_nextrawtok(ep, &sz))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); +this_tok: + switch (tok) { + case (EQN_TOK_UNDEF): + if ((rc = eqn_undef(ep)) <= 0) + return(rc); + break; + case (EQN_TOK_NDEFINE): + case (EQN_TOK_DEFINE): + if ((rc = eqn_def(ep)) <= 0) + return(rc); + break; + case (EQN_TOK_TDEFINE): + if (NULL == eqn_nextrawtok(ep, NULL)) + EQN_MSG(MANDOCERR_EQNEOF, ep); + else if (NULL == eqn_next(ep, + ep->data[(int)ep->cur], NULL, 0)) + EQN_MSG(MANDOCERR_EQNEOF, ep); + break; + case (EQN_TOK_DELIM): + eqn_delim(ep); + break; + case (EQN_TOK_GFONT): + if (eqn_nextrawtok(ep, NULL) == NULL) + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + break; + case (EQN_TOK_MARK): + case (EQN_TOK_LINEUP): + /* Ignore these. */ + break; + case (EQN_TOK_DYAD): + case (EQN_TOK_VEC): + case (EQN_TOK_UNDER): + case (EQN_TOK_BAR): + case (EQN_TOK_TILDE): + case (EQN_TOK_HAT): + case (EQN_TOK_DOT): + case (EQN_TOK_DOTDOT): + if (parent->last == NULL) { + mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + cur = eqn_box_alloc(ep, parent); + cur->type = EQN_TEXT; + cur->text = mandoc_strdup(""); + } + parent = eqn_box_makebinary(ep, EQNPOS_NONE, parent); + parent->type = EQN_LISTONE; + parent->expectargs = 1; + switch (tok) { + case (EQN_TOK_DOTDOT): + strlcpy(sym, "\\[ad]", sizeof(sym)); + break; + case (EQN_TOK_VEC): + strlcpy(sym, "\\[->]", sizeof(sym)); + break; + case (EQN_TOK_DYAD): + strlcpy(sym, "\\[<>]", sizeof(sym)); + break; + case (EQN_TOK_TILDE): + strlcpy(sym, "\\[a~]", sizeof(sym)); + break; + case (EQN_TOK_UNDER): + strlcpy(sym, "\\[ul]", sizeof(sym)); + break; + case (EQN_TOK_BAR): + strlcpy(sym, "\\[rl]", sizeof(sym)); + break; + case (EQN_TOK_DOT): + strlcpy(sym, "\\[a.]", sizeof(sym)); + break; + case (EQN_TOK_HAT): + strlcpy(sym, "\\[ha]", sizeof(sym)); + break; + default: + abort(); + } + + switch (tok) { + case (EQN_TOK_DOTDOT): + case (EQN_TOK_VEC): + case (EQN_TOK_DYAD): + case (EQN_TOK_TILDE): + case (EQN_TOK_BAR): + case (EQN_TOK_DOT): + case (EQN_TOK_HAT): + parent->top = mandoc_strdup(sym); + break; + case (EQN_TOK_UNDER): + parent->bottom = mandoc_strdup(sym); + break; + default: + abort(); + } + parent = parent->parent; + break; + case (EQN_TOK_FWD): + case (EQN_TOK_BACK): + case (EQN_TOK_DOWN): + case (EQN_TOK_UP): + subtok = eqn_tok_parse(ep, NULL); + if (subtok != EQN_TOK__MAX) { + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + tok = subtok; + goto this_tok; + } + break; + case (EQN_TOK_FAT): + case (EQN_TOK_ROMAN): + case (EQN_TOK_ITALIC): + case (EQN_TOK_BOLD): + while (parent->args == parent->expectargs) + parent = parent->parent; + /* + * These values apply to the next word or sequence of + * words; thus, we mark that we'll have a child with + * exactly one of those. + */ + parent = eqn_box_alloc(ep, parent); + parent->type = EQN_LISTONE; + parent->expectargs = 1; + switch (tok) { + case (EQN_TOK_FAT): + parent->font = EQNFONT_FAT; + break; + case (EQN_TOK_ROMAN): + parent->font = EQNFONT_ROMAN; + break; + case (EQN_TOK_ITALIC): + parent->font = EQNFONT_ITALIC; + break; + case (EQN_TOK_BOLD): + parent->font = EQNFONT_BOLD; + break; + default: + abort(); + } + break; + case (EQN_TOK_SIZE): + case (EQN_TOK_GSIZE): + /* Accept two values: integral size and a single. */ + if (NULL == (start = eqn_nexttok(ep, &sz))) { + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + break; + } + size = mandoc_strntoi(start, sz, 10); + if (-1 == size) { + mandoc_msg(MANDOCERR_IT_NONUM, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + break; + } + if (EQN_TOK_GSIZE == tok) { + ep->gsize = size; + break; + } + parent = eqn_box_alloc(ep, parent); + parent->type = EQN_LISTONE; + parent->expectargs = 1; + parent->size = size; + break; + case (EQN_TOK_FROM): + case (EQN_TOK_TO): + case (EQN_TOK_SUB): + case (EQN_TOK_SUP): + /* + * We have a left-right-associative expression. + * Repivot under a positional node, open a child scope + * and keep on reading. + */ + if (parent->last == NULL) { + mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + cur = eqn_box_alloc(ep, parent); + cur->type = EQN_TEXT; + cur->text = mandoc_strdup(""); + } + /* Handle the "subsup" and "fromto" positions. */ + if (EQN_TOK_SUP == tok && parent->pos == EQNPOS_SUB) { + parent->expectargs = 3; + parent->pos = EQNPOS_SUBSUP; + break; + } + if (EQN_TOK_TO == tok && parent->pos == EQNPOS_FROM) { + parent->expectargs = 3; + parent->pos = EQNPOS_FROMTO; + break; + } + switch (tok) { + case (EQN_TOK_FROM): + pos = EQNPOS_FROM; + break; + case (EQN_TOK_TO): + pos = EQNPOS_TO; + break; + case (EQN_TOK_SUP): + pos = EQNPOS_SUP; + break; + case (EQN_TOK_SUB): + pos = EQNPOS_SUB; + break; + default: + abort(); + } + parent = eqn_box_makebinary(ep, pos, parent); + break; + case (EQN_TOK_SQRT): + while (parent->args == parent->expectargs) + parent = parent->parent; + /* + * Accept a left-right-associative set of arguments just + * like sub and sup and friends but without rebalancing + * under a pivot. + */ + parent = eqn_box_alloc(ep, parent); + parent->type = EQN_SUBEXPR; + parent->pos = EQNPOS_SQRT; + parent->expectargs = 1; + break; + case (EQN_TOK_OVER): + /* + * We have a right-left-associative fraction. + * Close out anything that's currently open, then + * rebalance and continue reading. + */ + if (parent->last == NULL) { + mandoc_msg(MANDOCERR_EQN_NOBOX, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + cur = eqn_box_alloc(ep, parent); + cur->type = EQN_TEXT; + cur->text = mandoc_strdup(""); + } + while (EQN_SUBEXPR == parent->type) + parent = parent->parent; + parent = eqn_box_makebinary(ep, EQNPOS_OVER, parent); + break; + case (EQN_TOK_RIGHT): + case (EQN_TOK_BRACE_CLOSE): + /* + * Close out the existing brace. + * FIXME: this is a shitty sentinel: we should really + * have a native EQN_BRACE type or whatnot. + */ + for (cur = parent; cur != NULL; cur = cur->parent) + if (cur->type == EQN_LIST && + (tok == EQN_TOK_BRACE_CLOSE || + cur->left != NULL)) + break; + if (cur == NULL) { + mandoc_msg(MANDOCERR_BLK_NOTOPEN, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + break; + } + parent = cur; + if (EQN_TOK_RIGHT == tok) { + if (NULL == (start = eqn_nexttok(ep, &sz))) { + mandoc_msg(MANDOCERR_REQ_EMPTY, + ep->parse, ep->eqn.ln, + ep->eqn.pos, eqn_toks[tok]); + break; + } + /* Handling depends on right/left. */ + if (STRNEQ(start, sz, "ceiling", 7)) { + strlcpy(sym, "\\[rc]", sizeof(sym)); + parent->right = mandoc_strdup(sym); + } else if (STRNEQ(start, sz, "floor", 5)) { + strlcpy(sym, "\\[rf]", sizeof(sym)); + parent->right = mandoc_strdup(sym); + } else + parent->right = mandoc_strndup(start, sz); + } + parent = parent->parent; + if (EQN_TOK_BRACE_CLOSE == tok && parent && + (parent->type == EQN_PILE || + parent->type == EQN_MATRIX)) + parent = parent->parent; + /* Close out any "singleton" lists. */ + while (parent->type == EQN_LISTONE && + parent->args == parent->expectargs) + parent = parent->parent; + break; + case (EQN_TOK_BRACE_OPEN): + case (EQN_TOK_LEFT): + /* + * If we already have something in the stack and we're + * in an expression, then rewind til we're not any more + * (just like with the text node). + */ + while (parent->args == parent->expectargs) + parent = parent->parent; + if (EQN_TOK_LEFT == tok && + (start = eqn_nexttok(ep, &sz)) == NULL) { + mandoc_msg(MANDOCERR_REQ_EMPTY, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + break; + } + parent = eqn_box_alloc(ep, parent); + parent->type = EQN_LIST; + if (EQN_TOK_LEFT == tok) { + if (STRNEQ(start, sz, "ceiling", 7)) { + strlcpy(sym, "\\[lc]", sizeof(sym)); + parent->left = mandoc_strdup(sym); + } else if (STRNEQ(start, sz, "floor", 5)) { + strlcpy(sym, "\\[lf]", sizeof(sym)); + parent->left = mandoc_strdup(sym); + } else + parent->left = mandoc_strndup(start, sz); + } + break; + case (EQN_TOK_PILE): + case (EQN_TOK_LPILE): + case (EQN_TOK_RPILE): + case (EQN_TOK_CPILE): + case (EQN_TOK_CCOL): + case (EQN_TOK_LCOL): + case (EQN_TOK_RCOL): + while (parent->args == parent->expectargs) + parent = parent->parent; + parent = eqn_box_alloc(ep, parent); + parent->type = EQN_PILE; + parent->expectargs = 1; + break; + case (EQN_TOK_ABOVE): + for (cur = parent; cur != NULL; cur = cur->parent) + if (cur->type == EQN_PILE) + break; + if (cur == NULL) { + mandoc_msg(MANDOCERR_IT_STRAY, ep->parse, + ep->eqn.ln, ep->eqn.pos, eqn_toks[tok]); + break; + } + parent = eqn_box_alloc(ep, cur); + parent->type = EQN_LIST; + break; + case (EQN_TOK_MATRIX): + while (parent->args == parent->expectargs) + parent = parent->parent; + parent = eqn_box_alloc(ep, parent); + parent->type = EQN_MATRIX; + parent->expectargs = 1; + break; + case (EQN_TOK_EOF): + /* + * End of file! + * TODO: make sure we're not in an open subexpression. + */ return(0); + default: + assert(tok == EQN_TOK__MAX); + assert(NULL != p); + /* + * If we already have something in the stack and we're + * in an expression, then rewind til we're not any more. + */ + while (parent->args == parent->expectargs) + parent = parent->parent; + cur = eqn_box_alloc(ep, parent); + cur->type = EQN_TEXT; + for (i = 0; i < EQNSYM__MAX; i++) + if (0 == strcmp(eqnsyms[i].str, p)) { + (void)snprintf(sym, sizeof(sym), + "\\[%s]", eqnsyms[i].sym); + cur->text = mandoc_strdup(sym); + free(p); + break; + } + + if (i == EQNSYM__MAX) + cur->text = p; + /* + * Post-process list status. + */ + while (parent->type == EQN_LISTONE && + parent->args == parent->expectargs) + parent = parent->parent; + break; } - ep->gsize = mandoc_strntoi(start, sz, 10); - return(1); + goto next_tok; } -static int -eqn_do_undef(struct eqn_node *ep) +enum rofferr +eqn_end(struct eqn_node **epp) { - const char *start; - struct eqn_def *def; - size_t sz; + struct eqn_node *ep; - if (NULL == (start = eqn_nextrawtok(ep, &sz))) { - EQN_MSG(MANDOCERR_EQNEOF, ep); - return(0); - } else if (NULL != (def = eqn_def_find(ep, start, sz))) - def->keysz = 0; + ep = *epp; + *epp = NULL; - return(1); + ep->eqn.root = mandoc_calloc(1, sizeof(struct eqn_box)); + ep->eqn.root->expectargs = UINT_MAX; + return(0 == eqn_parse(ep, ep->eqn.root) ? ROFF_EQN : ROFF_IGN); } -static struct eqn_def * -eqn_def_find(struct eqn_node *ep, const char *key, size_t sz) +void +eqn_free(struct eqn_node *p) { int i; - for (i = 0; i < (int)ep->defsz; i++) - if (ep->defs[i].keysz && STRNEQ(ep->defs[i].key, - ep->defs[i].keysz, key, sz)) - return(&ep->defs[i]); + eqn_box_free(p->eqn.root); - return(NULL); + for (i = 0; i < (int)p->defsz; i++) { + free(p->defs[i].key); + free(p->defs[i].val); + } + + free(p->data); + free(p->defs); + free(p); } Index: vendor/mdocml/dist/eqn_html.c =================================================================== --- vendor/mdocml/dist/eqn_html.c (revision 275396) +++ vendor/mdocml/dist/eqn_html.c (revision 275397) @@ -1,81 +1,192 @@ -/* $Id: eqn_html.c,v 1.3 2014/04/20 16:46:04 schwarze Exp $ */ +/* $Id: eqn_html.c,v 1.10 2014/10/12 19:31:41 schwarze Exp $ */ /* - * Copyright (c) 2011 Kristaps Dzonsons + * Copyright (c) 2011, 2014 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include + #include #include #include #include #include "mandoc.h" #include "out.h" #include "html.h" -static const enum htmltag fontmap[EQNFONT__MAX] = { - TAG_SPAN, /* EQNFONT_NONE */ - TAG_SPAN, /* EQNFONT_ROMAN */ - TAG_B, /* EQNFONT_BOLD */ - TAG_B, /* EQNFONT_FAT */ - TAG_I /* EQNFONT_ITALIC */ -}; +static void +eqn_box(struct html *p, const struct eqn_box *bp) +{ + struct tag *post, *row, *cell, *t; + struct htmlpair tag[2]; + const struct eqn_box *child, *parent; + size_t i, j, rows; -static void eqn_box(struct html *, const struct eqn_box *); + if (NULL == bp) + return; + post = NULL; + /* + * Special handling for a matrix, which is presented to us in + * column order, but must be printed in row-order. + */ + if (EQN_MATRIX == bp->type) { + if (NULL == bp->first) + goto out; + if (EQN_LIST != bp->first->type) { + eqn_box(p, bp->first); + goto out; + } + if (NULL == (parent = bp->first->first)) + goto out; + /* Estimate the number of rows, first. */ + if (NULL == (child = parent->first)) + goto out; + for (rows = 0; NULL != child; rows++) + child = child->next; + /* Print row-by-row. */ + post = print_otag(p, TAG_MTABLE, 0, NULL); + for (i = 0; i < rows; i++) { + parent = bp->first->first; + row = print_otag(p, TAG_MTR, 0, NULL); + while (NULL != parent) { + child = parent->first; + for (j = 0; j < i; j++) { + if (NULL == child) + break; + child = child->next; + } + cell = print_otag + (p, TAG_MTD, 0, NULL); + /* + * If we have no data for this + * particular cell, then print a + * placeholder and continue--don't puke. + */ + if (NULL != child) + eqn_box(p, child->first); + print_tagq(p, cell); + parent = parent->next; + } + print_tagq(p, row); + } + goto out; + } + + switch (bp->pos) { + case (EQNPOS_TO): + post = print_otag(p, TAG_MOVER, 0, NULL); + break; + case (EQNPOS_SUP): + post = print_otag(p, TAG_MSUP, 0, NULL); + break; + case (EQNPOS_FROM): + post = print_otag(p, TAG_MUNDER, 0, NULL); + break; + case (EQNPOS_SUB): + post = print_otag(p, TAG_MSUB, 0, NULL); + break; + case (EQNPOS_OVER): + post = print_otag(p, TAG_MFRAC, 0, NULL); + break; + case (EQNPOS_FROMTO): + post = print_otag(p, TAG_MUNDEROVER, 0, NULL); + break; + case (EQNPOS_SUBSUP): + post = print_otag(p, TAG_MSUBSUP, 0, NULL); + break; + case (EQNPOS_SQRT): + post = print_otag(p, TAG_MSQRT, 0, NULL); + break; + default: + break; + } + + if (bp->top || bp->bottom) { + assert(NULL == post); + if (bp->top && NULL == bp->bottom) + post = print_otag(p, TAG_MOVER, 0, NULL); + else if (bp->top && bp->bottom) + post = print_otag(p, TAG_MUNDEROVER, 0, NULL); + else if (bp->bottom) + post = print_otag(p, TAG_MUNDER, 0, NULL); + } + + if (EQN_PILE == bp->type) { + assert(NULL == post); + if (bp->first != NULL && bp->first->type == EQN_LIST) + post = print_otag(p, TAG_MTABLE, 0, NULL); + } else if (bp->type == EQN_LIST && + bp->parent && bp->parent->type == EQN_PILE) { + assert(NULL == post); + post = print_otag(p, TAG_MTR, 0, NULL); + print_otag(p, TAG_MTD, 0, NULL); + } + + if (NULL != bp->text) { + assert(NULL == post); + post = print_otag(p, TAG_MI, 0, NULL); + print_text(p, bp->text); + } else if (NULL == post) { + if (NULL != bp->left || NULL != bp->right) { + PAIR_INIT(&tag[0], ATTR_OPEN, + NULL == bp->left ? "" : bp->left); + PAIR_INIT(&tag[1], ATTR_CLOSE, + NULL == bp->right ? "" : bp->right); + post = print_otag(p, TAG_MFENCED, 2, tag); + } + if (NULL == post) + post = print_otag(p, TAG_MROW, 0, NULL); + else + print_otag(p, TAG_MROW, 0, NULL); + } + + eqn_box(p, bp->first); + +out: + if (NULL != bp->bottom) { + t = print_otag(p, TAG_MO, 0, NULL); + print_text(p, bp->bottom); + print_tagq(p, t); + } + if (NULL != bp->top) { + t = print_otag(p, TAG_MO, 0, NULL); + print_text(p, bp->top); + print_tagq(p, t); + } + + if (NULL != post) + print_tagq(p, post); + + eqn_box(p, bp->next); +} + void print_eqn(struct html *p, const struct eqn *ep) { struct htmlpair tag; struct tag *t; PAIR_CLASS_INIT(&tag, "eqn"); - t = print_otag(p, TAG_SPAN, 1, &tag); + t = print_otag(p, TAG_MATH, 1, &tag); p->flags |= HTML_NONOSPACE; eqn_box(p, ep->root); p->flags &= ~HTML_NONOSPACE; print_tagq(p, t); -} - -static void -eqn_box(struct html *p, const struct eqn_box *bp) -{ - struct tag *t; - - t = EQNFONT_NONE == bp->font ? NULL : - print_otag(p, fontmap[(int)bp->font], 0, NULL); - - if (bp->left) - print_text(p, bp->left); - - if (bp->text) - print_text(p, bp->text); - - if (bp->first) - eqn_box(p, bp->first); - - if (NULL != t) - print_tagq(p, t); - if (bp->right) - print_text(p, bp->right); - - if (bp->next) - eqn_box(p, bp->next); } Index: vendor/mdocml/dist/eqn_term.c =================================================================== --- vendor/mdocml/dist/eqn_term.c (revision 275396) +++ vendor/mdocml/dist/eqn_term.c (revision 275397) @@ -1,77 +1,124 @@ -/* $Id: eqn_term.c,v 1.5 2014/04/20 16:46:04 schwarze Exp $ */ +/* $Id: eqn_term.c,v 1.7 2014/10/12 14:49:39 schwarze Exp $ */ /* * Copyright (c) 2011 Kristaps Dzonsons + * Copyright (c) 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include + #include #include #include #include #include "mandoc.h" #include "out.h" #include "term.h" static const enum termfont fontmap[EQNFONT__MAX] = { TERMFONT_NONE, /* EQNFONT_NONE */ TERMFONT_NONE, /* EQNFONT_ROMAN */ TERMFONT_BOLD, /* EQNFONT_BOLD */ TERMFONT_BOLD, /* EQNFONT_FAT */ TERMFONT_UNDER /* EQNFONT_ITALIC */ }; static void eqn_box(struct termp *, const struct eqn_box *); void term_eqn(struct termp *p, const struct eqn *ep) { - p->flags |= TERMP_NONOSPACE; eqn_box(p, ep->root); - term_word(p, " "); - p->flags &= ~TERMP_NONOSPACE; + p->flags &= ~TERMP_NOSPACE; } static void eqn_box(struct termp *p, const struct eqn_box *bp) { + const struct eqn_box *child; - if (EQNFONT_NONE != bp->font) + if (bp->type == EQN_LIST || + (bp->type == EQN_PILE && (bp->prev || bp->next)) || + (bp->parent != NULL && bp->parent->pos == EQNPOS_SQRT)) { + if (bp->parent->type == EQN_SUBEXPR && bp->prev != NULL) + p->flags |= TERMP_NOSPACE; + term_word(p, bp->left != NULL ? bp->left : "("); + p->flags |= TERMP_NOSPACE; + } + if (bp->font != EQNFONT_NONE) term_fontpush(p, fontmap[(int)bp->font]); - if (bp->left) - term_word(p, bp->left); - if (EQN_SUBEXPR == bp->type) - term_word(p, "("); - if (bp->text) + if (bp->text != NULL) term_word(p, bp->text); - if (bp->first) + if (bp->pos == EQNPOS_SQRT) { + term_word(p, "sqrt"); + p->flags |= TERMP_NOSPACE; eqn_box(p, bp->first); + } else if (bp->type == EQN_SUBEXPR) { + child = bp->first; + eqn_box(p, child); + p->flags |= TERMP_NOSPACE; + term_word(p, bp->pos == EQNPOS_OVER ? "/" : + (bp->pos == EQNPOS_SUP || + bp->pos == EQNPOS_TO) ? "^" : "_"); + p->flags |= TERMP_NOSPACE; + child = child->next; + eqn_box(p, child); + if (bp->pos == EQNPOS_FROMTO || + bp->pos == EQNPOS_SUBSUP) { + p->flags |= TERMP_NOSPACE; + term_word(p, "^"); + p->flags |= TERMP_NOSPACE; + child = child->next; + eqn_box(p, child); + } + } else { + child = bp->first; + if (bp->type == EQN_MATRIX && child->type == EQN_LIST) + child = child->first; + while (child != NULL) { + eqn_box(p, + bp->type == EQN_PILE && + child->type == EQN_LIST && + child->args == 1 ? + child->first : child); + child = child->next; + } + } - if (EQN_SUBEXPR == bp->type) - term_word(p, ")"); - if (bp->right) - term_word(p, bp->right); - if (EQNFONT_NONE != bp->font) + if (bp->font != EQNFONT_NONE) term_fontpop(p); + if (bp->type == EQN_LIST || + (bp->type == EQN_PILE && (bp->prev || bp->next)) || + (bp->parent != NULL && bp->parent->pos == EQNPOS_SQRT)) { + p->flags |= TERMP_NOSPACE; + term_word(p, bp->right != NULL ? bp->right : ")"); + if (bp->parent->type == EQN_SUBEXPR && bp->next != NULL) + p->flags |= TERMP_NOSPACE; + } - if (bp->next) - eqn_box(p, bp->next); + if (bp->top != NULL) { + p->flags |= TERMP_NOSPACE; + term_word(p, bp->top); + } + if (bp->bottom != NULL) { + p->flags |= TERMP_NOSPACE; + term_word(p, "_"); + } } Index: vendor/mdocml/dist/example.style.css =================================================================== --- vendor/mdocml/dist/example.style.css (revision 275396) +++ vendor/mdocml/dist/example.style.css (revision 275397) @@ -1,110 +1,111 @@ -/* $Id: example.style.css,v 1.49 2011/12/15 12:18:57 kristaps Exp $ */ +/* $Id: example.style.css,v 1.53 2014/09/27 11:16:24 kristaps Exp $ */ /* * This is an example style-sheet provided for mandoc(1) and the -Thtml * or -Txhtml output mode. * It mimics the appearance of the legacy man.cgi output. * See mdoc(7) and man(7) for macro explanations. */ div.mandoc { min-width: 102ex; width: 102ex; font-family: monospace; } /* This is the outer node of all mandoc -T[x]html documents. */ div.mandoc h1 { margin-bottom: 0ex; font-size: inherit; margin-left: -4ex; } /* Section header (Sh, SH). */ div.mandoc h2 { margin-bottom: 0ex; font-size: inherit; margin-left: -2ex; } /* Sub-section header (Ss, SS). */ div.mandoc table { width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */ div.mandoc td { vertical-align: top; } /* All table cells. */ div.mandoc p { } /* Paragraph: Pp, Lp. */ div.mandoc blockquote { margin-left: 5ex; margin-top: 0ex; margin-bottom: 0ex; } /* D1, Dl. */ div.mandoc div.section { margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */ div.mandoc div.subsection { } /* Sub-sections (Ss, SS). */ div.mandoc table.synopsis { } /* SYNOPSIS section table. */ div.mandoc table.foot { } /* Document footer. */ div.mandoc td.foot-date { width: 50%; } /* Document footer: date. */ -div.mandoc td.foot-os { width: 50%; text-align: right; } /* Document footer: OS/source. */ +div.mandoc td.foot-os { width: 50%; } /* Document footer: OS/source. */ div.mandoc table.head { } /* Document header. */ div.mandoc td.head-ltitle { width: 10%; } /* Document header: left-title. */ -div.mandoc td.head-vol { width: 80%; text-align: center; } /* Document header: volume. */ -div.mandoc td.head-rtitle { width: 10%; text-align: right; } /* Document header: right-title. */ +div.mandoc td.head-vol { width: 80%; } /* Document header: volume. */ +div.mandoc td.head-rtitle { width: 10%; } /* Document header: right-title. */ div.mandoc .display { } /* All Bd, D1, Dl. */ div.mandoc .list { } /* All Bl. */ div.mandoc i { } /* Italic: BI, IB, I, (implicit). */ div.mandoc b { } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */ div.mandoc small { } /* Small: SB, SM. */ div.mandoc .emph { font-style: italic; font-weight: normal; } /* Emphasis: Em, Bl -emphasis. */ div.mandoc .symb { font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */ div.mandoc .lit { font-style: normal; font-weight: normal; font-family: monospace; } /* Literal: Dl, Li, Ql, Bf -literal, Bl -literal, Bl -unfilled. */ div.mandoc i.addr { font-weight: normal; } /* Address (Ad). */ div.mandoc i.arg { font-weight: normal; } /* Command argument (Ar). */ div.mandoc span.author { } /* Author name (An). */ div.mandoc b.cmd { font-style: normal; } /* Command (Cm). */ div.mandoc b.config { font-style: normal; } /* Config statement (Cd). */ div.mandoc span.define { } /* Defines (Dv). */ div.mandoc span.desc { } /* Nd. After em-dash. */ div.mandoc b.diag { font-style: normal; } /* Diagnostic (Bl -diag). */ div.mandoc span.env { } /* Environment variables (Ev). */ div.mandoc span.errno { } /* Error string (Er). */ div.mandoc i.farg { font-weight: normal; } /* Function argument (Fa, Fn). */ div.mandoc i.file { font-weight: normal; } /* File (Pa). */ div.mandoc b.flag { font-style: normal; } /* Flag (Fl, Cm). */ div.mandoc b.fname { font-style: normal; } /* Function name (Fa, Fn, Rv). */ div.mandoc i.ftype { font-weight: normal; } /* Function types (Ft, Fn). */ div.mandoc b.includes { font-style: normal; } /* Header includes (In). */ div.mandoc span.lib { } /* Library (Lb). */ div.mandoc i.link-sec { font-weight: normal; } /* Section links (Sx). */ div.mandoc b.macro { font-style: normal; } /* Macro-ish thing (Fd). */ div.mandoc b.name { font-style: normal; } /* Name of utility (Nm). */ div.mandoc span.opt { } /* Options (Op, Oo/Oc). */ div.mandoc span.ref { } /* Citations (Rs). */ div.mandoc span.ref-auth { } /* Reference author (%A). */ div.mandoc i.ref-book { font-weight: normal; } /* Reference book (%B). */ div.mandoc span.ref-city { } /* Reference city (%C). */ div.mandoc span.ref-date { } /* Reference date (%D). */ div.mandoc i.ref-issue { font-weight: normal; } /* Reference issuer/publisher (%I). */ div.mandoc i.ref-jrnl { font-weight: normal; } /* Reference journal (%J). */ div.mandoc span.ref-num { } /* Reference number (%N). */ div.mandoc span.ref-opt { } /* Reference optionals (%O). */ div.mandoc span.ref-page { } /* Reference page (%P). */ div.mandoc span.ref-corp { } /* Reference corporate/foreign author (%Q). */ div.mandoc span.ref-rep { } /* Reference report (%R). */ div.mandoc span.ref-title { text-decoration: underline; } /* Reference title (%T). */ div.mandoc span.ref-vol { } /* Reference volume (%V). */ div.mandoc span.type { font-style: italic; font-weight: normal; } /* Variable types (Vt). */ div.mandoc span.unix { } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */ div.mandoc b.utility { font-style: normal; } /* Name of utility (Ex). */ div.mandoc b.var { font-style: normal; } /* Variables (Rv). */ div.mandoc a.link-ext { } /* Off-site link (Lk). */ div.mandoc a.link-includes { } /* Include-file link (In). */ div.mandoc a.link-mail { } /* Mailto links (Mt). */ div.mandoc a.link-man { } /* Manual links (Xr). */ div.mandoc a.link-ref { } /* Reference section links (%Q). */ div.mandoc a.link-sec { } /* Section links (Sx). */ div.mandoc dl.list-diag { } /* Formatting for lists. See mdoc(7). */ div.mandoc dt.list-diag { } div.mandoc dd.list-diag { } div.mandoc dl.list-hang { } div.mandoc dt.list-hang { } div.mandoc dd.list-hang { } div.mandoc dl.list-inset { } div.mandoc dt.list-inset { } div.mandoc dd.list-inset { } div.mandoc dl.list-ohang { } div.mandoc dt.list-ohang { } div.mandoc dd.list-ohang { margin-left: 0ex; } div.mandoc dl.list-tag { } div.mandoc dt.list-tag { } div.mandoc dd.list-tag { } div.mandoc table.list-col { } div.mandoc tr.list-col { } div.mandoc td.list-col { } div.mandoc ul.list-bul { list-style-type: disc; padding-left: 1em; } div.mandoc li.list-bul { } div.mandoc ul.list-dash { list-style-type: none; padding-left: 0em; } div.mandoc li.list-dash:before { content: "\2014 "; } div.mandoc ul.list-hyph { list-style-type: none; padding-left: 0em; } div.mandoc li.list-hyph:before { content: "\2013 "; } div.mandoc ul.list-item { list-style-type: none; padding-left: 0em; } div.mandoc li.list-item { } div.mandoc ol.list-enum { padding-left: 2em; } div.mandoc li.list-enum { } div.mandoc span.eqn { } /* Equation modes. See eqn(7). */ div.mandoc table.tbl { } /* Table modes. See tbl(7). */ +div.mandoc div.spacer { margin: 1em 0; } Index: vendor/mdocml/dist/html.c =================================================================== --- vendor/mdocml/dist/html.c (revision 275396) +++ vendor/mdocml/dist/html.c (revision 275397) @@ -1,776 +1,759 @@ -/* $Id: html.c,v 1.159 2014/07/23 15:00:08 schwarze Exp $ */ +/* $Id: html.c,v 1.181 2014/10/29 00:17:43 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons + * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons * Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include #include #include #include #include #include #include #include #include #include "mandoc.h" #include "mandoc_aux.h" #include "libmandoc.h" #include "out.h" #include "html.h" #include "main.h" struct htmldata { const char *name; int flags; #define HTML_CLRLINE (1 << 0) #define HTML_NOSTACK (1 << 1) #define HTML_AUTOCLOSE (1 << 2) /* Tag has auto-closure. */ }; static const struct htmldata htmltags[TAG_MAX] = { {"html", HTML_CLRLINE}, /* TAG_HTML */ {"head", HTML_CLRLINE}, /* TAG_HEAD */ {"body", HTML_CLRLINE}, /* TAG_BODY */ {"meta", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_META */ {"title", HTML_CLRLINE}, /* TAG_TITLE */ {"div", HTML_CLRLINE}, /* TAG_DIV */ {"h1", 0}, /* TAG_H1 */ {"h2", 0}, /* TAG_H2 */ {"span", 0}, /* TAG_SPAN */ {"link", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_LINK */ {"br", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_BR */ {"a", 0}, /* TAG_A */ {"table", HTML_CLRLINE}, /* TAG_TABLE */ {"tbody", HTML_CLRLINE}, /* TAG_TBODY */ {"col", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_COL */ {"tr", HTML_CLRLINE}, /* TAG_TR */ {"td", HTML_CLRLINE}, /* TAG_TD */ {"li", HTML_CLRLINE}, /* TAG_LI */ {"ul", HTML_CLRLINE}, /* TAG_UL */ {"ol", HTML_CLRLINE}, /* TAG_OL */ {"dl", HTML_CLRLINE}, /* TAG_DL */ {"dt", HTML_CLRLINE}, /* TAG_DT */ {"dd", HTML_CLRLINE}, /* TAG_DD */ {"blockquote", HTML_CLRLINE}, /* TAG_BLOCKQUOTE */ - {"p", HTML_CLRLINE | HTML_NOSTACK | HTML_AUTOCLOSE}, /* TAG_P */ {"pre", HTML_CLRLINE }, /* TAG_PRE */ {"b", 0 }, /* TAG_B */ {"i", 0 }, /* TAG_I */ {"code", 0 }, /* TAG_CODE */ {"small", 0 }, /* TAG_SMALL */ + {"style", HTML_CLRLINE}, /* TAG_STYLE */ + {"math", HTML_CLRLINE}, /* TAG_MATH */ + {"mrow", 0}, /* TAG_MROW */ + {"mi", 0}, /* TAG_MI */ + {"mo", 0}, /* TAG_MO */ + {"msup", 0}, /* TAG_MSUP */ + {"msub", 0}, /* TAG_MSUB */ + {"msubsup", 0}, /* TAG_MSUBSUP */ + {"mfrac", 0}, /* TAG_MFRAC */ + {"msqrt", 0}, /* TAG_MSQRT */ + {"mfenced", 0}, /* TAG_MFENCED */ + {"mtable", 0}, /* TAG_MTABLE */ + {"mtr", 0}, /* TAG_MTR */ + {"mtd", 0}, /* TAG_MTD */ + {"munderover", 0}, /* TAG_MUNDEROVER */ + {"munder", 0}, /* TAG_MUNDER*/ + {"mover", 0}, /* TAG_MOVER*/ }; static const char *const htmlattrs[ATTR_MAX] = { - "http-equiv", /* ATTR_HTTPEQUIV */ - "content", /* ATTR_CONTENT */ "name", /* ATTR_NAME */ "rel", /* ATTR_REL */ "href", /* ATTR_HREF */ "type", /* ATTR_TYPE */ "media", /* ATTR_MEDIA */ "class", /* ATTR_CLASS */ "style", /* ATTR_STYLE */ - "width", /* ATTR_WIDTH */ "id", /* ATTR_ID */ - "summary", /* ATTR_SUMMARY */ - "align", /* ATTR_ALIGN */ "colspan", /* ATTR_COLSPAN */ + "charset", /* ATTR_CHARSET */ + "open", /* ATTR_OPEN */ + "close", /* ATTR_CLOSE */ + "mathvariant", /* ATTR_MATHVARIANT */ }; static const char *const roffscales[SCALE_MAX] = { "cm", /* SCALE_CM */ "in", /* SCALE_IN */ "pc", /* SCALE_PC */ "pt", /* SCALE_PT */ "em", /* SCALE_EM */ "em", /* SCALE_MM */ "ex", /* SCALE_EN */ "ex", /* SCALE_BU */ "em", /* SCALE_VS */ "ex", /* SCALE_FS */ }; static void bufncat(struct html *, const char *, size_t); static void print_ctag(struct html *, enum htmltag); static int print_escape(char); static int print_encode(struct html *, const char *, int); static void print_metaf(struct html *, enum mandoc_esc); static void print_attr(struct html *, const char *, const char *); -static void *ml_alloc(char *, enum htmltype); -static void * -ml_alloc(char *outopts, enum htmltype type) +void * +html_alloc(const struct mchars *mchars, char *outopts) { struct html *h; const char *toks[5]; char *v; toks[0] = "style"; toks[1] = "man"; toks[2] = "includes"; toks[3] = "fragment"; toks[4] = NULL; h = mandoc_calloc(1, sizeof(struct html)); - h->type = type; h->tags.head = NULL; - h->symtab = mchars_alloc(); + h->symtab = mchars; while (outopts && *outopts) switch (getsubopt(&outopts, UNCONST(toks), &v)) { case 0: h->style = v; break; case 1: h->base_man = v; break; case 2: h->base_includes = v; break; case 3: h->oflags |= HTML_FRAGMENT; break; default: break; } return(h); } -void * -html_alloc(char *outopts) -{ - - return(ml_alloc(outopts, HTML_HTML_4_01_STRICT)); -} - -void * -xhtml_alloc(char *outopts) -{ - - return(ml_alloc(outopts, HTML_XHTML_1_0_STRICT)); -} - void html_free(void *p) { struct tag *tag; struct html *h; h = (struct html *)p; while ((tag = h->tags.head) != NULL) { h->tags.head = tag->next; free(tag); } - if (h->symtab) - mchars_free(h->symtab); - free(h); } void print_gen_head(struct html *h) { struct htmlpair tag[4]; + struct tag *t; - tag[0].key = ATTR_HTTPEQUIV; - tag[0].val = "Content-Type"; - tag[1].key = ATTR_CONTENT; - tag[1].val = "text/html; charset=utf-8"; - print_otag(h, TAG_META, 2, tag); + tag[0].key = ATTR_CHARSET; + tag[0].val = "utf-8"; + print_otag(h, TAG_META, 1, tag); - tag[0].key = ATTR_NAME; - tag[0].val = "resource-type"; - tag[1].key = ATTR_CONTENT; - tag[1].val = "document"; - print_otag(h, TAG_META, 2, tag); + /* + * Print a default style-sheet. + */ + t = print_otag(h, TAG_STYLE, 0, NULL); + print_text(h, "table.head, table.foot { width: 100%; }\n" + "td.head-rtitle, td.foot-os { text-align: right; }\n" + "td.head-vol { text-align: center; }\n" + "table.foot td { width: 50%; }\n" + "table.head td { width: 33%; }\n" + "div.spacer { margin: 1em 0; }\n"); + print_tagq(h, t); if (h->style) { tag[0].key = ATTR_REL; tag[0].val = "stylesheet"; tag[1].key = ATTR_HREF; tag[1].val = h->style; tag[2].key = ATTR_TYPE; tag[2].val = "text/css"; tag[3].key = ATTR_MEDIA; tag[3].val = "all"; print_otag(h, TAG_LINK, 4, tag); } } static void print_metaf(struct html *h, enum mandoc_esc deco) { enum htmlfont font; switch (deco) { case ESCAPE_FONTPREV: font = h->metal; break; case ESCAPE_FONTITALIC: font = HTMLFONT_ITALIC; break; case ESCAPE_FONTBOLD: font = HTMLFONT_BOLD; break; case ESCAPE_FONTBI: font = HTMLFONT_BI; break; case ESCAPE_FONT: /* FALLTHROUGH */ case ESCAPE_FONTROMAN: font = HTMLFONT_NONE; break; default: abort(); /* NOTREACHED */ } if (h->metaf) { print_tagq(h, h->metaf); h->metaf = NULL; } h->metal = h->metac; h->metac = font; 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) { size_t rsz; int skip, sz; /* * Account for escaped sequences within string length * calculations. This follows the logic in term_strlen() as we * must calculate the width of produced strings. * Assume that characters are always width of "1". This is * hacky, but it gets the job done for approximation of widths. */ sz = 0; 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: /* FALLTHROUGH */ case ESCAPE_NUMBERED: /* FALLTHROUGH */ case ESCAPE_SPECIAL: if (skip) skip = 0; else sz++; break; case ESCAPE_SKIPCHAR: skip = 1; break; default: break; } } return(sz); } static int print_escape(char c) { switch (c) { case '<': printf("<"); break; case '>': printf(">"); break; case '&': printf("&"); break; case '"': printf("""); break; case ASCII_NBRSP: putchar('-'); break; case ASCII_HYPH: putchar('-'); /* FALLTHROUGH */ case ASCII_BREAK: break; default: return(0); } return(1); } static int print_encode(struct html *h, const char *p, int norecurse) { size_t sz; int c, len, nospace; const char *seq; enum mandoc_esc esc; static const char rejs[9] = { '\\', '<', '>', '&', '"', ASCII_NBRSP, ASCII_HYPH, ASCII_BREAK, '\0' }; 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); p += (int)sz; if ('\0' == *p) break; if (print_escape(*p++)) continue; esc = mandoc_escape(&p, &seq, &len); if (ESCAPE_ERROR == esc) break; switch (esc) { case ESCAPE_FONT: /* FALLTHROUGH */ case ESCAPE_FONTPREV: /* FALLTHROUGH */ case ESCAPE_FONTBOLD: /* FALLTHROUGH */ case ESCAPE_FONTITALIC: /* FALLTHROUGH */ case ESCAPE_FONTBI: /* FALLTHROUGH */ case ESCAPE_FONTROMAN: if (0 == norecurse) print_metaf(h, esc); continue; case ESCAPE_SKIPCHAR: h->flags |= HTML_SKIPCHAR; continue; default: break; } if (h->flags & HTML_SKIPCHAR) { h->flags &= ~HTML_SKIPCHAR; continue; } switch (esc) { case ESCAPE_UNICODE: /* Skip past "u" header. */ c = mchars_num2uc(seq + 1, len - 1); - if ('\0' != c) - printf("&#x%x;", c); break; case ESCAPE_NUMBERED: c = mchars_num2char(seq, len); - if ( ! ('\0' == c || print_escape(c))) - putchar(c); + if (c < 0) + continue; break; case ESCAPE_SPECIAL: c = mchars_spec2cp(h->symtab, seq, len); - if (c > 0) - printf("&#%d;", c); - else if (-1 == c && 1 == len && - !print_escape(*seq)) - putchar((int)*seq); + if (c <= 0) + continue; break; case ESCAPE_NOSPACE: if ('\0' == *p) nospace = 1; - break; + continue; default: - break; + continue; } + if ((c < 0x20 && c != 0x09) || + (c > 0x7E && c < 0xA0)) + c = 0xFFFD; + if (c > 0x7E) + printf("&#%d;", c); + else if ( ! print_escape(c)) + putchar(c); } return(nospace); } static void print_attr(struct html *h, const char *key, const char *val) { printf(" %s=\"", key); (void)print_encode(h, val, 1); putchar('\"'); } struct tag * print_otag(struct html *h, enum htmltag tag, int sz, const struct htmlpair *p) { int i; struct tag *t; /* Push this tags onto the stack of open scopes. */ if ( ! (HTML_NOSTACK & htmltags[tag].flags)) { t = mandoc_malloc(sizeof(struct tag)); t->tag = tag; t->next = h->tags.head; h->tags.head = t; } else t = NULL; if ( ! (HTML_NOSPACE & h->flags)) if ( ! (HTML_CLRLINE & htmltags[tag].flags)) { /* Manage keeps! */ if ( ! (HTML_KEEP & h->flags)) { if (HTML_PREKEEP & h->flags) h->flags |= HTML_KEEP; putchar(' '); } else printf(" "); } if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; else h->flags |= HTML_NOSPACE; /* Print out the tag name and attributes. */ printf("<%s", htmltags[tag].name); for (i = 0; i < sz; i++) print_attr(h, htmlattrs[p[i].key], p[i].val); - /* Add non-overridable attributes. */ + /* Accommodate for "well-formed" singleton escaping. */ - if (TAG_HTML == tag && HTML_XHTML_1_0_STRICT == h->type) { - print_attr(h, "xmlns", "http://www.w3.org/1999/xhtml"); - print_attr(h, "xml:lang", "en"); - print_attr(h, "lang", "en"); - } - - /* Accommodate for XML "well-formed" singleton escaping. */ - if (HTML_AUTOCLOSE & htmltags[tag].flags) - switch (h->type) { - case HTML_XHTML_1_0_STRICT: - putchar('/'); - break; - default: - break; - } + putchar('/'); putchar('>'); h->flags |= HTML_NOSPACE; if ((HTML_AUTOCLOSE | HTML_CLRLINE) & htmltags[tag].flags) putchar('\n'); return(t); } static void print_ctag(struct html *h, enum htmltag tag) { printf("", htmltags[tag].name); if (HTML_CLRLINE & htmltags[tag].flags) { h->flags |= HTML_NOSPACE; putchar('\n'); } } void print_gen_decls(struct html *h) { - const char *doctype; - const char *dtd; - const char *name; - switch (h->type) { - case HTML_HTML_4_01_STRICT: - name = "HTML"; - doctype = "-//W3C//DTD HTML 4.01//EN"; - dtd = "http://www.w3.org/TR/html4/strict.dtd"; - break; - default: - puts(""); - name = "html"; - doctype = "-//W3C//DTD XHTML 1.0 Strict//EN"; - dtd = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"; - break; - } - - printf("\n", - name, doctype, dtd); + puts(""); } void print_text(struct html *h, const char *word) { if ( ! (HTML_NOSPACE & h->flags)) { /* Manage keeps! */ if ( ! (HTML_KEEP & h->flags)) { if (HTML_PREKEEP & h->flags) h->flags |= HTML_KEEP; putchar(' '); } else printf(" "); } assert(NULL == h->metaf); 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)) { if ( ! (h->flags & HTML_NONOSPACE)) h->flags &= ~HTML_NOSPACE; } else h->flags |= HTML_NOSPACE; if (h->metaf) { print_tagq(h, h->metaf); h->metaf = NULL; } h->flags &= ~HTML_IGNDELIM; } void print_tagq(struct html *h, const struct tag *until) { struct tag *tag; while ((tag = h->tags.head) != NULL) { /* * Remember to close out and nullify the current * meta-font and table, if applicable. */ if (tag == h->metaf) h->metaf = NULL; if (tag == h->tblt) h->tblt = NULL; print_ctag(h, tag->tag); h->tags.head = tag->next; free(tag); if (until && tag == until) return; } } void print_stagq(struct html *h, const struct tag *suntil) { struct tag *tag; while ((tag = h->tags.head) != NULL) { if (suntil && tag == suntil) return; /* * Remember to close out and nullify the current * meta-font and table, if applicable. */ if (tag == h->metaf) h->metaf = NULL; if (tag == h->tblt) h->tblt = NULL; print_ctag(h, tag->tag); h->tags.head = tag->next; free(tag); } } void +print_paragraph(struct html *h) +{ + struct tag *t; + struct htmlpair tag; + + PAIR_CLASS_INIT(&tag, "spacer"); + t = print_otag(h, TAG_DIV, 1, &tag); + print_tagq(h, t); +} + + +void bufinit(struct html *h) { h->buf[0] = '\0'; h->buflen = 0; } void bufcat_style(struct html *h, const char *key, const char *val) { bufcat(h, key); bufcat(h, ":"); bufcat(h, val); bufcat(h, ";"); } void bufcat(struct html *h, const char *p) { /* * XXX This is broken and not easy to fix. * When using the -Oincludes option, buffmt_includes() * may pass in strings overrunning BUFSIZ, causing a crash. */ h->buflen = strlcat(h->buf, p, BUFSIZ); assert(h->buflen < BUFSIZ); } void bufcat_fmt(struct html *h, const char *fmt, ...) { va_list ap; va_start(ap, fmt); (void)vsnprintf(h->buf + (int)h->buflen, BUFSIZ - h->buflen - 1, fmt, ap); va_end(ap); h->buflen = strlen(h->buf); } static void bufncat(struct html *h, const char *p, size_t sz) { assert(h->buflen + sz + 1 < BUFSIZ); strncat(h->buf, p, sz); h->buflen += sz; } void buffmt_includes(struct html *h, const char *name) { const char *p, *pp; pp = h->base_includes; bufinit(h); while (NULL != (p = strchr(pp, '%'))) { bufncat(h, pp, (size_t)(p - pp)); switch (*(p + 1)) { case'I': bufcat(h, name); break; default: bufncat(h, p, 2); break; } pp = p + 2; } if (pp) bufcat(h, pp); } void buffmt_man(struct html *h, const char *name, const char *sec) { const char *p, *pp; pp = h->base_man; bufinit(h); while (NULL != (p = strchr(pp, '%'))) { bufncat(h, pp, (size_t)(p - pp)); switch (*(p + 1)) { case 'S': bufcat(h, sec ? sec : "1"); break; case 'N': bufcat_fmt(h, "%s", name); break; default: bufncat(h, p, 2); break; } pp = p + 2; } if (pp) bufcat(h, pp); } void bufcat_su(struct html *h, const char *p, const struct roffsu *su) { double v; v = su->scale; if (SCALE_MM == su->unit && 0.0 == (v /= 100.0)) v = 1.0; + else if (SCALE_BU == su->unit) + v /= 24.0; bufcat_fmt(h, "%s: %.2f%s;", p, v, roffscales[su->unit]); } void bufcat_id(struct html *h, const char *src) { /* Cf. . */ while ('\0' != *src) bufcat_fmt(h, "%.2x", *src++); } Index: vendor/mdocml/dist/html.h =================================================================== --- vendor/mdocml/dist/html.h (revision 275396) +++ vendor/mdocml/dist/html.h (revision 275397) @@ -1,169 +1,180 @@ -/* $Id: html.h,v 1.51 2014/04/20 16:46:04 schwarze Exp $ */ +/* $Id: html.h,v 1.67 2014/10/28 17:36:19 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons + * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef HTML_H #define HTML_H __BEGIN_DECLS enum htmltag { TAG_HTML, TAG_HEAD, TAG_BODY, TAG_META, TAG_TITLE, TAG_DIV, TAG_H1, TAG_H2, TAG_SPAN, TAG_LINK, TAG_BR, TAG_A, TAG_TABLE, TAG_TBODY, TAG_COL, TAG_TR, TAG_TD, TAG_LI, TAG_UL, TAG_OL, TAG_DL, TAG_DT, TAG_DD, TAG_BLOCKQUOTE, - TAG_P, TAG_PRE, TAG_B, TAG_I, TAG_CODE, TAG_SMALL, + TAG_STYLE, + TAG_MATH, + TAG_MROW, + TAG_MI, + TAG_MO, + TAG_MSUP, + TAG_MSUB, + TAG_MSUBSUP, + TAG_MFRAC, + TAG_MSQRT, + TAG_MFENCED, + TAG_MTABLE, + TAG_MTR, + TAG_MTD, + TAG_MUNDEROVER, + TAG_MUNDER, + TAG_MOVER, TAG_MAX }; enum htmlattr { - ATTR_HTTPEQUIV, - ATTR_CONTENT, ATTR_NAME, ATTR_REL, ATTR_HREF, ATTR_TYPE, ATTR_MEDIA, ATTR_CLASS, ATTR_STYLE, - ATTR_WIDTH, ATTR_ID, - ATTR_SUMMARY, - ATTR_ALIGN, ATTR_COLSPAN, + ATTR_CHARSET, + ATTR_OPEN, + ATTR_CLOSE, + ATTR_MATHVARIANT, ATTR_MAX }; enum htmlfont { HTMLFONT_NONE = 0, HTMLFONT_BOLD, HTMLFONT_ITALIC, HTMLFONT_BI, HTMLFONT_MAX }; struct tag { struct tag *next; enum htmltag tag; }; struct tagq { struct tag *head; }; struct htmlpair { enum htmlattr key; const char *val; }; #define PAIR_INIT(p, t, v) \ do { \ (p)->key = (t); \ (p)->val = (v); \ } while (/* CONSTCOND */ 0) #define PAIR_ID_INIT(p, v) PAIR_INIT(p, ATTR_ID, v) #define PAIR_CLASS_INIT(p, v) PAIR_INIT(p, ATTR_CLASS, v) #define PAIR_HREF_INIT(p, v) PAIR_INIT(p, ATTR_HREF, v) #define PAIR_STYLE_INIT(p, h) PAIR_INIT(p, ATTR_STYLE, (h)->buf) -#define PAIR_SUMMARY_INIT(p, v) PAIR_INIT(p, ATTR_SUMMARY, v) -enum htmltype { - HTML_HTML_4_01_STRICT, - HTML_XHTML_1_0_STRICT -}; - struct html { int flags; #define HTML_NOSPACE (1 << 0) /* suppress next space */ #define HTML_IGNDELIM (1 << 1) #define HTML_KEEP (1 << 2) #define HTML_PREKEEP (1 << 3) #define HTML_NONOSPACE (1 << 4) /* never add spaces */ #define HTML_LITERAL (1 << 5) /* literal (e.g.,
) context */
 #define	HTML_SKIPCHAR	 (1 << 6) /* skip the next character */
+#define	HTML_NOSPLIT	 (1 << 7) /* do not break line before .An */
+#define	HTML_SPLIT	 (1 << 8) /* break line before .An */
 	struct tagq	  tags; /* stack of open tags */
 	struct rofftbl	  tbl; /* current table */
 	struct tag	 *tblt; /* current open table scope */
-	struct mchars	 *symtab; /* character-escapes */
+	const struct mchars *symtab; /* character table */
 	char		 *base_man; /* base for manpage href */
 	char		 *base_includes; /* base for include href */
 	char		 *style; /* style-sheet URI */
 	char		  buf[BUFSIZ]; /* see bufcat and friends */
 	size_t		  buflen;
 	struct tag	 *metaf; /* current open font scope */
 	enum htmlfont	  metal; /* last used font */
 	enum htmlfont	  metac; /* current font mode */
-	enum htmltype	  type; /* output media type */
 	int		  oflags; /* output options */
 #define	HTML_FRAGMENT	 (1 << 0) /* don't emit HTML/HEAD/BODY */
 };
 
 void		  print_gen_decls(struct html *);
 void		  print_gen_head(struct html *);
 struct tag	 *print_otag(struct html *, enum htmltag,
 				int, const struct htmlpair *);
 void		  print_tagq(struct html *, const struct tag *);
 void		  print_stagq(struct html *, const struct tag *);
 void		  print_text(struct html *, const char *);
 void		  print_tblclose(struct html *);
 void		  print_tbl(struct html *, const struct tbl_span *);
 void		  print_eqn(struct html *, const struct eqn *);
+void		  print_paragraph(struct html *);
 
 #if __GNUC__ - 0 >= 4
 __attribute__((__format__ (__printf__, 2, 3)))
 #endif
 void		  bufcat_fmt(struct html *, const char *, ...);
 void		  bufcat(struct html *, const char *);
 void		  bufcat_id(struct html *, const char *);
 void		  bufcat_style(struct html *,
 			const char *, const char *);
 void		  bufcat_su(struct html *, const char *,
 			const struct roffsu *);
 void		  bufinit(struct html *);
 void		  buffmt_man(struct html *,
 			const char *, const char *);
 void		  buffmt_includes(struct html *, const char *);
 
 int		  html_strlen(const char *);
 
 __END_DECLS
 
 #endif /*!HTML_H*/
Index: vendor/mdocml/dist/lib.c
===================================================================
--- vendor/mdocml/dist/lib.c	(revision 275396)
+++ vendor/mdocml/dist/lib.c	(revision 275397)
@@ -1,36 +1,36 @@
-/*	$Id: lib.c,v 1.10 2014/03/23 11:25:26 schwarze Exp $ */
+/*	$Id: lib.c,v 1.11 2014/08/10 23:54:41 schwarze Exp $ */
 /*
  * Copyright (c) 2009 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
+
+#include 
 
 #include 
 
 #include "mdoc.h"
 #include "libmdoc.h"
 
 #define LINE(x, y) \
 	if (0 == strcmp(p, x)) return(y);
 
 const char *
 mdoc_a2lib(const char *p)
 {
 
 #include "lib.in"
 
 	return(NULL);
 }
Index: vendor/mdocml/dist/libman.h
===================================================================
--- vendor/mdocml/dist/libman.h	(revision 275396)
+++ vendor/mdocml/dist/libman.h	(revision 275397)
@@ -1,77 +1,79 @@
-/*	$Id: libman.h,v 1.63 2014/08/01 21:24:17 schwarze Exp $ */
+/*	$Id: libman.h,v 1.65 2014/11/28 05:51:32 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef LIBMAN_H
 #define LIBMAN_H
 
 enum	man_next {
 	MAN_NEXT_SIBLING = 0,
 	MAN_NEXT_CHILD
 };
 
 struct	man {
 	struct mparse	*parse; /* parse pointer */
 	int		 quick; /* abort parse early */
 	int		 flags; /* parse flags */
 #define	MAN_ELINE	(1 << 1) /* Next-line element scope. */
 #define	MAN_BLINE	(1 << 2) /* Next-line block scope. */
 #define	MAN_LITERAL	(1 << 4) /* Literal input. */
 #define	MAN_NEWLINE	(1 << 6) /* first macro/text in a line */
 	enum man_next	 next; /* where to put the next node */
 	struct man_node	*last; /* the last parsed node */
 	struct man_node	*first; /* the first parsed node */
 	struct man_meta	 meta; /* document meta-data */
 	struct roff	*roff;
 };
 
 #define	MACRO_PROT_ARGS	  struct man *man, \
 			  enum mant tok, \
 			  int line, \
 			  int ppos, \
 			  int *pos, \
 			  char *buf
 
 struct	man_macro {
-	int		(*fp)(MACRO_PROT_ARGS);
+	void		(*fp)(MACRO_PROT_ARGS);
 	int		  flags;
 #define	MAN_SCOPED	 (1 << 0)
 #define	MAN_EXPLICIT	 (1 << 1)	/* See blk_imp(). */
 #define	MAN_FSCOPED	 (1 << 2)	/* See blk_imp(). */
 #define	MAN_NSCOPED	 (1 << 3)	/* See in_line_eoln(). */
 #define	MAN_NOCLOSE	 (1 << 4)	/* See blk_exp(). */
 #define	MAN_BSCOPE	 (1 << 5)	/* Break BLINE scope. */
+#define	MAN_JOIN	 (1 << 6)	/* Join arguments together. */
 };
 
 extern	const struct man_macro *const man_macros;
 
 __BEGIN_DECLS
 
-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);
-int		  man_tail_alloc(struct man *, int, int, enum mant);
-int		  man_body_alloc(struct man *, int, int, enum mant);
-int		  man_elem_alloc(struct man *, int, int, enum mant);
+void		  man_word_alloc(struct man *, int, int, const char *);
+void		  man_word_append(struct man *, const char *);
+void		  man_block_alloc(struct man *, int, int, enum mant);
+void		  man_head_alloc(struct man *, int, int, enum mant);
+void		  man_body_alloc(struct man *, int, int, enum mant);
+void		  man_elem_alloc(struct man *, int, int, enum mant);
 void		  man_node_delete(struct man *, struct man_node *);
 void		  man_hash_init(void);
 enum mant	  man_hash_find(const char *);
-int		  man_macroend(struct man *);
-int		  man_valid_post(struct man *);
-int		  man_unscope(struct man *, const struct man_node *);
+void		  man_macroend(struct man *);
+void		  man_valid_post(struct man *);
+void		  man_unscope(struct man *, const struct man_node *);
 
 __END_DECLS
 
 #endif /*!LIBMAN_H*/
Index: vendor/mdocml/dist/libmandoc.h
===================================================================
--- vendor/mdocml/dist/libmandoc.h	(revision 275396)
+++ vendor/mdocml/dist/libmandoc.h	(revision 275397)
@@ -1,92 +1,95 @@
-/*	$Id: libmandoc.h,v 1.42 2014/07/09 11:31:43 schwarze Exp $ */
+/*	$Id: libmandoc.h,v 1.49 2014/11/28 06:27:05 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons 
  * Copyright (c) 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef LIBMANDOC_H
 #define LIBMANDOC_H
 
 enum	rofferr {
 	ROFF_CONT, /* continue processing line */
 	ROFF_RERUN, /* re-run roff interpreter with offset */
 	ROFF_APPEND, /* re-run main parser, appending next line */
 	ROFF_REPARSE, /* re-run main parser on the result */
 	ROFF_SO, /* include another file */
 	ROFF_IGN, /* ignore current line */
 	ROFF_TBL, /* a table row was successfully parsed */
 	ROFF_EQN, /* an equation was successfully parsed */
 	ROFF_ERR /* badness: puke and stop */
 };
 
+struct	buf {
+	char	*buf;
+	size_t	 sz;
+};
+
 __BEGIN_DECLS
 
 struct	roff;
 struct	mdoc;
 struct	man;
 
 void		 mandoc_msg(enum mandocerr, struct mparse *,
 			int, int, const char *);
 #if __GNUC__ - 0 >= 4
 __attribute__((__format__ (__printf__, 5, 6)))
 #endif
 void		 mandoc_vmsg(enum mandocerr, struct mparse *,
 			int, int, const char *, ...);
 char		*mandoc_getarg(struct mparse *, char **, int, int *);
 char		*mandoc_normdate(struct mparse *, char *, int, int);
 int		 mandoc_eos(const char *, size_t);
 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 *,
 			const char *, int);
 void		 mdoc_reset(struct mdoc *);
 int		 mdoc_parseln(struct mdoc *, int, char *, int);
 int		 mdoc_endparse(struct mdoc *);
-int		 mdoc_addspan(struct mdoc *, const struct tbl_span *);
-int		 mdoc_addeqn(struct mdoc *, const struct eqn *);
+void		 mdoc_addspan(struct mdoc *, const struct tbl_span *);
+void		 mdoc_addeqn(struct mdoc *, const struct eqn *);
 
 void		 man_free(struct man *);
 struct	man	*man_alloc(struct roff *, struct mparse *, int);
 void		 man_reset(struct man *);
 int		 man_parseln(struct man *, int, char *, int);
 int		 man_endparse(struct man *);
-int		 man_addspan(struct man *, const struct tbl_span *);
-int		 man_addeqn(struct man *, const struct eqn *);
+void		 man_addspan(struct man *, const struct tbl_span *);
+void		 man_addeqn(struct man *, const struct eqn *);
 
+int		 preconv_cue(const struct buf *, size_t);
+int		 preconv_encode(struct buf *, size_t *,
+			struct buf *, size_t *, int *);
+
 void		 roff_free(struct roff *);
-struct roff	*roff_alloc(struct mparse *, int);
+struct roff	*roff_alloc(struct mparse *, const struct mchars *, int);
 void		 roff_reset(struct roff *);
-enum rofferr	 roff_parseln(struct roff *, int,
-			char **, size_t *, int, int *);
+enum rofferr	 roff_parseln(struct roff *, int, struct buf *, int *);
 void		 roff_endparse(struct roff *);
 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 *,
-			int, int, const char *);
-int		 roff_closeeqn(struct roff *);
-#endif
+int		 roff_getformat(const struct roff *);
 
 const struct tbl_span	*roff_span(const struct roff *);
 const struct eqn	*roff_eqn(const struct roff *);
 
 __END_DECLS
 
 #endif /*!LIBMANDOC_H*/
Index: vendor/mdocml/dist/libmdoc.h
===================================================================
--- vendor/mdocml/dist/libmdoc.h	(revision 275396)
+++ vendor/mdocml/dist/libmdoc.h	(revision 275397)
@@ -1,143 +1,133 @@
-/*	$Id: libmdoc.h,v 1.88 2014/08/01 17:40:34 schwarze Exp $ */
+/*	$Id: libmdoc.h,v 1.95 2014/11/29 03:37:44 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2013 Ingo Schwarze 
+ * Copyright (c) 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef LIBMDOC_H
 #define LIBMDOC_H
 
 enum	mdoc_next {
 	MDOC_NEXT_SIBLING = 0,
 	MDOC_NEXT_CHILD
 };
 
 struct	mdoc {
 	struct mparse	 *parse; /* parse pointer */
 	const char	 *defos; /* default argument for .Os */
 	int		  quick; /* abort parse early */
 	int		  flags; /* parse flags */
 #define	MDOC_LITERAL	 (1 << 1) /* in a literal scope */
 #define	MDOC_PBODY	 (1 << 2) /* in the document body */
 #define	MDOC_NEWLINE	 (1 << 3) /* first macro/text in a line */
 #define	MDOC_PHRASELIT	 (1 << 4) /* literal within a partila phrase */
 #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 */
+#define	MDOC_NODELIMC	 (1 << 10) /* disable closing delimiter handling */
 	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 */
 	struct mdoc_node *last_es; /* the most recent Es node */
 	struct mdoc_meta  meta; /* document meta-data */
 	enum mdoc_sec	  lastnamed;
 	enum mdoc_sec	  lastsec;
 	struct roff	 *roff;
 };
 
 #define	MACRO_PROT_ARGS	struct mdoc *mdoc, \
 			enum mdoct tok, \
 			int line, \
 			int ppos, \
 			int *pos, \
 			char *buf
 
 struct	mdoc_macro {
-	int		(*fp)(MACRO_PROT_ARGS);
+	void		(*fp)(MACRO_PROT_ARGS);
 	int		  flags;
 #define	MDOC_CALLABLE	 (1 << 0)
 #define	MDOC_PARSED	 (1 << 1)
 #define	MDOC_EXPLICIT	 (1 << 2)
 #define	MDOC_PROLOGUE	 (1 << 3)
 #define	MDOC_IGNDELIM	 (1 << 4)
 #define	MDOC_JOIN	 (1 << 5)
 };
 
 enum	margserr {
 	ARGS_ERROR,
 	ARGS_EOLN, /* end-of-line */
 	ARGS_WORD, /* normal word */
 	ARGS_PUNCT, /* series of punctuation */
 	ARGS_QWORD, /* quoted word */
 	ARGS_PHRASE, /* Ta'd phrase (-column) */
 	ARGS_PPHRASE, /* tabbed phrase (-column) */
 	ARGS_PEND /* last phrase (-column) */
 };
 
-enum	margverr {
-	ARGV_ERROR,
-	ARGV_EOLN, /* end of line */
-	ARGV_ARG, /* valid argument */
-	ARGV_WORD /* normal word (or bad argument---same thing) */
-};
-
 /*
  * A punctuation delimiter is opening, closing, or "middle mark"
  * punctuation.  These govern spacing.
  * Opening punctuation (e.g., the opening parenthesis) suppresses the
  * following space; closing punctuation (e.g., the closing parenthesis)
  * suppresses the leading space; middle punctuation (e.g., the vertical
  * bar) can do either.  The middle punctuation delimiter bends the rules
  * depending on usage.
  */
 enum	mdelim {
 	DELIM_NONE = 0,
 	DELIM_OPEN,
 	DELIM_MIDDLE,
 	DELIM_CLOSE,
 	DELIM_MAX
 };
 
 extern	const struct mdoc_macro *const mdoc_macros;
 
 __BEGIN_DECLS
 
-int		  mdoc_macro(MACRO_PROT_ARGS);
-int		  mdoc_word_alloc(struct mdoc *,
-			int, int, const char *);
+void		  mdoc_macro(MACRO_PROT_ARGS);
+void		  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,
+void		  mdoc_elem_alloc(struct mdoc *, int, int,
 			enum mdoct, struct mdoc_arg *);
-int		  mdoc_block_alloc(struct mdoc *, int, int,
+struct mdoc_node *mdoc_block_alloc(struct mdoc *, int, int,
 			enum mdoct, struct mdoc_arg *);
-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 *, int, int, enum mdoct,
+struct mdoc_node *mdoc_head_alloc(struct mdoc *, int, int, enum mdoct);
+void		  mdoc_tail_alloc(struct mdoc *, int, int, enum mdoct);
+struct mdoc_node *mdoc_body_alloc(struct mdoc *, int, int, enum mdoct);
+void		  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_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 *);
 const char	 *mdoc_a2lib(const char *);
 const char	 *mdoc_a2st(const char *);
 const char	 *mdoc_a2arch(const char *);
-const char	 *mdoc_a2vol(const char *);
-int		  mdoc_valid_pre(struct mdoc *, struct mdoc_node *);
-int		  mdoc_valid_post(struct mdoc *);
-enum margverr	  mdoc_argv(struct mdoc *, int, enum mdoct,
+void		  mdoc_valid_pre(struct mdoc *, struct mdoc_node *);
+void		  mdoc_valid_post(struct mdoc *);
+void		  mdoc_argv(struct mdoc *, int, enum mdoct,
 			struct mdoc_arg **, int *, char *);
 void		  mdoc_argv_free(struct mdoc_arg *);
 enum margserr	  mdoc_args(struct mdoc *, int,
 			int *, char *, enum mdoct, char **);
-enum margserr	  mdoc_zargs(struct mdoc *, int,
-			int *, char *, char **);
-int		  mdoc_macroend(struct mdoc *);
+void		  mdoc_macroend(struct mdoc *);
 enum mdelim	  mdoc_isdelim(const char *);
 
 __END_DECLS
 
 #endif /*!LIBMDOC_H*/
Index: vendor/mdocml/dist/libroff.h
===================================================================
--- vendor/mdocml/dist/libroff.h	(revision 275396)
+++ vendor/mdocml/dist/libroff.h	(revision 275397)
@@ -1,84 +1,88 @@
-/*	$Id: libroff.h,v 1.29 2014/04/20 16:46:04 schwarze Exp $ */
+/*	$Id: libroff.h,v 1.31 2014/10/25 14:35:37 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef LIBROFF_H
 #define LIBROFF_H
 
 __BEGIN_DECLS
 
 enum	tbl_part {
 	TBL_PART_OPTS, /* in options (first line) */
 	TBL_PART_LAYOUT, /* describing layout */
 	TBL_PART_DATA, /* creating data rows */
 	TBL_PART_CDATA /* continue previous row */
 };
 
 struct	tbl_node {
 	struct mparse	 *parse; /* parse point */
 	int		  pos; /* invocation column */
 	int		  line; /* invocation line */
 	enum tbl_part	  part;
 	struct tbl_opts	  opts;
 	struct tbl_row	 *first_row;
 	struct tbl_row	 *last_row;
 	struct tbl_span	 *first_span;
 	struct tbl_span	 *current_span;
 	struct tbl_span	 *last_span;
 	struct tbl_head	 *first_head;
 	struct tbl_head	 *last_head;
 	struct tbl_node	 *next;
 };
 
 struct	eqn_node {
-	struct eqn_def	 *defs;
-	size_t		  defsz;
-	char		 *data;
-	size_t		  rew;
-	size_t		  cur;
-	size_t		  sz;
-	int		  gsize;
-	struct eqn	  eqn;
-	struct mparse	 *parse;
-	struct eqn_node  *next;
+	struct eqn	  eqn;    /* syntax tree of this equation */
+	struct mparse	 *parse;  /* main parser, for error reporting */
+	struct eqn_node  *next;   /* singly linked list of equations */
+	struct eqn_def	 *defs;   /* array of definitions */
+	char		 *data;   /* source code of this equation */
+	size_t		  defsz;  /* number of definitions */
+	size_t		  sz;     /* length of the source code */
+	size_t		  cur;    /* parse point in the source code */
+	size_t		  rew;    /* beginning of the current token */
+	int		  gsize;  /* default point size */
+	int		  delim;  /* in-line delimiters enabled */
+	char		  odelim; /* in-line opening delimiter */
+	char		  cdelim; /* in-line closing delimiter */
 };
 
 struct	eqn_def {
 	char		 *key;
 	size_t		  keysz;
 	char		 *val;
 	size_t		  valsz;
 };
 
 struct tbl_node	*tbl_alloc(int, int, struct mparse *);
 void		 tbl_restart(int, int, struct tbl_node *);
 void		 tbl_free(struct tbl_node *);
 void		 tbl_reset(struct tbl_node *);
 enum rofferr	 tbl_read(struct tbl_node *, int, const char *, int);
 int		 tbl_option(struct tbl_node *, int, const char *);
 int		 tbl_layout(struct tbl_node *, int, const char *);
 int		 tbl_data(struct tbl_node *, int, const char *);
 int		 tbl_cdata(struct tbl_node *, int, const char *);
 const struct tbl_span	*tbl_span(struct tbl_node *);
 void		 tbl_end(struct tbl_node **);
-struct eqn_node	*eqn_alloc(const char *, int, int, struct mparse *);
+struct eqn_node	*eqn_alloc(int, int, struct mparse *);
 enum rofferr	 eqn_end(struct eqn_node **);
 void		 eqn_free(struct eqn_node *);
 enum rofferr	 eqn_read(struct eqn_node **, int,
 			const char *, int, int *);
 
 __END_DECLS
 
 #endif /*LIBROFF_H*/
Index: vendor/mdocml/dist/main.c
===================================================================
--- vendor/mdocml/dist/main.c	(revision 275396)
+++ vendor/mdocml/dist/main.c	(revision 275397)
@@ -1,430 +1,861 @@
-/*	$Id: main.c,v 1.177 2014/06/21 22:24:01 schwarze Exp $ */
+/*	$Id: main.c,v 1.200 2014/11/26 21:40:17 schwarze Exp $ */
 /*
- * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2008-2012 Kristaps Dzonsons 
  * Copyright (c) 2010, 2011, 2012, 2014 Ingo Schwarze 
  * Copyright (c) 2010 Joerg Sonnenberger 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
+#include 
+#include 
+#include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "main.h"
 #include "mdoc.h"
 #include "man.h"
+#include "manpath.h"
+#include "mansearch.h"
 
 #if !defined(__GNUC__) || (__GNUC__ < 2)
 # if !defined(lint)
 #  define __attribute__(x)
 # endif
 #endif /* !defined(__GNUC__) || (__GNUC__ < 2) */
 
+enum	outmode {
+	OUTMODE_DEF = 0,
+	OUTMODE_FLN,
+	OUTMODE_LST,
+	OUTMODE_ALL,
+	OUTMODE_INT,
+	OUTMODE_ONE
+};
+
 typedef	void		(*out_mdoc)(void *, const struct mdoc *);
 typedef	void		(*out_man)(void *, const struct man *);
 typedef	void		(*out_free)(void *);
 
 enum	outt {
 	OUTT_ASCII = 0,	/* -Tascii */
 	OUTT_LOCALE,	/* -Tlocale */
 	OUTT_UTF8,	/* -Tutf8 */
 	OUTT_TREE,	/* -Ttree */
 	OUTT_MAN,	/* -Tman */
 	OUTT_HTML,	/* -Thtml */
-	OUTT_XHTML,	/* -Txhtml */
 	OUTT_LINT,	/* -Tlint */
 	OUTT_PS,	/* -Tps */
 	OUTT_PDF	/* -Tpdf */
 };
 
 struct	curparse {
 	struct mparse	 *mp;
+	struct mchars	 *mchars;	/* character table */
 	enum mandoclevel  wlevel;	/* ignore messages below this */
 	int		  wstop;	/* stop after a file with a warning */
 	enum outt	  outtype;	/* which output to use */
 	out_mdoc	  outmdoc;	/* mdoc output ptr */
 	out_man		  outman;	/* man output ptr */
 	out_free	  outfree;	/* free output ptr */
 	void		 *outdata;	/* data for output */
 	char		  outopts[BUFSIZ]; /* buf of output opts */
 };
 
+static	int		  koptions(int *, char *);
 static	int		  moptions(int *, char *);
 static	void		  mmsg(enum mandocerr, enum mandoclevel,
 				const char *, int, int, const char *);
 static	void		  parse(struct curparse *, int,
 				const char *, enum mandoclevel *);
+static	enum mandoclevel  passthrough(const char *, int, int);
+static	void		  spawn_pager(void);
 static	int		  toptions(struct curparse *, char *);
-static	void		  usage(void) __attribute__((noreturn));
+static	void		  usage(enum argmode) __attribute__((noreturn));
 static	void		  version(void) __attribute__((noreturn));
 static	int		  woptions(struct curparse *, char *);
 
+static	const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9};
 static	const char	 *progname;
 
 
 int
 main(int argc, char *argv[])
 {
-	int		 c;
 	struct curparse	 curp;
-	int		 options;
-	enum mandoclevel rc;
+	struct mansearch search;
+	struct manpaths	 paths;
+	char		*conf_file, *defpaths, *auxpaths;
 	char		*defos;
+#if HAVE_SQLITE3
+	struct manpage	*res, *resp;
+	size_t		 isec, i, sz;
+	int		 prio, best_prio;
+	char		 sec;
+#endif
+	enum mandoclevel rc;
+	enum outmode	 outmode;
+	int		 fd;
+	int		 show_usage;
+	int		 use_pager;
+	int		 synopsis_only;
+	int		 options;
+	int		 c;
 
 	progname = strrchr(argv[0], '/');
 	if (progname == NULL)
 		progname = argv[0];
 	else
 		++progname;
 
-	memset(&curp, 0, sizeof(struct curparse));
+	/* Search options. */
 
-	options = MPARSE_SO;
+	memset(&paths, 0, sizeof(struct manpaths));
+	conf_file = defpaths = auxpaths = NULL;
+
+	memset(&search, 0, sizeof(struct mansearch));
+	search.outkey = "Nd";
+
+	if (strcmp(progname, "man") == 0)
+		search.argmode = ARG_NAME;
+	else if (strncmp(progname, "apropos", 7) == 0)
+		search.argmode = ARG_EXPR;
+	else if (strncmp(progname, "whatis", 6) == 0)
+		search.argmode = ARG_WORD;
+	else
+		search.argmode = ARG_FILE;
+
+	/* Parser and formatter options. */
+
+	memset(&curp, 0, sizeof(struct curparse));
 	curp.outtype = OUTT_ASCII;
 	curp.wlevel  = MANDOCLEVEL_FATAL;
+	options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1;
 	defos = NULL;
 
-	while (-1 != (c = getopt(argc, argv, "I:m:O:T:VW:")))
+	use_pager = 1;
+	show_usage = 0;
+	synopsis_only = 0;
+	outmode = OUTMODE_DEF;
+
+	while (-1 != (c = getopt(argc, argv,
+			"aC:cfhI:iK:klM:m:O:S:s:T:VW:w"))) {
 		switch (c) {
+		case 'a':
+			outmode = OUTMODE_ALL;
+			break;
+		case 'C':
+			conf_file = optarg;
+			break;
+		case 'c':
+			use_pager = 0;
+			break;
+		case 'f':
+			search.argmode = ARG_WORD;
+			break;
+		case 'h':
+			(void)strlcat(curp.outopts, "synopsis,", BUFSIZ);
+			synopsis_only = 1;
+			use_pager = 0;
+			outmode = OUTMODE_ALL;
+			break;
 		case 'I':
 			if (strncmp(optarg, "os=", 3)) {
 				fprintf(stderr,
 				    "%s: -I%s: Bad argument\n",
 				    progname, optarg);
 				return((int)MANDOCLEVEL_BADARG);
 			}
 			if (defos) {
 				fprintf(stderr,
 				    "%s: -I%s: Duplicate argument\n",
 				    progname, optarg);
 				return((int)MANDOCLEVEL_BADARG);
 			}
 			defos = mandoc_strdup(optarg + 3);
 			break;
-		case 'm':
-			if ( ! moptions(&options, optarg))
+		case 'i':
+			outmode = OUTMODE_INT;
+			break;
+		case 'K':
+			if ( ! koptions(&options, optarg))
 				return((int)MANDOCLEVEL_BADARG);
 			break;
+		case 'k':
+			search.argmode = ARG_EXPR;
+			break;
+		case 'l':
+			search.argmode = ARG_FILE;
+			outmode = OUTMODE_ALL;
+			break;
+		case 'M':
+			defpaths = optarg;
+			break;
+		case 'm':
+			auxpaths = optarg;
+			break;
 		case 'O':
+			search.outkey = optarg;
 			(void)strlcat(curp.outopts, optarg, BUFSIZ);
 			(void)strlcat(curp.outopts, ",", BUFSIZ);
 			break;
+		case 'S':
+			search.arch = optarg;
+			break;
+		case 's':
+			search.sec = optarg;
+			break;
 		case 'T':
 			if ( ! toptions(&curp, optarg))
 				return((int)MANDOCLEVEL_BADARG);
 			break;
 		case 'W':
 			if ( ! woptions(&curp, optarg))
 				return((int)MANDOCLEVEL_BADARG);
 			break;
+		case 'w':
+			outmode = OUTMODE_FLN;
+			break;
 		case 'V':
 			version();
 			/* NOTREACHED */
 		default:
-			usage();
-			/* NOTREACHED */
+			show_usage = 1;
+			break;
 		}
+	}
 
-	curp.mp = mparse_alloc(options, curp.wlevel, mmsg, defos);
+	if (show_usage)
+		usage(search.argmode);
 
+	/* Postprocess options. */
+
+	if (outmode == OUTMODE_DEF) {
+		switch (search.argmode) {
+		case ARG_FILE:
+			outmode = OUTMODE_ALL;
+			use_pager = 0;
+			break;
+		case ARG_NAME:
+			outmode = OUTMODE_ONE;
+			break;
+		default:
+			outmode = OUTMODE_LST;
+			break;
+		}
+	}
+
+	/* Parse arguments. */
+
+	argc -= optind;
+	argv += optind;
+#if HAVE_SQLITE3
+	resp = NULL;
+#endif
+
+	/* Quirk for a man(1) section argument without -s. */
+
+	if (search.argmode == ARG_NAME &&
+	    argv[0] != NULL &&
+	    isdigit((unsigned char)argv[0][0]) &&
+	    (argv[0][1] == '\0' || !strcmp(argv[0], "3p"))) {
+		search.sec = argv[0];
+		argv++;
+		argc--;
+	}
+
+	rc = MANDOCLEVEL_OK;
+
+	/* man(1), whatis(1), apropos(1) */
+
+	if (search.argmode != ARG_FILE) {
+#if HAVE_SQLITE3
+		if (argc == 0)
+			usage(search.argmode);
+
+		if (search.argmode == ARG_NAME &&
+		    outmode == OUTMODE_ONE)
+			search.firstmatch = 1;
+
+		/* Access the mandoc database. */
+
+		manpath_parse(&paths, conf_file, defpaths, auxpaths);
+		mansearch_setup(1);
+		if( ! mansearch(&search, &paths, argc, argv, &res, &sz))
+			usage(search.argmode);
+		resp = res;
+
+		if (sz == 0) {
+			if (search.argmode == ARG_NAME)
+				fprintf(stderr, "%s: No entry for %s "
+				    "in the manual.\n", progname, argv[0]);
+			rc = MANDOCLEVEL_BADARG;
+			goto out;
+		}
+
+		/*
+		 * For standard man(1) and -a output mode,
+		 * prepare for copying filename pointers
+		 * into the program parameter array.
+		 */
+
+		if (outmode == OUTMODE_ONE) {
+			argc = 1;
+			best_prio = 10;
+		} else if (outmode == OUTMODE_ALL)
+			argc = (int)sz;
+
+		/* Iterate all matching manuals. */
+
+		for (i = 0; i < sz; i++) {
+			if (outmode == OUTMODE_FLN)
+				puts(res[i].file);
+			else if (outmode == OUTMODE_LST)
+				printf("%s - %s\n", res[i].names,
+				    res[i].output == NULL ? "" :
+				    res[i].output);
+			else if (outmode == OUTMODE_ONE) {
+				/* Search for the best section. */
+				isec = strcspn(res[i].file, "123456789");
+				sec = res[i].file[isec];
+				if ('\0' == sec)
+					continue;
+				prio = sec_prios[sec - '1'];
+				if (prio >= best_prio)
+					continue;
+				best_prio = prio;
+				resp = res + i;
+			}
+		}
+
+		/*
+		 * For man(1), -a and -i output mode, fall through
+		 * to the main mandoc(1) code iterating files
+		 * and running the parsers on each of them.
+		 */
+
+		if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST)
+			goto out;
+#else
+		fputs("mandoc: database support not compiled in\n",
+		    stderr);
+		return((int)MANDOCLEVEL_BADARG);
+#endif
+	}
+
+	/* mandoc(1) */
+
+	if ( ! moptions(&options, auxpaths))
+		return((int)MANDOCLEVEL_BADARG);
+
+	if (use_pager && isatty(STDOUT_FILENO))
+		spawn_pager();
+
+	curp.mchars = mchars_alloc();
+	curp.mp = mparse_alloc(options, curp.wlevel, mmsg,
+	    curp.mchars, defos);
+
 	/*
 	 * Conditionally start up the lookaside buffer before parsing.
 	 */
 	if (OUTT_MAN == curp.outtype)
 		mparse_keep(curp.mp);
 
-	argc -= optind;
-	argv += optind;
+	if (argc == 0)
+		parse(&curp, STDIN_FILENO, "", &rc);
 
-	rc = MANDOCLEVEL_OK;
+	while (argc) {
+#if HAVE_SQLITE3
+		if (resp != NULL) {
+			rc = mparse_open(curp.mp, &fd, resp->file);
+			if (fd == -1)
+				/* nothing */;
+			else if (resp->form & FORM_SRC) {
+				/* For .so only; ignore failure. */
+				chdir(paths.paths[resp->ipath]);
+				parse(&curp, fd, resp->file, &rc);
+			} else
+				rc = passthrough(resp->file, fd,
+				    synopsis_only);
+			resp++;
+		} else
+#endif
+		{
+			rc = mparse_open(curp.mp, &fd, *argv++);
+			if (fd != -1)
+				parse(&curp, fd, argv[-1], &rc);
+		}
 
-	if (NULL == *argv)
-		parse(&curp, STDIN_FILENO, "", &rc);
+		if (mparse_wait(curp.mp) != MANDOCLEVEL_OK)
+			rc = MANDOCLEVEL_SYSERR;
 
-	while (*argv) {
-		parse(&curp, -1, *argv, &rc);
 		if (MANDOCLEVEL_OK != rc && curp.wstop)
 			break;
-		++argv;
+		argc--;
 	}
 
 	if (curp.outfree)
 		(*curp.outfree)(curp.outdata);
-	if (curp.mp)
-		mparse_free(curp.mp);
+	mparse_free(curp.mp);
+	mchars_free(curp.mchars);
+
+#if HAVE_SQLITE3
+out:
+	if (search.argmode != ARG_FILE) {
+		manpath_free(&paths);
+		mansearch_free(res, sz);
+		mansearch_setup(0);
+	}
+#endif
+
 	free(defos);
 
 	return((int)rc);
 }
 
 static void
 version(void)
 {
 
-	printf("%s %s\n", progname, VERSION);
+	printf("mandoc %s\n", VERSION);
 	exit((int)MANDOCLEVEL_OK);
 }
 
 static void
-usage(void)
+usage(enum argmode argmode)
 {
 
-	fprintf(stderr, "usage: %s "
-			"[-V] "
-			"[-Ios=name] "
-			"[-mformat] "
-			"[-Ooption] "
-			"[-Toutput] "
-			"[-Wlevel]\n"
-			"\t      [file ...]\n",
-			progname);
-
+	switch (argmode) {
+	case ARG_FILE:
+		fputs("usage: mandoc [-acfhklV] [-Ios=name] "
+		    "[-Kencoding] [-mformat] [-Ooption]\n"
+		    "\t      [-Toutput] [-Wlevel] [file ...]\n", stderr);
+		break;
+	case ARG_NAME:
+		fputs("usage: man [-acfhklVw] [-C file] "
+		    "[-M path] [-m path] [-S arch] [-s section]\n"
+		    "\t   [section] name ...\n", stderr);
+		break;
+	case ARG_WORD:
+		fputs("usage: whatis [-acfhklVw] [-C file] "
+		    "[-M path] [-m path] [-O outkey] [-S arch]\n"
+		    "\t      [-s section] name ...\n", stderr);
+		break;
+	case ARG_EXPR:
+		fputs("usage: apropos [-acfhklVw] [-C file] "
+		    "[-M path] [-m path] [-O outkey] [-S arch]\n"
+		    "\t       [-s section] expression ...\n", stderr);
+		break;
+	}
 	exit((int)MANDOCLEVEL_BADARG);
 }
 
 static void
 parse(struct curparse *curp, int fd, const char *file,
 	enum mandoclevel *level)
 {
 	enum mandoclevel  rc;
 	struct mdoc	 *mdoc;
 	struct man	 *man;
 
 	/* Begin by parsing the file itself. */
 
 	assert(file);
 	assert(fd >= -1);
 
 	rc = mparse_readfd(curp->mp, fd, file);
 
 	/* Stop immediately if the parse has failed. */
 
 	if (MANDOCLEVEL_FATAL <= rc)
 		goto cleanup;
 
 	/*
 	 * With -Wstop and warnings or errors of at least the requested
 	 * level, do not produce output.
 	 */
 
 	if (MANDOCLEVEL_OK != rc && curp->wstop)
 		goto cleanup;
 
 	/* If unset, allocate output dev now (if applicable). */
 
 	if ( ! (curp->outman && curp->outmdoc)) {
 		switch (curp->outtype) {
-		case OUTT_XHTML:
-			curp->outdata = xhtml_alloc(curp->outopts);
-			curp->outfree = html_free;
-			break;
 		case OUTT_HTML:
-			curp->outdata = html_alloc(curp->outopts);
+			curp->outdata = html_alloc(curp->mchars,
+			    curp->outopts);
 			curp->outfree = html_free;
 			break;
 		case OUTT_UTF8:
-			curp->outdata = utf8_alloc(curp->outopts);
+			curp->outdata = utf8_alloc(curp->mchars,
+			    curp->outopts);
 			curp->outfree = ascii_free;
 			break;
 		case OUTT_LOCALE:
-			curp->outdata = locale_alloc(curp->outopts);
+			curp->outdata = locale_alloc(curp->mchars,
+			    curp->outopts);
 			curp->outfree = ascii_free;
 			break;
 		case OUTT_ASCII:
-			curp->outdata = ascii_alloc(curp->outopts);
+			curp->outdata = ascii_alloc(curp->mchars,
+			    curp->outopts);
 			curp->outfree = ascii_free;
 			break;
 		case OUTT_PDF:
-			curp->outdata = pdf_alloc(curp->outopts);
+			curp->outdata = pdf_alloc(curp->mchars,
+			    curp->outopts);
 			curp->outfree = pspdf_free;
 			break;
 		case OUTT_PS:
-			curp->outdata = ps_alloc(curp->outopts);
+			curp->outdata = ps_alloc(curp->mchars,
+			    curp->outopts);
 			curp->outfree = pspdf_free;
 			break;
 		default:
 			break;
 		}
 
 		switch (curp->outtype) {
 		case OUTT_HTML:
-			/* FALLTHROUGH */
-		case OUTT_XHTML:
 			curp->outman = html_man;
 			curp->outmdoc = html_mdoc;
 			break;
 		case OUTT_TREE:
 			curp->outman = tree_man;
 			curp->outmdoc = tree_mdoc;
 			break;
 		case OUTT_MAN:
 			curp->outmdoc = man_mdoc;
 			curp->outman = man_man;
 			break;
 		case OUTT_PDF:
 			/* FALLTHROUGH */
 		case OUTT_ASCII:
 			/* FALLTHROUGH */
 		case OUTT_UTF8:
 			/* FALLTHROUGH */
 		case OUTT_LOCALE:
 			/* FALLTHROUGH */
 		case OUTT_PS:
 			curp->outman = terminal_man;
 			curp->outmdoc = terminal_mdoc;
 			break;
 		default:
 			break;
 		}
 	}
 
 	mparse_result(curp->mp, &mdoc, &man, NULL);
 
 	/* Execute the out device, if it exists. */
 
 	if (man && curp->outman)
 		(*curp->outman)(curp->outdata, man);
 	if (mdoc && curp->outmdoc)
 		(*curp->outmdoc)(curp->outdata, mdoc);
 
  cleanup:
 
 	mparse_reset(curp->mp);
 
 	if (*level < rc)
 		*level = rc;
 }
 
+static enum mandoclevel
+passthrough(const char *file, int fd, int synopsis_only)
+{
+	const char	 synb[] = "S\bSY\bYN\bNO\bOP\bPS\bSI\bIS\bS";
+	const char	 synr[] = "SYNOPSIS";
+
+	FILE		*stream;
+	const char	*syscall;
+	char		*line;
+	size_t		 len, off;
+	ssize_t		 nw;
+	int		 print;
+
+	if ((stream = fdopen(fd, "r")) == NULL) {
+		close(fd);
+		syscall = "fdopen";
+		goto fail;
+	}
+
+	print = 0;
+	while ((line = fgetln(stream, &len)) != NULL) {
+		if (synopsis_only) {
+			if (print) {
+				if ( ! isspace((unsigned char)*line))
+					goto done;
+				while (len &&
+				    isspace((unsigned char)*line)) {
+					line++;
+					len--;
+				}
+			} else {
+				if ((len == sizeof(synb) &&
+				     ! strncmp(line, synb, len - 1)) ||
+				    (len == sizeof(synr) &&
+				     ! strncmp(line, synr, len - 1)))
+					print = 1;
+				continue;
+			}
+		}
+		for (off = 0; off < len; off += nw)
+			if ((nw = write(STDOUT_FILENO, line + off,
+			    len - off)) == -1 || nw == 0) {
+				fclose(stream);
+				syscall = "write";
+				goto fail;
+			}
+	}
+
+	if (ferror(stream)) {
+		fclose(stream);
+		syscall = "fgetln";
+		goto fail;
+	}
+
+done:
+	fclose(stream);
+	return(MANDOCLEVEL_OK);
+
+fail:
+	fprintf(stderr, "%s: %s: SYSERR: %s: %s",
+	    progname, file, syscall, strerror(errno));
+	return(MANDOCLEVEL_SYSERR);
+}
+
 static int
+koptions(int *options, char *arg)
+{
+
+	if ( ! strcmp(arg, "utf-8")) {
+		*options |=  MPARSE_UTF8;
+		*options &= ~MPARSE_LATIN1;
+	} else if ( ! strcmp(arg, "iso-8859-1")) {
+		*options |=  MPARSE_LATIN1;
+		*options &= ~MPARSE_UTF8;
+	} else if ( ! strcmp(arg, "us-ascii")) {
+		*options &= ~(MPARSE_UTF8 | MPARSE_LATIN1);
+	} else {
+		fprintf(stderr, "%s: -K%s: Bad argument\n",
+		    progname, arg);
+		return(0);
+	}
+	return(1);
+}
+
+static int
 moptions(int *options, char *arg)
 {
 
-	if (0 == strcmp(arg, "doc"))
+	if (arg == NULL)
+		/* nothing to do */;
+	else if (0 == strcmp(arg, "doc"))
 		*options |= MPARSE_MDOC;
 	else if (0 == strcmp(arg, "andoc"))
 		/* nothing to do */;
 	else if (0 == strcmp(arg, "an"))
 		*options |= MPARSE_MAN;
 	else {
 		fprintf(stderr, "%s: -m%s: Bad argument\n",
 		    progname, arg);
 		return(0);
 	}
 
 	return(1);
 }
 
 static int
 toptions(struct curparse *curp, char *arg)
 {
 
 	if (0 == strcmp(arg, "ascii"))
 		curp->outtype = OUTT_ASCII;
 	else if (0 == strcmp(arg, "lint")) {
 		curp->outtype = OUTT_LINT;
 		curp->wlevel  = MANDOCLEVEL_WARNING;
 	} else if (0 == strcmp(arg, "tree"))
 		curp->outtype = OUTT_TREE;
 	else if (0 == strcmp(arg, "man"))
 		curp->outtype = OUTT_MAN;
 	else if (0 == strcmp(arg, "html"))
 		curp->outtype = OUTT_HTML;
 	else if (0 == strcmp(arg, "utf8"))
 		curp->outtype = OUTT_UTF8;
 	else if (0 == strcmp(arg, "locale"))
 		curp->outtype = OUTT_LOCALE;
 	else if (0 == strcmp(arg, "xhtml"))
-		curp->outtype = OUTT_XHTML;
+		curp->outtype = OUTT_HTML;
 	else if (0 == strcmp(arg, "ps"))
 		curp->outtype = OUTT_PS;
 	else if (0 == strcmp(arg, "pdf"))
 		curp->outtype = OUTT_PDF;
 	else {
 		fprintf(stderr, "%s: -T%s: Bad argument\n",
 		    progname, arg);
 		return(0);
 	}
 
 	return(1);
 }
 
 static int
 woptions(struct curparse *curp, char *arg)
 {
 	char		*v, *o;
 	const char	*toks[6];
 
 	toks[0] = "stop";
 	toks[1] = "all";
 	toks[2] = "warning";
 	toks[3] = "error";
 	toks[4] = "fatal";
 	toks[5] = NULL;
 
 	while (*arg) {
 		o = arg;
 		switch (getsubopt(&arg, UNCONST(toks), &v)) {
 		case 0:
 			curp->wstop = 1;
 			break;
 		case 1:
 			/* FALLTHROUGH */
 		case 2:
 			curp->wlevel = MANDOCLEVEL_WARNING;
 			break;
 		case 3:
 			curp->wlevel = MANDOCLEVEL_ERROR;
 			break;
 		case 4:
 			curp->wlevel = MANDOCLEVEL_FATAL;
 			break;
 		default:
 			fprintf(stderr, "%s: -W%s: Bad argument\n",
 			    progname, o);
 			return(0);
 		}
 	}
 
 	return(1);
 }
 
 static void
 mmsg(enum mandocerr t, enum mandoclevel lvl,
 		const char *file, int line, int col, const char *msg)
 {
 	const char	*mparse_msg;
 
 	fprintf(stderr, "%s: %s:", progname, file);
 
 	if (line)
 		fprintf(stderr, "%d:%d:", line, col + 1);
 
 	fprintf(stderr, " %s", mparse_strlevel(lvl));
 
 	if (NULL != (mparse_msg = mparse_strerror(t)))
 		fprintf(stderr, ": %s", mparse_msg);
 
 	if (msg)
 		fprintf(stderr, ": %s", msg);
 
 	fputc('\n', stderr);
+}
+
+static void
+spawn_pager(void)
+{
+#define MAX_PAGER_ARGS 16
+	char		*argv[MAX_PAGER_ARGS];
+	const char	*pager;
+	char		*cp;
+	int		 fildes[2];
+	int		 argc;
+
+	if (pipe(fildes) == -1) {
+		fprintf(stderr, "%s: pipe: %s\n",
+		    progname, strerror(errno));
+		return;
+	}
+
+	switch (fork()) {
+	case -1:
+		fprintf(stderr, "%s: fork: %s\n",
+		    progname, strerror(errno));
+		exit((int)MANDOCLEVEL_SYSERR);
+	case 0:
+		close(fildes[0]);
+		if (dup2(fildes[1], STDOUT_FILENO) == -1) {
+			fprintf(stderr, "%s: dup output: %s\n",
+			    progname, strerror(errno));
+			exit((int)MANDOCLEVEL_SYSERR);
+		}
+		return;
+	default:
+		break;
+	}
+
+	/* The original process becomes the pager. */
+
+	close(fildes[1]);
+	if (dup2(fildes[0], STDIN_FILENO) == -1) {
+		fprintf(stderr, "%s: dup input: %s\n",
+		    progname, strerror(errno));
+		exit((int)MANDOCLEVEL_SYSERR);
+	}
+
+	pager = getenv("MANPAGER");
+	if (pager == NULL || *pager == '\0')
+		pager = getenv("PAGER");
+	if (pager == NULL || *pager == '\0')
+		pager = "/usr/bin/more -s";
+	cp = mandoc_strdup(pager);
+
+	/*
+	 * Parse the pager command into words.
+	 * Intentionally do not do anything fancy here.
+	 */
+
+	argc = 0;
+	while (argc + 1 < MAX_PAGER_ARGS) {
+		argv[argc++] = cp;
+		cp = strchr(cp, ' ');
+		if (cp == NULL)
+			break;
+		*cp++ = '\0';
+		while (*cp == ' ')
+			cp++;
+		if (*cp == '\0')
+			break;
+	}
+	argv[argc] = NULL;
+
+	/* Hand over to the pager. */
+
+	execvp(argv[0], argv);
+	fprintf(stderr, "%s: exec: %s\n",
+	    progname, strerror(errno));
+	exit((int)MANDOCLEVEL_SYSERR);
 }
Index: vendor/mdocml/dist/main.h
===================================================================
--- vendor/mdocml/dist/main.h	(revision 275396)
+++ vendor/mdocml/dist/main.h	(revision 275397)
@@ -1,61 +1,60 @@
-/*	$Id: main.h,v 1.16 2014/04/20 16:46:04 schwarze Exp $ */
+/*	$Id: main.h,v 1.17 2014/10/28 17:36:19 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef	MAIN_H
 #define	MAIN_H
 
 __BEGIN_DECLS
 
 struct	mdoc;
 struct	man;
 
 #define	UNCONST(a)	((void *)(uintptr_t)(const void *)(a))
 
 
 /*
  * Definitions for main.c-visible output device functions, e.g., -Thtml
  * and -Tascii.  Note that ascii_alloc() is named as such in
  * anticipation of latin1_alloc() and so on, all of which map into the
  * terminal output routines with different character settings.
  */
 
-void		 *html_alloc(char *);
-void		 *xhtml_alloc(char *);
+void		 *html_alloc(const struct mchars *, char *);
 void		  html_mdoc(void *, const struct mdoc *);
 void		  html_man(void *, const struct man *);
 void		  html_free(void *);
 
 void		  tree_mdoc(void *, const struct mdoc *);
 void		  tree_man(void *, const struct man *);
 
 void		  man_mdoc(void *, const struct mdoc *);
 void		  man_man(void *, const struct man *);
 
-void		 *locale_alloc(char *);
-void		 *utf8_alloc(char *);
-void		 *ascii_alloc(char *);
+void		 *locale_alloc(const struct mchars *, char *);
+void		 *utf8_alloc(const struct mchars *, char *);
+void		 *ascii_alloc(const struct mchars *, char *);
 void		  ascii_free(void *);
 
-void		 *pdf_alloc(char *);
-void		 *ps_alloc(char *);
+void		 *pdf_alloc(const struct mchars *, char *);
+void		 *ps_alloc(const struct mchars *, char *);
 void		  pspdf_free(void *);
 
 void		  terminal_mdoc(void *, const struct mdoc *);
 void		  terminal_man(void *, const struct man *);
 
 __END_DECLS
 
 #endif /*!MAIN_H*/
Index: vendor/mdocml/dist/makewhatis.8
===================================================================
--- vendor/mdocml/dist/makewhatis.8	(revision 275396)
+++ vendor/mdocml/dist/makewhatis.8	(revision 275397)
@@ -1,217 +1,217 @@
-.\"	$Id: makewhatis.8,v 1.2 2014/04/25 12:13:15 schwarze Exp $
+.\"	$Id: makewhatis.8,v 1.3 2014/08/17 21:03:06 schwarze Exp $
 .\"
 .\" Copyright (c) 2011, 2012 Kristaps Dzonsons 
 .\" Copyright (c) 2011, 2012 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" copyright notice and this permission notice appear in all copies.
 .\"
 .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: April 25 2014 $
+.Dd $Mdocdate: August 17 2014 $
 .Dt MAKEWHATIS 8
 .Os
 .Sh NAME
 .Nm makewhatis
 .Nd index UNIX manuals
 .Sh SYNOPSIS
 .Nm
 .Op Fl aDnpQ
 .Op Fl T Cm utf8
 .Op Fl C Ar file
 .Nm
 .Op Fl aDnpQ
 .Op Fl T Cm utf8
 .Ar dir ...
 .Nm
 .Op Fl DnpQ
 .Op Fl T Cm utf8
 .Fl d Ar dir
 .Op Ar
 .Nm
 .Op Fl Dnp
 .Op Fl T Cm utf8
 .Fl u Ar dir
 .Op Ar
 .Nm
 .Op Fl DQ
 .Fl t Ar
 .Sh DESCRIPTION
 The
 .Nm
 utility extracts keywords from
 .Ux
 manuals and indexes them in a database for fast retrieval by
 .Xr apropos 1 ,
 .Xr whatis 1 ,
 and
 .Xr man 1 Ns 's
 .Fl k
 option.
 .Pp
 By default,
 .Nm
 creates a database in each
 .Ar dir
 using the files
 .Sm off
 .Sy man Ar section Li /
 .Op Ar arch Li /
 .Ar title . section
 .Sm on
 and
 .Sm off
 .Sy cat Ar section Li /
 .Op Ar arch Li /
 .Ar title . Sy 0
 .Sm on
 in that directory.
 Existing databases are replaced.
 If
 .Ar dir
 is not provided,
 .Nm
 uses the default paths stipulated by
 .Xr manpath 1 ,
 or
 .Xr man.conf 5 .
 .Pp
 The arguments are as follows:
 .Bl -tag -width "-C file"
 .It Fl a
 Use all directories and files found below
 .Ar dir ... .
 .It Fl C Ar file
 Specify an alternative configuration
 .Ar file
 in
 .Xr man.conf 5
 format.
 .It Fl D
 Display all files added or removed to the index.
 With a second
 .Fl D ,
-also show all keyswords added for each file.
+also show all keywords added for each file.
 .It Fl d Ar dir
 Merge (remove and re-add)
 .Ar
 to the database in
 .Ar dir .
 .It Fl n
 Do not create or modify any database; scan and parse only,
 and print manual page names and descriptions to standard output.
 .It Fl p
 Print warnings about potential problems with manual pages
 to the standard error output.
 .It Fl Q
 Quickly build reduced-size databases
 by reading only the NAME sections of manuals.
 The resulting databases will usually contain names and descriptions only.
 .It Fl T Cm utf8
 Use UTF-8 encoding instead of ASCII for strings stored in the databases.
 .It Fl t Ar
 Check the given
 .Ar files
 for potential problems.
 Implies
 .Fl a ,
 .Fl n ,
 and
 .Fl p .
 All diagnostic messages are printed to the standard output;
 the standard error output is not used.
 .It Fl u Ar dir
 Remove
 .Ar
 from the database in
 .Ar dir .
 .El
 .Pp
 If fatal parse errors are encountered while parsing, the offending file
 is printed to stderr, omitted from the index, and the parse continues
 with the next input file.
 .Sh FILES
 .Bl -tag -width Ds
 .It Pa mandoc.db
 A database of manpages relative to the directory of the file.
 This file is portable across architectures and systems, so long as the
 manpage hierarchy it indexes does not change.
 .It Pa /etc/man.conf
 The default
 .Xr man 1
 configuration file.
 .El
 .Sh EXIT STATUS
 The
 .Nm
 utility exits with one of the following values:
 .Pp
 .Bl -tag -width Ds -compact
 .It 0
 No errors occurred.
 .It 5
 Invalid command line arguments were specified.
 No input files have been read.
 .It 6
 An operating system error occurred, for example memory exhaustion or an
 error accessing input files.
 Such errors cause
 .Nm
 to exit at once, possibly in the middle of parsing or formatting a file.
 The output databases are corrupt and should be removed.
 .El
 .Sh SEE ALSO
 .Xr apropos 1 ,
 .Xr man 1 ,
 .Xr whatis 1 ,
 .Xr man.conf 5
 .Sh HISTORY
 A
 .Nm
 utility first appeared in
 .Bx 2 .
 It was rewritten in
 .Xr perl 1
 for
 .Ox 2.7
 and in C for
 .Ox 5.6 .
 .Pp
 The
 .Ar dir
 argument first appeared in
 .Nx 1.0 ;
 the options
 .Fl dpt
 in
 .Ox 2.7 ;
 the option
 .Fl u
 in
 .Ox 3.4 ;
 and the options
 .Fl aCDnQT
 in
 .Ox 5.6 .
 .Sh AUTHORS
 .An -nosplit
 .An Bill Joy
 wrote the original
 .Bx
 .Nm
 in February 1979,
 .An Marc Espie
 started the Perl version in 2000,
 and the current version of
 .Nm
 was written by
 .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
 and
 .An Ingo Schwarze Aq Mt schwarze@openbsd.org .
Index: vendor/mdocml/dist/man.1
===================================================================
--- vendor/mdocml/dist/man.1	(nonexistent)
+++ vendor/mdocml/dist/man.1	(revision 275397)
@@ -0,0 +1,402 @@
+.\"	$Id: man.1,v 1.7 2014/11/11 02:43:41 schwarze Exp $
+.\"
+.\" Copyright (c) 1989, 1990, 1993
+.\"	The Regents of the University of California.  All rights reserved.
+.\" Copyright (c) 2003, 2007, 2008, 2014 Jason McIntyre 
+.\" Copyright (c) 2010, 2011 Ingo Schwarze 
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the University nor the names of its contributors
+.\"    may be used to endorse or promote products derived from this software
+.\"    without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"     @(#)man.1	8.2 (Berkeley) 1/2/94
+.\"
+.Dd $Mdocdate: November 11 2014 $
+.Dt MAN 1
+.Os
+.Sh NAME
+.Nm man
+.Nd display manual pages
+.Sh SYNOPSIS
+.Nm man
+.Op Fl acfhklVw
+.Op Fl C Ar file
+.Op Fl M Ar path
+.Op Fl m Ar path
+.Op Fl S Ar subsection
+.Op Fl s Ar section
+.Op Ar section
+.Ar name ...
+.Sh DESCRIPTION
+The
+.Nm
+utility
+displays the
+manual pages entitled
+.Ar name .
+Pages may be selected according to
+a specific category
+.Pq Ar section
+or
+machine architecture
+.Pq Ar subsection .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Display all of the manual pages for a specified
+.Ar section
+and
+.Ar name
+combination.
+Normally, only the first manual page found is displayed.
+.It Fl C Ar file
+Use the specified
+.Ar file
+instead of the default configuration file.
+This permits users to configure their own manual environment.
+See
+.Xr man.conf 5
+for a description of the contents of this file.
+.It Fl c
+Copy the manual page to the standard output instead of using
+.Xr more 1
+to paginate it.
+This is done by default if the standard output is not a terminal device.
+.It Fl f
+A synonym for
+.Xr whatis 1 .
+It searches for
+.Ar name
+in manual page names and displays the header lines from all matching pages.
+The search is case insensitive and matches whole words only.
+This overrides any earlier
+.Fl k
+and
+.Fl l
+options.
+.It Fl h
+Display only the SYNOPSIS lines of the requested manual pages.
+Implies
+.Fl a
+and
+.Fl c .
+.It Fl k
+A synonym for
+.Xr apropos 1 .
+Instead of
+.Ar name ,
+an expression can be provided using the syntax described in the
+.Xr apropos 1
+manual.
+By default, it displays the header lines of all matching pages.
+This overrides any earlier
+.Fl f
+and
+.Fl l
+options.
+.It Fl l
+A synonym for
+.Xr mandoc 1
+.Fl a .
+The
+.Ar name
+arguments are interpreted as filenames.
+No search is done and
+.Ar file ,
+.Ar path ,
+.Ar section ,
+and
+.Ar subsection
+are ignored.
+This overrides any earlier
+.Fl f ,
+.Fl k ,
+and
+.Fl w
+options.
+.It Fl M Ar path
+Override the list of standard directories which
+.Nm
+searches for manual pages.
+The supplied
+.Ar path
+must be a colon
+.Pq Ql \&:
+separated list of directories.
+This search path may also be set using the environment variable
+.Ev MANPATH .
+The subdirectories to be searched, and their search order,
+are specified by the
+.Dq _subdir
+line in the
+.Nm
+configuration file.
+.It Fl m Ar path
+Augment the list of standard directories which
+.Nm
+searches for manual pages.
+The supplied
+.Ar path
+must be a colon
+.Pq Ql \&:
+separated list of directories.
+These directories will be searched before the standard directories or
+the directories specified using the
+.Fl M
+option or the
+.Ev MANPATH
+environment variable.
+The subdirectories to be searched, and their search order,
+are specified by the
+.Dq _subdir
+line in the
+.Nm
+configuration file.
+.It Fl S Ar subsection
+Restricts the directories that
+.Nm
+will search to those of a specific
+.Xr machine 1
+architecture.
+.Ar subsection
+is case insensitive.
+.Pp
+By default manual pages for all architectures are installed.
+Therefore this option can be used to view pages for one
+architecture whilst using another.
+.Pp
+This option overrides the
+.Ev MACHINE
+environment variable.
+.It Xo
+.Op Fl s
+.Ar section
+.Xc
+Restricts the directories that
+.Nm
+will search to a specific section.
+The currently available sections are:
+.Pp
+.Bl -tag -width "localXXX" -offset indent -compact
+.It 1
+General commands
+.Pq tools and utilities .
+.It 2
+System calls and error numbers.
+.It 3
+Libraries.
+.It 3f
+Fortran programmer's reference guide.
+.It 3p
+.Xr perl 1
+programmer's reference guide.
+.It 4
+Device drivers.
+.It 5
+File formats.
+.It 6
+Games.
+.It 7
+Miscellaneous.
+.It 8
+System maintenance and operation commands.
+.It 9
+Kernel internals.
+.It X11
+An alias for X11R6.
+.It X11R6
+X Window System.
+.It local
+Pages located in
+.Pa /usr/local .
+.It n
+Tcl/Tk commands.
+.El
+.Pp
+The
+.Nm
+configuration file,
+.Xr man.conf 5 ,
+specifies the possible
+.Ar section
+values, and their search order.
+Additional sections may be specified.
+.It Fl V
+Print version and exit.
+.It Fl w
+List the pathnames of the manual pages which
+.Nm
+would display for the specified
+.Ar section
+and
+.Ar name
+combination.
+.El
+.Pp
+The
+.Nm
+utility also supports the options
+.Fl IKOTW
+described in the
+.Xr mandoc 1
+manual.
+.Pp
+Guidelines for writing
+man pages can be found in
+.Xr mdoc 7 .
+.Pp
+If both a formatted and an unformatted version of the same manual page,
+for example
+.Pa cat1/foo.0
+and
+.Pa man1/foo.1 ,
+exist in the same directory, and at least one of them is selected,
+only the newer one is used.
+However, if both the
+.Fl a
+and the
+.Fl w
+options are specified, both file names are printed.
+.Sh ENVIRONMENT
+.Bl -tag -width MANPATHX
+.It Ev MACHINE
+As some manual pages are intended only for specific architectures,
+.Nm
+searches any subdirectories,
+with the same name as the current architecture,
+in every directory which it searches.
+Machine specific areas are checked before general areas.
+The current machine type may be overridden by setting the environment
+variable
+.Ev MACHINE
+to the name of a specific architecture,
+or with the
+.Fl S
+option.
+.Ev MACHINE
+is case insensitive.
+.It Ev MANPAGER
+Any non-empty value of the environment variable
+.Ev MANPAGER
+will be used instead of the standard pagination program,
+.Xr more 1 .
+.It Ev MANPATH
+The standard search path used by
+.Nm
+may be overridden by specifying a path in the
+.Ev MANPATH
+environment
+variable.
+The format of the path is a colon
+.Pq Ql \&:
+separated list of directories.
+The subdirectories to be searched, as well as their search order,
+are specified by the
+.Dq _subdir
+line in the
+.Nm
+configuration file.
+.It Ev PAGER
+Specifies the pagination program to use when
+.Ev MANPAGER
+is not defined.
+If neither PAGER nor MANPAGER is defined,
+.Pa /usr/bin/more Fl s
+will be used.
+.El
+.Sh FILES
+.Bl -tag -width /etc/man.conf -compact
+.It Pa /etc/man.conf
+default man configuration file
+.El
+.Sh EXIT STATUS
+.Ex -std man
+.Sh SEE ALSO
+.Xr apropos 1 ,
+.Xr intro 1 ,
+.Xr whatis 1 ,
+.Xr whereis 1 ,
+.Xr intro 2 ,
+.Xr intro 3 ,
+.Xr intro 4 ,
+.Xr intro 5 ,
+.Xr man.conf 5 ,
+.Xr intro 6 ,
+.Xr intro 7 ,
+.Xr mdoc 7 ,
+.Xr intro 8 ,
+.Xr intro 9
+.Sh STANDARDS
+The
+.Nm
+utility is compliant with the
+.St -p1003.1-2008
+specification.
+.Pp
+The flags
+.Op Fl aCcfhMmSsw ,
+as well as the environment variables
+.Ev MACHINE ,
+.Ev MANPAGER ,
+and
+.Ev MANPATH ,
+are extensions to that specification.
+.Sh HISTORY
+A
+.Nm
+command first appeared in
+.At v3 .
+.Pp
+The
+.Fl w
+option first appeared in
+.At v7 ;
+.Fl f
+and
+.Fl k
+in
+.Bx 4 ;
+.Fl M
+in
+.Bx 4.3 ;
+.Fl a
+in
+.Bx 4.3 Tahoe ;
+.Fl c
+and
+.Fl m
+in
+.Bx 4.3 Reno ;
+.Fl h
+in
+.Bx 4.3 Net/2 ;
+.Fl C
+in
+.Nx 1.0 ;
+and
+.Fl s
+and
+.Fl S
+in
+.Ox 2.3 .

Property changes on: vendor/mdocml/dist/man.1
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: vendor/mdocml/dist/man.c
===================================================================
--- vendor/mdocml/dist/man.c	(revision 275396)
+++ vendor/mdocml/dist/man.c	(revision 275397)
@@ -1,704 +1,678 @@
-/*	$Id: man.c,v 1.137 2014/08/01 21:24:17 schwarze Exp $ */
+/*	$Id: man.c,v 1.145 2014/11/28 06:27:05 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2013, 2014 Ingo Schwarze 
  * Copyright (c) 2011 Joerg Sonnenberger 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "man.h"
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "libman.h"
 #include "libmandoc.h"
 
 const	char *const __man_macronames[MAN_MAX] = {
 	"br",		"TH",		"SH",		"SS",
 	"TP",		"LP",		"PP",		"P",
 	"IP",		"HP",		"SM",		"SB",
 	"BI",		"IB",		"BR",		"RB",
 	"R",		"B",		"I",		"IR",
 	"RI",		"na",		"sp",		"nf",
 	"fi",		"RE",		"RS",		"DT",
 	"UC",		"PD",		"AT",		"in",
 	"ft",		"OP",		"EX",		"EE",
 	"UR",		"UE",		"ll"
 	};
 
 const	char * const *man_macronames = __man_macronames;
 
 static	struct man_node	*man_node_alloc(struct man *, int, int,
 				enum man_type, enum mant);
-static	int		 man_node_append(struct man *,
-				struct man_node *);
+static	void		 man_node_append(struct man *, struct man_node *);
 static	void		 man_node_free(struct man_node *);
 static	void		 man_node_unlink(struct man *,
 				struct man_node *);
 static	int		 man_ptext(struct man *, int, char *, int);
 static	int		 man_pmacro(struct man *, int, char *, int);
 static	void		 man_free1(struct man *);
 static	void		 man_alloc1(struct man *);
-static	int		 man_descope(struct man *, int, int);
+static	void		 man_descope(struct man *, int, int);
 
 
 const struct man_node *
 man_node(const struct man *man)
 {
 
 	return(man->first);
 }
 
 const struct man_meta *
 man_meta(const struct man *man)
 {
 
 	return(&man->meta);
 }
 
 void
 man_reset(struct man *man)
 {
 
 	man_free1(man);
 	man_alloc1(man);
 }
 
 void
 man_free(struct man *man)
 {
 
 	man_free1(man);
 	free(man);
 }
 
 struct man *
 man_alloc(struct roff *roff, struct mparse *parse, int quick)
 {
 	struct man	*p;
 
 	p = mandoc_calloc(1, sizeof(struct man));
 
 	man_hash_init();
 	p->parse = parse;
 	p->quick = quick;
 	p->roff = roff;
 
 	man_alloc1(p);
 	return(p);
 }
 
 int
 man_endparse(struct man *man)
 {
 
-	return(man_macroend(man));
+	man_macroend(man);
+	return(1);
 }
 
 int
 man_parseln(struct man *man, int ln, char *buf, int offs)
 {
 
-	man->flags |= MAN_NEWLINE;
+	if (man->last->type != MAN_EQN || ln > man->last->line)
+		man->flags |= MAN_NEWLINE;
 
 	return (roff_getcontrol(man->roff, buf, &offs) ?
 	    man_pmacro(man, ln, buf, offs) :
 	    man_ptext(man, ln, buf, offs));
 }
 
 static void
 man_free1(struct man *man)
 {
 
 	if (man->first)
 		man_node_delete(man, man->first);
-	if (man->meta.title)
-		free(man->meta.title);
-	if (man->meta.source)
-		free(man->meta.source);
-	if (man->meta.date)
-		free(man->meta.date);
-	if (man->meta.vol)
-		free(man->meta.vol);
-	if (man->meta.msec)
-		free(man->meta.msec);
+	free(man->meta.title);
+	free(man->meta.source);
+	free(man->meta.date);
+	free(man->meta.vol);
+	free(man->meta.msec);
 }
 
 static void
 man_alloc1(struct man *man)
 {
 
 	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;
 }
 
 
-static int
+static void
 man_node_append(struct man *man, struct man_node *p)
 {
 
 	assert(man->last);
 	assert(man->first);
-	assert(MAN_ROOT != p->type);
+	assert(p->type != MAN_ROOT);
 
 	switch (man->next) {
 	case MAN_NEXT_SIBLING:
 		man->last->next = p;
 		p->prev = man->last;
 		p->parent = man->last->parent;
 		break;
 	case MAN_NEXT_CHILD:
 		man->last->child = p;
 		p->parent = man->last;
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	assert(p->parent);
 	p->parent->nchild++;
 
 	switch (p->type) {
 	case MAN_BLOCK:
 		if (p->tok == MAN_SH || p->tok == MAN_SS)
 			man->flags &= ~MAN_LITERAL;
 		break;
 	case MAN_HEAD:
-		assert(MAN_BLOCK == p->parent->type);
+		assert(p->parent->type == MAN_BLOCK);
 		p->parent->head = p;
 		break;
-	case MAN_TAIL:
-		assert(MAN_BLOCK == p->parent->type);
-		p->parent->tail = p;
-		break;
 	case MAN_BODY:
-		assert(MAN_BLOCK == p->parent->type);
+		assert(p->parent->type == MAN_BLOCK);
 		p->parent->body = p;
 		break;
 	default:
 		break;
 	}
 
 	man->last = p;
 
 	switch (p->type) {
 	case MAN_TBL:
 		/* FALLTHROUGH */
 	case MAN_TEXT:
-		if ( ! man_valid_post(man))
-			return(0);
+		man_valid_post(man);
 		break;
 	default:
 		break;
 	}
-
-	return(1);
 }
 
 static struct man_node *
 man_node_alloc(struct man *man, int line, int pos,
 		enum man_type type, enum mant tok)
 {
 	struct man_node *p;
 
 	p = mandoc_calloc(1, sizeof(struct man_node));
 	p->line = line;
 	p->pos = pos;
 	p->type = type;
 	p->tok = tok;
 
-	if (MAN_NEWLINE & man->flags)
+	if (man->flags & MAN_NEWLINE)
 		p->flags |= MAN_LINE;
 	man->flags &= ~MAN_NEWLINE;
 	return(p);
 }
 
-int
+void
 man_elem_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
 	p = man_node_alloc(man, line, pos, MAN_ELEM, tok);
-	if ( ! man_node_append(man, p))
-		return(0);
+	man_node_append(man, p);
 	man->next = MAN_NEXT_CHILD;
-	return(1);
 }
 
-int
-man_tail_alloc(struct man *man, int line, int pos, enum mant tok)
-{
-	struct man_node *p;
-
-	p = man_node_alloc(man, line, pos, MAN_TAIL, tok);
-	if ( ! man_node_append(man, p))
-		return(0);
-	man->next = MAN_NEXT_CHILD;
-	return(1);
-}
-
-int
+void
 man_head_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
 	p = man_node_alloc(man, line, pos, MAN_HEAD, tok);
-	if ( ! man_node_append(man, p))
-		return(0);
+	man_node_append(man, p);
 	man->next = MAN_NEXT_CHILD;
-	return(1);
 }
 
-int
+void
 man_body_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
 	p = man_node_alloc(man, line, pos, MAN_BODY, tok);
-	if ( ! man_node_append(man, p))
-		return(0);
+	man_node_append(man, p);
 	man->next = MAN_NEXT_CHILD;
-	return(1);
 }
 
-int
+void
 man_block_alloc(struct man *man, int line, int pos, enum mant tok)
 {
 	struct man_node *p;
 
 	p = man_node_alloc(man, line, pos, MAN_BLOCK, tok);
-	if ( ! man_node_append(man, p))
-		return(0);
+	man_node_append(man, p);
 	man->next = MAN_NEXT_CHILD;
-	return(1);
 }
 
-int
+void
 man_word_alloc(struct man *man, int line, int pos, const char *word)
 {
 	struct man_node	*n;
 
 	n = man_node_alloc(man, line, pos, MAN_TEXT, MAN_MAX);
 	n->string = roff_strdup(man->roff, word);
+	man_node_append(man, n);
+	man->next = MAN_NEXT_SIBLING;
+}
 
-	if ( ! man_node_append(man, n))
-		return(0);
+void
+man_word_append(struct man *man, const char *word)
+{
+	struct man_node	*n;
+	char		*addstr, *newstr;
 
+	n = man->last;
+	addstr = roff_strdup(man->roff, word);
+	mandoc_asprintf(&newstr, "%s %s", n->string, addstr);
+	free(addstr);
+	free(n->string);
+	n->string = newstr;
 	man->next = MAN_NEXT_SIBLING;
-	return(1);
 }
 
 /*
  * Free all of the resources held by a node.  This does NOT unlink a
  * node from its context; for that, see man_node_unlink().
  */
 static void
 man_node_free(struct man_node *p)
 {
 
-	if (p->string)
-		free(p->string);
+	free(p->string);
 	free(p);
 }
 
 void
 man_node_delete(struct man *man, struct man_node *p)
 {
 
 	while (p->child)
 		man_node_delete(man, p->child);
 
 	man_node_unlink(man, p);
 	man_node_free(p);
 }
 
-int
+void
 man_addeqn(struct man *man, const struct eqn *ep)
 {
 	struct man_node	*n;
 
 	n = man_node_alloc(man, ep->ln, ep->pos, MAN_EQN, MAN_MAX);
 	n->eqn = ep;
-
-	if ( ! man_node_append(man, n))
-		return(0);
-
+	if (ep->ln > man->last->line)
+		n->flags |= MAN_LINE;
+	man_node_append(man, n);
 	man->next = MAN_NEXT_SIBLING;
-	return(man_descope(man, ep->ln, ep->pos));
+	man_descope(man, ep->ln, ep->pos);
 }
 
-int
+void
 man_addspan(struct man *man, const struct tbl_span *sp)
 {
 	struct man_node	*n;
 
 	n = man_node_alloc(man, sp->line, 0, MAN_TBL, MAN_MAX);
 	n->span = sp;
-
-	if ( ! man_node_append(man, n))
-		return(0);
-
+	man_node_append(man, n);
 	man->next = MAN_NEXT_SIBLING;
-	return(man_descope(man, sp->line, 0));
+	man_descope(man, sp->line, 0);
 }
 
-static int
+static void
 man_descope(struct man *man, int line, int offs)
 {
 	/*
 	 * Co-ordinate what happens with having a next-line scope open:
 	 * first close out the element scope (if applicable), then close
 	 * out the block scope (also if applicable).
 	 */
 
-	if (MAN_ELINE & man->flags) {
+	if (man->flags & MAN_ELINE) {
 		man->flags &= ~MAN_ELINE;
-		if ( ! man_unscope(man, man->last->parent))
-			return(0);
+		man_unscope(man, man->last->parent);
 	}
-
-	if ( ! (MAN_BLINE & man->flags))
-		return(1);
+	if ( ! (man->flags & MAN_BLINE))
+		return;
 	man->flags &= ~MAN_BLINE;
-
-	if ( ! man_unscope(man, man->last->parent))
-		return(0);
-	return(man_body_alloc(man, line, offs, man->last->tok));
+	man_unscope(man, man->last->parent);
+	man_body_alloc(man, line, offs, man->last->tok);
 }
 
 static int
 man_ptext(struct man *man, int line, char *buf, int offs)
 {
 	int		 i;
 
 	/* Literal free-form text whitespace is preserved. */
 
-	if (MAN_LITERAL & man->flags) {
-		if ( ! man_word_alloc(man, line, offs, buf + offs))
-			return(0);
-		return(man_descope(man, line, offs));
+	if (man->flags & MAN_LITERAL) {
+		man_word_alloc(man, line, offs, buf + offs);
+		man_descope(man, line, offs);
+		return(1);
 	}
 
-	for (i = offs; ' ' == buf[i]; i++)
+	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]) {
+	if (buf[i] == '\0') {
 		/* Allocate a blank entry. */
-		if (MAN_SH != man->last->tok &&
-		    MAN_SS != man->last->tok) {
-			if ( ! man_elem_alloc(man, line, offs, MAN_sp))
-				return(0);
+		if (man->last->tok != MAN_SH &&
+		    man->last->tok != MAN_SS) {
+			man_elem_alloc(man, line, offs, MAN_sp);
 			man->next = MAN_NEXT_SIBLING;
 		}
 		return(1);
 	}
 
 	/*
 	 * Warn if the last un-escaped character is whitespace. Then
 	 * strip away the remaining spaces (tabs stay!).
 	 */
 
 	i = (int)strlen(buf);
 	assert(i);
 
 	if (' ' == buf[i - 1] || '\t' == buf[i - 1]) {
 		if (i > 1 && '\\' != buf[i - 2])
 			mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
 			    line, i - 1, NULL);
 
 		for (--i; i && ' ' == buf[i]; i--)
 			/* Spin back to non-space. */ ;
 
 		/* Jump ahead of escaped whitespace. */
 		i += '\\' == buf[i] ? 2 : 1;
 
 		buf[i] = '\0';
 	}
+	man_word_alloc(man, line, offs, buf + offs);
 
-	if ( ! man_word_alloc(man, line, offs, buf + offs))
-		return(0);
-
 	/*
 	 * End-of-sentence check.  If the last character is an unescaped
 	 * EOS character, then flag the node as being the end of a
 	 * sentence.  The front-end will know how to interpret this.
 	 */
 
 	assert(i);
 	if (mandoc_eos(buf, (size_t)i))
 		man->last->flags |= MAN_EOS;
 
-	return(man_descope(man, line, offs));
+	man_descope(man, line, offs);
+	return(1);
 }
 
 static int
 man_pmacro(struct man *man, int ln, char *buf, int offs)
 {
-	char		 mac[5];
 	struct man_node	*n;
+	const char	*cp;
 	enum mant	 tok;
 	int		 i, ppos;
 	int		 bline;
+	char		 mac[5];
 
-	if ('"' == buf[offs]) {
-		mandoc_msg(MANDOCERR_COMMENT_BAD, man->parse,
-		    ln, offs, NULL);
-		return(1);
-	} else if ('\0' == buf[offs])
-		return(1);
-
 	ppos = offs;
 
 	/*
 	 * Copy the first word into a nil-terminated buffer.
-	 * Stop copying when a tab, space, or eoln is encountered.
+	 * Stop when a space, tab, escape, or eoln is encountered.
 	 */
 
 	i = 0;
-	while (i < 4 && '\0' != buf[offs] && ' ' != buf[offs] &&
-	    '\t' != buf[offs])
+	while (i < 4 && strchr(" \t\\", buf[offs]) == NULL)
 		mac[i++] = buf[offs++];
 
 	mac[i] = '\0';
 
 	tok = (i > 0 && i < 4) ? man_hash_find(mac) : MAN_MAX;
 
-	if (MAN_MAX == tok) {
+	if (tok == MAN_MAX) {
 		mandoc_msg(MANDOCERR_MACRO, man->parse,
 		    ln, ppos, buf + ppos - 1);
 		return(1);
 	}
 
-	/* The macro is sane.  Jump to the next word. */
+	/* Skip a leading escape sequence or tab. */
 
-	while (buf[offs] && ' ' == buf[offs])
+	switch (buf[offs]) {
+	case '\\':
+		cp = buf + offs + 1;
+		mandoc_escape(&cp, NULL, NULL);
+		offs = cp - buf;
+		break;
+	case '\t':
 		offs++;
+		break;
+	default:
+		break;
+	}
 
+	/* Jump to the next non-whitespace word. */
+
+	while (buf[offs] && buf[offs] == ' ')
+		offs++;
+
 	/*
 	 * Trailing whitespace.  Note that tabs are allowed to be passed
 	 * into the parser as "text", so we only warn about spaces here.
 	 */
 
-	if ('\0' == buf[offs] && ' ' == buf[offs - 1])
+	if (buf[offs] == '\0' && buf[offs - 1] == ' ')
 		mandoc_msg(MANDOCERR_SPACE_EOL, man->parse,
 		    ln, offs - 1, NULL);
 
 	/*
 	 * Remove prior ELINE macro, as it's being clobbered by a new
 	 * macro.  Note that NSCOPED macros do not close out ELINE
 	 * macros---they don't print text---so we let those slip by.
 	 */
 
-	if ( ! (MAN_NSCOPED & man_macros[tok].flags) &&
+	if ( ! (man_macros[tok].flags & MAN_NSCOPED) &&
 			man->flags & MAN_ELINE) {
 		n = man->last;
 		assert(MAN_TEXT != n->type);
 
 		/* Remove repeated NSCOPED macros causing ELINE. */
 
-		if (MAN_NSCOPED & man_macros[n->tok].flags)
+		if (man_macros[n->tok].flags & MAN_NSCOPED)
 			n = n->parent;
 
 		mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line,
 		    n->pos, "%s breaks %s", man_macronames[tok],
 		    man_macronames[n->tok]);
 
 		man_node_delete(man, n);
 		man->flags &= ~MAN_ELINE;
 	}
 
 	/*
 	 * Remove prior BLINE macro that is being clobbered.
 	 */
 	if ((man->flags & MAN_BLINE) &&
-	    (MAN_BSCOPE & man_macros[tok].flags)) {
+	    (man_macros[tok].flags & MAN_BSCOPE)) {
 		n = man->last;
 
 		/* Might be a text node like 8 in
 		 * .TP 8
 		 * .SH foo
 		 */
-		if (MAN_TEXT == n->type)
+		if (n->type == MAN_TEXT)
 			n = n->parent;
 
 		/* Remove element that didn't end BLINE, if any. */
-		if ( ! (MAN_BSCOPE & man_macros[n->tok].flags))
+		if ( ! (man_macros[n->tok].flags & MAN_BSCOPE))
 			n = n->parent;
 
-		assert(MAN_HEAD == n->type);
+		assert(n->type == MAN_HEAD);
 		n = n->parent;
-		assert(MAN_BLOCK == n->type);
-		assert(MAN_SCOPED & man_macros[n->tok].flags);
+		assert(n->type == MAN_BLOCK);
+		assert(man_macros[n->tok].flags & MAN_SCOPED);
 
 		mandoc_vmsg(MANDOCERR_BLK_LINE, man->parse, n->line,
 		    n->pos, "%s breaks %s", man_macronames[tok],
 		    man_macronames[n->tok]);
 
 		man_node_delete(man, n);
 		man->flags &= ~MAN_BLINE;
 	}
 
 	/* Remember whether we are in next-line scope for a block head. */
 
 	bline = man->flags & MAN_BLINE;
 
 	/* Call to handler... */
 
 	assert(man_macros[tok].fp);
-	if ( ! (*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf))
-		return(0);
+	(*man_macros[tok].fp)(man, tok, ln, ppos, &offs, buf);
 
 	/* In quick mode (for mandocdb), abort after the NAME section. */
 
-	if (man->quick && MAN_SH == tok) {
+	if (man->quick && tok == MAN_SH) {
 		n = man->last;
-		if (MAN_BODY == n->type &&
+		if (n->type == MAN_BODY &&
 		    strcmp(n->prev->child->string, "NAME"))
 			return(2);
 	}
 
 	/*
 	 * If we are in a next-line scope for a block head,
 	 * close it out now and switch to the body,
 	 * unless the next-line scope is allowed to continue.
 	 */
 
 	if ( ! bline || man->flags & MAN_ELINE ||
 	    man_macros[tok].flags & MAN_NSCOPED)
 		return(1);
 
-	assert(MAN_BLINE & man->flags);
+	assert(man->flags & MAN_BLINE);
 	man->flags &= ~MAN_BLINE;
 
-	if ( ! man_unscope(man, man->last->parent))
-		return(0);
-	return(man_body_alloc(man, ln, ppos, man->last->tok));
+	man_unscope(man, man->last->parent);
+	man_body_alloc(man, ln, ppos, man->last->tok);
+	return(1);
 }
 
 /*
  * 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 *man, struct man_node *n)
 {
 
 	/* Adjust siblings. */
 
 	if (n->prev)
 		n->prev->next = n->next;
 	if (n->next)
 		n->next->prev = n->prev;
 
 	/* Adjust parent. */
 
 	if (n->parent) {
 		n->parent->nchild--;
 		if (n->parent->child == n)
 			n->parent->child = n->prev ? n->prev : n->next;
 	}
 
 	/* Adjust parse point, if applicable. */
 
 	if (man && man->last == n) {
 		/*XXX: this can occur when bailing from validation. */
 		/*assert(NULL == n->next);*/
 		if (n->prev) {
 			man->last = n->prev;
 			man->next = MAN_NEXT_SIBLING;
 		} else {
 			man->last = n->parent;
 			man->next = MAN_NEXT_CHILD;
 		}
 	}
 
 	if (man && man->first == n)
 		man->first = NULL;
 }
 
 const struct mparse *
 man_mparse(const struct man *man)
 {
 
 	assert(man && man->parse);
 	return(man->parse);
 }
 
 void
 man_deroff(char **dest, const struct man_node *n)
 {
 	char	*cp;
 	size_t	 sz;
 
-	if (MAN_TEXT != n->type) {
+	if (n->type != MAN_TEXT) {
 		for (n = n->child; n; n = n->next)
 			man_deroff(dest, n);
 		return;
 	}
 
 	/* Skip leading whitespace and escape sequences. */
 
 	cp = n->string;
 	while ('\0' != *cp) {
 		if ('\\' == *cp) {
 			cp++;
 			mandoc_escape((const char **)&cp, NULL, NULL);
 		} else if (isspace((unsigned char)*cp))
 			cp++;
 		else
 			break;
 	}
 
 	/* Skip trailing whitespace. */
 
 	for (sz = strlen(cp); sz; sz--)
 		if (0 == isspace((unsigned char)cp[sz-1]))
 			break;
 
 	/* Skip empty strings. */
 
 	if (0 == sz)
 		return;
 
 	if (NULL == *dest) {
 		*dest = mandoc_strndup(cp, sz);
 		return;
 	}
 
 	mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp);
 	free(*dest);
 	*dest = cp;
 }
Index: vendor/mdocml/dist/man.cgi.8
===================================================================
--- vendor/mdocml/dist/man.cgi.8	(revision 275396)
+++ vendor/mdocml/dist/man.cgi.8	(revision 275397)
@@ -1,409 +1,415 @@
-.\"     $Id: man.cgi.8,v 1.9 2014/07/22 18:14:13 schwarze Exp $
+.\"     $Id: man.cgi.8,v 1.11 2014/09/14 19:44:28 schwarze Exp $
 .\"
 .\" Copyright (c) 2014 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" copyright notice and this permission notice appear in all copies.
 .\"
 .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: July 22 2014 $
+.Dd $Mdocdate: September 14 2014 $
 .Dt MAN.CGI 8
 .Os
 .Sh NAME
 .Nm man.cgi
 .Nd CGI program to search and display manual pages
 .Sh DESCRIPTION
 The
 .Nm
 CGI program searches for manual pages on a WWW server
 and displays them to HTTP clients,
 providing functionality equivalent to the
 .Xr apropos 1
 and
 .Xr man 1
 utilities.
 It can use multiple manual trees in parallel.
 .Ss HTML search interface
 At the top of each generated HTML page,
 .Nm
 displays a search form containing these elements:
 .Bl -enum
 .It
 An input box for search queries, expecting
 either a name of a manual page or an
 .Ar expression
 using the syntax described in the
 .Xr apropos 1
 manual; filling this in is required for each search.
+.Pp
+The expression is broken into words at whitespace.
+Whitespace characters and backslashes can be escaped
+by prepending a backslash.
+The effect of prepending a backslash to another character is undefined;
+in the current implementation, it has no effect.
 .It
 A
 .Dq Submit
 button to send a search request from the client to the server.
 .It
 A
 .Dq Reset
 button to undo any changes to the input boxes and the dropdown menus
 and reset them to the values contained in the
 .Ev QUERY_STRING .
 .It
 Radio buttons to select pages either by name like in
 .Xr man 1
 or using
 .Xr apropos 1
 queries.
 .It
 A dropdown menu to optionally select a manual section.
 If one is provided, it has the same effect as the
 .Xr man 1
 and
 .Xr apropos 1
 .Fl s
 option.
 Otherwise, pages from all sections are shown.
 .It
 A dropdown menu to optionally select an architecture.
 If one is provided, it has the same effect as the
 .Xr man 1
 and
 .Xr apropos 1
 .Fl S
 option.
 By default, pages for all architectures are shown.
 .It
 A dropdown menu to select a manual tree.
 If the configuration file
 .Pa /var/www/man/manpath.conf
 contains only one manpath, the dropdown menu is not shown.
 By default, the first manpath given in the file is used.
 .El
 .Ss Program output
 The
 .Nm
 program generates five kinds of output pages:
 .Bl -tag -width Ds
 .It The index page.
 This is returned when calling
 .Nm
 without
 .Ev PATH_INFO
 and without a
 .Ev QUERY_STRING .
 It serves as a starting point for using the program
 and shows the search form only.
 .It A list page.
 Lists are returned when searches match more than one manual page.
 The first column shows the names and section numbers of manuals
 as clickable links.
 The second column shows the one-line descriptions of the manuals.
 .It A manual page.
 This output format is used when a search matches exactly one
 manual page, or when a link on a list page or an
 .Ic \&Xr
 link on another manual page is followed.
 .It A no-result page.
 This is shown when a search request returns no results -
 eiher because it violates the query syntax, or because
 the search does not match any manual pages.
 .It \&An error page.
 This cannot happen by merely clicking the
 .Dq Search
 button, but only by manually entering an invalid URI.
 It does not show the search form, but only an error message
 and a link back to the index page.
 .El
 .Ss Setup
 For each manual tree, create one first-level subdirectory below
 .Pa /var/www/man .
 The name of one of these directories is called a
 .Dq manpath
 in the context of
 .Nm .
 Create a single ASCII text file
 .Pa /var/www/man/manpath.conf
 containing the names of these directories, one per line.
 The directory given first is used as the default manpath.
 .Pp
 Inside each of these directories, use the same directory and file
 structure as found below
 .Pa /usr/share/man ,
 that is, second-level subdirectories
 .Pa /var/www/man/*/man1 , /var/www/man/*/man2
 etc. containing source
 .Xr mdoc 7
 and
 .Xr man 7
 manuals with file name extensions matching the section numbers,
 second-level subdirectories
 .Pa /var/www/man/*/cat1 , /var/www/man/*/cat2
 etc. containing preformatted manuals with the file name extension
 .Sq 0 ,
 and optional third-level subdirectories for architectures.
 Use
 .Xr makewhatis 8
 to create a
 .Xr mandoc.db 5
 database inside each manpath.
 .Pp
 Configure your web server to execute CGI programs located in
 .Pa /cgi-bin .
 When using
 .Xr nginx 8 ,
 the
 .Xr slowcgi 8
 proxy daemon is needed to translate FastCGI requests to plain old CGI.
 .Pp
 To compile
 .Nm ,
 first copy
 .Pa cgi.h.example
 to
 .Pa cgi.h
 and edit it according to your needs.
 It contains the following compile-time definitions:
 .Bl -tag -width Ds
 .It Ev COMPAT_OLDURI
 Only useful for running on www.openbsd.org to deal with old URIs containing
 .Qq "manpath=OpenBSD "
 where the blank character has to be translated to a hyphen.
 When compiling for other sites, this definition can be deleted.
 .It Ev CSS_DIR
 An optional path to the directory containing the CSS files,
 to be specified relative to the server's document root,
 and to be specified without a trailing slash.
 When not specified, the CSS files
 are assumed to be in the document root.
 This is used in generated HTML code.
 .It Ev CUSTOMIZE_BEGIN
 A HTML string to be inserted right after opening the
 .Aq BODY
 element.
 .It Ev CUSTOMIZE_TITLE
 An ASCII string to be used for the HTML
 .Aq TITLE
 element.
 .It Ev HTTP_HOST
 The FQDN of the (possibly virtual) host the HTTP server is running on.
 This is used for
 .Ic Location:
 headers in HTTP 303 responses.
 .It Ev MAN_DIR
 A path to the
 .Nm
 data directory to be used instead of
 .Pa /var/www/man ,
 relative to the web server
 .Xr chroot 2
 directory, to be specified without a trailing slash.
 This is prepended to the manpath when opening
 .Xr mandoc.db 5
 and manual page files.
 .El
 .Pp
 After editing
 .Pa cgi.h ,
 run
 .Pp
 .Dl make man.cgi
 .Pp
 and copy the files to the proper locations.
 Reading the
 .Cm installcgi
 target in the
 .Pa Makefile
 can help with that, but do not run it without carefully checking it
 because the directory layouts of web servers vary greatly.
 .Ss URI interface
 .Nm
 uniform resource identifiers are not needed for interactive use,
 but can be useful for deep linking.
 They consist of:
 .Bl -enum
 .It
 The
 .Cm http://
 protocol specifier.
 .It
 The host name and a following slash.
 .It
 The path to the program, normally
 .Pa cgi-bin/man.cgi/ .
 .It
 To show a single page, a slash, the manpath, another slash,
 and the name of the requested file, for example
 .Pa /OpenBSD-current/man1/mandoc.1 .
 .It
 For searches, a query string starting with a question mark
 and consisting of
 .Ar key Ns = Ns Ar value
 pairs, separated by ampersands, for example
 .Pa ?manpath=OpenBSD-current&query=mandoc .
 Supported keys are
 .Cm manpath ,
 .Cm query ,
 .Cm sec ,
 .Cm arch ,
 corresponding to
 .Xr apropos 1
 .Fl M ,
 .Ar expression ,
 .Fl s ,
 .Fl S ,
 respectively, and
 .Cm apropos ,
 which is a boolean parameter to select or deselect the
 .Xr apropos 1
 query mode.
 For backward compatibility with the traditional
 .Nm ,
 .Cm sektion
 is supported as an alias for
 .Cm sec .
 .El
 .Ss Restricted character set
 For security reasons, in particular to prevent cross site scripting
 attacks, some strings used by
 .Nm
 can only contain the following characters:
 .Pp
 .Bl -dash -compact -offset indent
 .It
 lower case and upper case ASCII letters
 .It
 the ten decimal digits
 .It
 the dash
 .Pq Sq -
 .It
 the dot
 .Pq Sq \&.
 .It
 the slash
 .Pq Sq /
 .It
 the underscore
 .Pq Sq _
 .El
 .Pp
 In particular, this applies to the
 .Ev SCRIPT_NAME ,
 to all manpaths, and to all architecture names.
 .Sh ENVIRONMENT
 The web server may pass the following CGI variables to
 .Nm :
 .Bl -tag -width Ds
 .It Ev PATH_INFO
 The final part of the URI path passed from the client to the server,
 starting after the
 .Ev SCRIPT_NAME
 and ending before the
 .Ev QUERY_STRING .
 It is used by the
 .Cm show
-page to aquire the manpath and filename it needs.
+page to acquire the manpath and filename it needs.
 .It Ev QUERY_STRING
 The HTTP query string passed from the client to the server.
 It is the final part of the URI, after the question mark.
 It is used by the
 .Cm search
 page to acquire the named parameters it needs.
 .It Ev SCRIPT_NAME
 The path to the
 .Nm
 binary relative to the server root, usually
 .Pa /cgi-bin/man.cgi .
 This is used for generating URIs to be embedded
 in generated HTML code and HTTP headers.
 If this contains any character not contained in the
 .Sx Restricted character set ,
 .Nm
 reports an internal server error and exits without doing anything.
 .El
 .Sh FILES
 .Bl -tag -width Ds
 .It Pa /var/www
 Default web server
 .Xr chroot 2
 directory.
 All the following paths are specified relative to this directory.
 .It Pa /cgi-bin/man.cgi
 The path to the
 .Nm
 program relative to the server root.
 Can be overridden by
 .Ev SCRIPT_NAME .
 .It Pa /htdocs
 The path to the server document root relative to the server root.
 This is part of the web server configuration and not specific to
 .Nm .
 .It Pa /htdocs/man-cgi.css
 A style sheet for general
 .Nm
 styling, referenced from each generated HTML page.
 .It Pa /htdocs/man.css
 A style sheet for
 .Xr mandoc 1
 HTML styling, referenced from each generated HTML page after
 .Pa man-cgi.css .
 .It Pa /man
 Default
 .Nm
 data directory containing all the manual trees.
 Can be overridden by
 .Ev MAN_DIR .
 .It Pa /man/mandoc/man1/apropos.1 , /man/mandoc/man8/man.cgi.8
 Manual pages documenting
 .Nm
 itself, linked from the index page.
 .It Pa /man/manpath.conf
 The list of available manpaths, one per line.
 If any of the lines in this file contains a slash
 .Pq Sq /
 or any character not contained in the
 .Sx Restricted character set ,
 .Nm
 reports an internal server error and exits without doing anything.
 .It Pa /man/OpenBSD-current/man1/mandoc.1
 An example
 .Xr mdoc 7
 source file located below the
 .Dq OpenBSD-current
 manpath.
 .El
 .Sh COMPATIBILITY
 The
 .Nm
 CGI program is call-compatible with queries from the traditional
 .Pa man.cgi
 script by Wolfram Schneider.
 However, the output may not be quite the same.
 .Sh SEE ALSO
 .Xr apropos 1 ,
 .Xr mandoc.db 5 ,
 .Xr makewhatis 8 ,
 .Xr slowcgi 8
 .Sh HISTORY
 A version of
 .Nm
 based on
 .Xr mandoc 1
 first appeared in mdocml-1.12.1 (March 2012).
 The current SQLite3-based version first appeared in
 .Ox 5.6 .
 .Sh AUTHORS
 .An -nosplit
 The
 .Nm
 program was written by
 .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
 and ported to the SQLite3-based
 .Xr mandoc.db 5
 backend by
 .An Ingo Schwarze Aq Mt schwarze@openbsd.org .
Index: vendor/mdocml/dist/man.h
===================================================================
--- vendor/mdocml/dist/man.h	(revision 275396)
+++ vendor/mdocml/dist/man.h	(revision 275397)
@@ -1,121 +1,120 @@
-/*	$Id: man.h,v 1.65 2014/06/20 23:02:31 schwarze Exp $ */
+/*	$Id: man.h,v 1.66 2014/11/28 05:51:32 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef MAN_H
 #define MAN_H
 
 enum	mant {
 	MAN_br = 0,
 	MAN_TH,
 	MAN_SH,
 	MAN_SS,
 	MAN_TP,
 	MAN_LP,
 	MAN_PP,
 	MAN_P,
 	MAN_IP,
 	MAN_HP,
 	MAN_SM,
 	MAN_SB,
 	MAN_BI,
 	MAN_IB,
 	MAN_BR,
 	MAN_RB,
 	MAN_R,
 	MAN_B,
 	MAN_I,
 	MAN_IR,
 	MAN_RI,
 	MAN_na,
 	MAN_sp,
 	MAN_nf,
 	MAN_fi,
 	MAN_RE,
 	MAN_RS,
 	MAN_DT,
 	MAN_UC,
 	MAN_PD,
 	MAN_AT,
 	MAN_in,
 	MAN_ft,
 	MAN_OP,
 	MAN_EX,
 	MAN_EE,
 	MAN_UR,
 	MAN_UE,
 	MAN_ll,
 	MAN_MAX
 };
 
 enum	man_type {
 	MAN_TEXT,
 	MAN_ELEM,
 	MAN_ROOT,
 	MAN_BLOCK,
 	MAN_HEAD,
 	MAN_BODY,
-	MAN_TAIL,
 	MAN_TBL,
 	MAN_EQN
 };
 
 struct	man_meta {
 	char		*msec; /* `TH' section (1, 3p, etc.) */
 	char		*date; /* `TH' normalised date */
 	char		*vol; /* `TH' volume */
 	char		*title; /* `TH' title (e.g., FOO) */
 	char		*source; /* `TH' source (e.g., GNU) */
 	int		 hasbody; /* document is not empty */
 };
 
 struct	man_node {
 	struct man_node	*parent; /* parent AST node */
 	struct man_node	*child; /* first child AST node */
 	struct man_node	*next; /* sibling AST node */
 	struct man_node	*prev; /* prior sibling AST node */
 	int		 nchild; /* number children */
 	int		 line;
 	int		 pos;
 	enum mant	 tok; /* tok or MAN__MAX if none */
 	int		 flags;
 #define	MAN_VALID	(1 << 0) /* has been validated */
 #define	MAN_EOS		(1 << 2) /* at sentence boundary */
 #define	MAN_LINE	(1 << 3) /* first macro/text on line */
 	enum man_type	 type; /* AST node type */
 	char		*string; /* TEXT node argument */
 	struct man_node	*head; /* BLOCK node HEAD ptr */
 	struct man_node *tail; /* BLOCK node TAIL ptr */
 	struct man_node	*body; /* BLOCK node BODY ptr */
 	const struct tbl_span *span; /* TBL */
 	const struct eqn *eqn; /* EQN */
 };
 
 /* Names of macros.  Index is enum mant. */
 extern	const char *const *man_macronames;
 
 __BEGIN_DECLS
 
 struct	man;
 
 const struct man_node *man_node(const struct man *);
 const struct man_meta *man_meta(const struct man *);
 const struct mparse   *man_mparse(const struct man *);
 void man_deroff(char **, const struct man_node *);
 
 __END_DECLS
 
 #endif /*!MAN_H*/
Index: vendor/mdocml/dist/man_hash.c
===================================================================
--- vendor/mdocml/dist/man_hash.c	(revision 275396)
+++ vendor/mdocml/dist/man_hash.c	(revision 275397)
@@ -1,105 +1,103 @@
-/*	$Id: man_hash.c,v 1.27 2014/04/20 16:46:04 schwarze Exp $ */
+/*	$Id: man_hash.c,v 1.28 2014/08/10 23:54:41 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 
 #include "man.h"
 #include "mandoc.h"
 #include "libman.h"
 
 #define	HASH_DEPTH	 6
 
 #define	HASH_ROW(x) do { \
 		if (isupper((unsigned char)(x))) \
 			(x) -= 65; \
 		else \
 			(x) -= 97; \
 		(x) *= HASH_DEPTH; \
 	} while (/* CONSTCOND */ 0)
 
 /*
  * Lookup table is indexed first by lower-case first letter (plus one
  * for the period, which is stored in the last row), then by lower or
  * uppercase second letter.  Buckets correspond to the index of the
  * macro (the integer value of the enum stored as a char to save a bit
  * of space).
  */
 static	unsigned char	 table[26 * HASH_DEPTH];
 
 
 /*
  * XXX - this hash has global scope, so if intended for use as a library
  * with multiple callers, it will need re-invocation protection.
  */
 void
 man_hash_init(void)
 {
 	int		 i, j, x;
 
 	memset(table, UCHAR_MAX, sizeof(table));
 
 	assert(MAN_MAX < UCHAR_MAX);
 
 	for (i = 0; i < (int)MAN_MAX; i++) {
 		x = man_macronames[i][0];
 
 		assert(isalpha((unsigned char)x));
 
 		HASH_ROW(x);
 
 		for (j = 0; j < HASH_DEPTH; j++)
 			if (UCHAR_MAX == table[x + j]) {
 				table[x + j] = (unsigned char)i;
 				break;
 			}
 
 		assert(j < HASH_DEPTH);
 	}
 }
 
 enum mant
 man_hash_find(const char *tmp)
 {
 	int		 x, y, i;
 	enum mant	 tok;
 
 	if ('\0' == (x = tmp[0]))
 		return(MAN_MAX);
 	if ( ! (isalpha((unsigned char)x)))
 		return(MAN_MAX);
 
 	HASH_ROW(x);
 
 	for (i = 0; i < HASH_DEPTH; i++) {
 		if (UCHAR_MAX == (y = table[x + i]))
 			return(MAN_MAX);
 
 		tok = (enum mant)y;
 		if (0 == strcmp(tmp, man_macronames[tok]))
 			return(tok);
 	}
 
 	return(MAN_MAX);
 }
Index: vendor/mdocml/dist/man_html.c
===================================================================
--- vendor/mdocml/dist/man_html.c	(revision 275396)
+++ vendor/mdocml/dist/man_html.c	(revision 275397)
@@ -1,698 +1,683 @@
-/*	$Id: man_html.c,v 1.96 2014/08/01 19:25:52 schwarze Exp $ */
+/*	$Id: man_html.c,v 1.104 2014/09/27 11:17:19 kristaps Exp $ */
 /*
- * Copyright (c) 2008-2012 Kristaps Dzonsons 
+ * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons 
  * Copyright (c) 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "out.h"
 #include "html.h"
 #include "man.h"
 #include "main.h"
 
 /* TODO: preserve ident widths. */
 /* FIXME: have PD set the default vspace width. */
 
 #define	INDENT		  5
 
 #define	MAN_ARGS	  const struct man_meta *man, \
 			  const struct man_node *n, \
 			  struct mhtml *mh, \
 			  struct html *h
 
 struct	mhtml {
 	int		  fl;
 #define	MANH_LITERAL	 (1 << 0) /* literal context */
 };
 
 struct	htmlman {
 	int		(*pre)(MAN_ARGS);
 	int		(*post)(MAN_ARGS);
 };
 
 static	void		  print_bvspace(struct html *,
 				const struct man_node *);
 static	void		  print_man(MAN_ARGS);
 static	void		  print_man_head(MAN_ARGS);
 static	void		  print_man_nodelist(MAN_ARGS);
 static	void		  print_man_node(MAN_ARGS);
 static	int		  a2width(const struct man_node *,
 				struct roffsu *);
 static	int		  man_B_pre(MAN_ARGS);
 static	int		  man_HP_pre(MAN_ARGS);
 static	int		  man_IP_pre(MAN_ARGS);
 static	int		  man_I_pre(MAN_ARGS);
 static	int		  man_OP_pre(MAN_ARGS);
 static	int		  man_PP_pre(MAN_ARGS);
 static	int		  man_RS_pre(MAN_ARGS);
 static	int		  man_SH_pre(MAN_ARGS);
 static	int		  man_SM_pre(MAN_ARGS);
 static	int		  man_SS_pre(MAN_ARGS);
 static	int		  man_UR_pre(MAN_ARGS);
 static	int		  man_alt_pre(MAN_ARGS);
 static	int		  man_br_pre(MAN_ARGS);
 static	int		  man_ign_pre(MAN_ARGS);
 static	int		  man_in_pre(MAN_ARGS);
 static	int		  man_literal_pre(MAN_ARGS);
 static	void		  man_root_post(MAN_ARGS);
 static	void		  man_root_pre(MAN_ARGS);
 
 static	const struct htmlman mans[MAN_MAX] = {
 	{ man_br_pre, NULL }, /* br */
 	{ NULL, NULL }, /* TH */
 	{ man_SH_pre, NULL }, /* SH */
 	{ man_SS_pre, NULL }, /* SS */
 	{ man_IP_pre, NULL }, /* TP */
 	{ man_PP_pre, NULL }, /* LP */
 	{ man_PP_pre, NULL }, /* PP */
 	{ man_PP_pre, NULL }, /* P */
 	{ man_IP_pre, NULL }, /* IP */
 	{ man_HP_pre, NULL }, /* HP */
 	{ man_SM_pre, NULL }, /* SM */
 	{ man_SM_pre, NULL }, /* SB */
 	{ man_alt_pre, NULL }, /* BI */
 	{ man_alt_pre, NULL }, /* IB */
 	{ man_alt_pre, NULL }, /* BR */
 	{ man_alt_pre, NULL }, /* RB */
 	{ NULL, NULL }, /* R */
 	{ man_B_pre, NULL }, /* B */
 	{ man_I_pre, NULL }, /* I */
 	{ man_alt_pre, NULL }, /* IR */
 	{ man_alt_pre, NULL }, /* RI */
 	{ man_ign_pre, NULL }, /* na */
 	{ man_br_pre, NULL }, /* sp */
 	{ man_literal_pre, NULL }, /* nf */
 	{ man_literal_pre, NULL }, /* fi */
 	{ NULL, NULL }, /* RE */
 	{ man_RS_pre, NULL }, /* RS */
 	{ man_ign_pre, NULL }, /* DT */
 	{ man_ign_pre, NULL }, /* UC */
 	{ man_ign_pre, NULL }, /* PD */
 	{ man_ign_pre, NULL }, /* AT */
 	{ 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 */
 	{ man_ign_pre, NULL }, /* ll */
 };
 
 
 /*
  * Printing leading vertical space before a block.
  * This is used for the paragraph macros.
  * The rules are pretty simple, since there's very little nesting going
  * on here.  Basically, if we're the first within another block (SS/SH),
  * then don't emit vertical space.  If we are (RS), then do.  If not the
  * first, print it.
  */
 static void
 print_bvspace(struct html *h, const struct man_node *n)
 {
 
 	if (n->body && n->body->child)
 		if (MAN_TBL == n->body->child->type)
 			return;
 
 	if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
 		if (NULL == n->prev)
 			return;
 
-	print_otag(h, TAG_P, 0, NULL);
+	print_paragraph(h);
 }
 
 void
 html_man(void *arg, const struct man *man)
 {
 	struct mhtml	 mh;
 
 	memset(&mh, 0, sizeof(struct mhtml));
 	print_man(man_meta(man), man_node(man), &mh, (struct html *)arg);
 	putchar('\n');
 }
 
 static void
 print_man(MAN_ARGS)
 {
 	struct tag	*t, *tt;
 	struct htmlpair	 tag;
 
 	PAIR_CLASS_INIT(&tag, "mandoc");
 
 	if ( ! (HTML_FRAGMENT & h->oflags)) {
 		print_gen_decls(h);
 		t = print_otag(h, TAG_HTML, 0, NULL);
 		tt = print_otag(h, TAG_HEAD, 0, NULL);
 		print_man_head(man, n, mh, h);
 		print_tagq(h, tt);
 		print_otag(h, TAG_BODY, 0, NULL);
 		print_otag(h, TAG_DIV, 1, &tag);
 	} else
 		t = print_otag(h, TAG_DIV, 1, &tag);
 
 	print_man_nodelist(man, n, mh, h);
 	print_tagq(h, t);
 }
 
 static void
 print_man_head(MAN_ARGS)
 {
 
 	print_gen_head(h);
 	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);
 }
 
 static void
 print_man_nodelist(MAN_ARGS)
 {
 
 	print_man_node(man, n, mh, h);
 	if (n->next)
 		print_man_nodelist(man, n->next, mh, h);
 }
 
 static void
 print_man_node(MAN_ARGS)
 {
 	int		 child;
 	struct tag	*t;
 
 	child = 1;
 	t = h->tags.head;
 
 	switch (n->type) {
 	case MAN_ROOT:
 		man_root_pre(man, n, mh, h);
 		break;
 	case MAN_TEXT:
 		/*
 		 * If we have a blank line, output a vertical space.
 		 * If we have a space as the first character, break
 		 * before printing the line's data.
 		 */
 		if ('\0' == *n->string) {
-			print_otag(h, TAG_P, 0, NULL);
+			print_paragraph(h);
 			return;
 		}
 
 		if (' ' == *n->string && MAN_LINE & n->flags)
 			print_otag(h, TAG_BR, 0, NULL);
 		else if (MANH_LITERAL & mh->fl && n->prev)
 			print_otag(h, TAG_BR, 0, NULL);
 
 		print_text(h, n->string);
 		return;
 	case MAN_EQN:
 		print_eqn(h, n->eqn);
 		break;
 	case MAN_TBL:
 		/*
 		 * This will take care of initialising all of the table
 		 * state data for the first table, then tearing it down
 		 * for the last one.
 		 */
 		print_tbl(h, n->span);
 		return;
 	default:
 		/*
 		 * Close out scope of font prior to opening a macro
 		 * scope.
 		 */
 		if (HTMLFONT_NONE != h->metac) {
 			h->metal = h->metac;
 			h->metac = HTMLFONT_NONE;
 		}
 
 		/*
 		 * Close out the current table, if it's open, and unset
 		 * the "meta" table state.  This will be reopened on the
 		 * next table element.
 		 */
 		if (h->tblt) {
 			print_tblclose(h);
 			t = h->tags.head;
 		}
 		if (mans[n->tok].pre)
 			child = (*mans[n->tok].pre)(man, n, mh, h);
 		break;
 	}
 
 	if (child && n->child)
 		print_man_nodelist(man, n->child, mh, h);
 
 	/* This will automatically close out any font scope. */
 	print_stagq(h, t);
 
 	switch (n->type) {
 	case MAN_ROOT:
 		man_root_post(man, n, mh, h);
 		break;
 	case MAN_EQN:
 		break;
 	default:
 		if (mans[n->tok].post)
 			(*mans[n->tok].post)(man, n, mh, h);
 		break;
 	}
 }
 
 static int
 a2width(const struct man_node *n, struct roffsu *su)
 {
 
 	if (MAN_TEXT != n->type)
 		return(0);
 	if (a2roffsu(n->string, su, SCALE_BU))
 		return(1);
 
 	return(0);
 }
 
 static void
 man_root_pre(MAN_ARGS)
 {
-	struct htmlpair	 tag[3];
+	struct htmlpair	 tag;
 	struct tag	*t, *tt;
 	char		*title;
 
 	assert(man->title);
 	assert(man->msec);
 	mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
 
-	PAIR_SUMMARY_INIT(&tag[0], "Document Header");
-	PAIR_CLASS_INIT(&tag[1], "head");
-	PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
-	t = print_otag(h, TAG_TABLE, 3, tag);
-	PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
-	print_otag(h, TAG_COL, 1, tag);
-	print_otag(h, TAG_COL, 1, tag);
-	print_otag(h, TAG_COL, 1, tag);
+	PAIR_CLASS_INIT(&tag, "head");
+	t = print_otag(h, TAG_TABLE, 1, &tag);
 
 	print_otag(h, TAG_TBODY, 0, NULL);
 
 	tt = print_otag(h, TAG_TR, 0, NULL);
 
-	PAIR_CLASS_INIT(&tag[0], "head-ltitle");
-	print_otag(h, TAG_TD, 1, tag);
+	PAIR_CLASS_INIT(&tag, "head-ltitle");
+	print_otag(h, TAG_TD, 1, &tag);
 	print_text(h, title);
 	print_stagq(h, tt);
 
-	PAIR_CLASS_INIT(&tag[0], "head-vol");
-	PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
-	print_otag(h, TAG_TD, 2, tag);
+	PAIR_CLASS_INIT(&tag, "head-vol");
+	print_otag(h, TAG_TD, 1, &tag);
 	if (NULL != man->vol)
 		print_text(h, man->vol);
 	print_stagq(h, tt);
 
-	PAIR_CLASS_INIT(&tag[0], "head-rtitle");
-	PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
-	print_otag(h, TAG_TD, 2, tag);
+	PAIR_CLASS_INIT(&tag, "head-rtitle");
+	print_otag(h, TAG_TD, 1, &tag);
 	print_text(h, title);
 	print_tagq(h, t);
 	free(title);
 }
 
 static void
 man_root_post(MAN_ARGS)
 {
-	struct htmlpair	 tag[3];
+	struct htmlpair	 tag;
 	struct tag	*t, *tt;
 
-	PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
-	PAIR_CLASS_INIT(&tag[1], "foot");
-	PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
-	t = print_otag(h, TAG_TABLE, 3, tag);
-	PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
-	print_otag(h, TAG_COL, 1, tag);
-	print_otag(h, TAG_COL, 1, tag);
+	PAIR_CLASS_INIT(&tag, "foot");
+	t = print_otag(h, TAG_TABLE, 1, &tag);
 
 	tt = print_otag(h, TAG_TR, 0, NULL);
 
-	PAIR_CLASS_INIT(&tag[0], "foot-date");
-	print_otag(h, TAG_TD, 1, tag);
+	PAIR_CLASS_INIT(&tag, "foot-date");
+	print_otag(h, TAG_TD, 1, &tag);
 
 	assert(man->date);
 	print_text(h, man->date);
 	print_stagq(h, tt);
 
-	PAIR_CLASS_INIT(&tag[0], "foot-os");
-	PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
-	print_otag(h, TAG_TD, 2, tag);
+	PAIR_CLASS_INIT(&tag, "foot-os");
+	print_otag(h, TAG_TD, 1, &tag);
 
 	if (man->source)
 		print_text(h, man->source);
 	print_tagq(h, t);
 }
 
 
 static int
 man_br_pre(MAN_ARGS)
 {
 	struct roffsu	 su;
 	struct htmlpair	 tag;
 
 	SCALE_VS_INIT(&su, 1);
 
 	if (MAN_sp == n->tok) {
 		if (NULL != (n = n->child))
 			if ( ! a2roffsu(n->string, &su, SCALE_VS))
 				SCALE_VS_INIT(&su, atoi(n->string));
 	} else
 		su.scale = 0.0;
 
 	bufinit(h);
 	bufcat_su(h, "height", &su);
 	PAIR_STYLE_INIT(&tag, h);
 	print_otag(h, TAG_DIV, 1, &tag);
 
 	/* So the div isn't empty: */
 	print_text(h, "\\~");
 
 	return(0);
 }
 
 static int
 man_SH_pre(MAN_ARGS)
 {
 	struct htmlpair	 tag;
 
 	if (MAN_BLOCK == n->type) {
 		mh->fl &= ~MANH_LITERAL;
 		PAIR_CLASS_INIT(&tag, "section");
 		print_otag(h, TAG_DIV, 1, &tag);
 		return(1);
 	} else if (MAN_BODY == n->type)
 		return(1);
 
 	print_otag(h, TAG_H1, 0, NULL);
 	return(1);
 }
 
 static int
 man_alt_pre(MAN_ARGS)
 {
 	const struct man_node	*nn;
 	int		 i, savelit;
 	enum htmltag	 fp;
 	struct tag	*t;
 
 	if ((savelit = mh->fl & MANH_LITERAL))
 		print_otag(h, TAG_BR, 0, NULL);
 
 	mh->fl &= ~MANH_LITERAL;
 
 	for (i = 0, nn = n->child; nn; nn = nn->next, i++) {
 		t = NULL;
 		switch (n->tok) {
 		case MAN_BI:
 			fp = i % 2 ? TAG_I : TAG_B;
 			break;
 		case MAN_IB:
 			fp = i % 2 ? TAG_B : TAG_I;
 			break;
 		case MAN_RI:
 			fp = i % 2 ? TAG_I : TAG_MAX;
 			break;
 		case MAN_IR:
 			fp = i % 2 ? TAG_MAX : TAG_I;
 			break;
 		case MAN_BR:
 			fp = i % 2 ? TAG_MAX : TAG_B;
 			break;
 		case MAN_RB:
 			fp = i % 2 ? TAG_B : TAG_MAX;
 			break;
 		default:
 			abort();
 			/* NOTREACHED */
 		}
 
 		if (i)
 			h->flags |= HTML_NOSPACE;
 
 		if (TAG_MAX != fp)
 			t = print_otag(h, fp, 0, NULL);
 
 		print_man_node(man, nn, mh, h);
 
 		if (t)
 			print_tagq(h, t);
 	}
 
 	if (savelit)
 		mh->fl |= MANH_LITERAL;
 
 	return(0);
 }
 
 static int
 man_SM_pre(MAN_ARGS)
 {
 
 	print_otag(h, TAG_SMALL, 0, NULL);
 	if (MAN_SB == n->tok)
 		print_otag(h, TAG_B, 0, NULL);
 	return(1);
 }
 
 static int
 man_SS_pre(MAN_ARGS)
 {
 	struct htmlpair	 tag;
 
 	if (MAN_BLOCK == n->type) {
 		mh->fl &= ~MANH_LITERAL;
 		PAIR_CLASS_INIT(&tag, "subsection");
 		print_otag(h, TAG_DIV, 1, &tag);
 		return(1);
 	} else if (MAN_BODY == n->type)
 		return(1);
 
 	print_otag(h, TAG_H2, 0, NULL);
 	return(1);
 }
 
 static int
 man_PP_pre(MAN_ARGS)
 {
 
 	if (MAN_HEAD == n->type)
 		return(0);
 	else if (MAN_BLOCK == n->type)
 		print_bvspace(h, n);
 
 	return(1);
 }
 
 static int
 man_IP_pre(MAN_ARGS)
 {
 	const struct man_node	*nn;
 
 	if (MAN_BODY == n->type) {
 		print_otag(h, TAG_DD, 0, NULL);
 		return(1);
 	} else if (MAN_HEAD != n->type) {
 		print_otag(h, TAG_DL, 0, NULL);
 		return(1);
 	}
 
 	/* FIXME: width specification. */
 
 	print_otag(h, TAG_DT, 0, NULL);
 
 	/* For IP, only print the first header element. */
 
 	if (MAN_IP == n->tok && n->child)
 		print_man_node(man, n->child, mh, h);
 
 	/* For TP, only print next-line header elements. */
 
 	if (MAN_TP == n->tok) {
 		nn = n->child;
 		while (NULL != nn && 0 == (MAN_LINE & nn->flags))
 			nn = nn->next;
 		while (NULL != nn) {
 			print_man_node(man, nn, mh, h);
 			nn = nn->next;
 		}
 	}
 
 	return(0);
 }
 
 static int
 man_HP_pre(MAN_ARGS)
 {
-	struct htmlpair	 tag;
+	struct htmlpair	 tag[2];
 	struct roffsu	 su;
 	const struct man_node *np;
 
 	if (MAN_HEAD == n->type)
 		return(0);
 	else if (MAN_BLOCK != n->type)
 		return(1);
 
 	np = n->head->child;
 
 	if (NULL == np || ! a2width(np, &su))
 		SCALE_HS_INIT(&su, INDENT);
 
 	bufinit(h);
 
 	print_bvspace(h, n);
 	bufcat_su(h, "margin-left", &su);
 	su.scale = -su.scale;
 	bufcat_su(h, "text-indent", &su);
-	PAIR_STYLE_INIT(&tag, h);
-	print_otag(h, TAG_P, 1, &tag);
+	PAIR_STYLE_INIT(&tag[0], h);
+	PAIR_CLASS_INIT(&tag[1], "spacer");
+	print_otag(h, TAG_DIV, 2, tag);
 	return(1);
 }
 
 static int
 man_OP_pre(MAN_ARGS)
 {
 	struct tag	*tt;
 	struct htmlpair	 tag;
 
 	print_text(h, "[");
 	h->flags |= HTML_NOSPACE;
 	PAIR_CLASS_INIT(&tag, "opt");
 	tt = print_otag(h, TAG_SPAN, 1, &tag);
 
 	if (NULL != (n = n->child)) {
 		print_otag(h, TAG_B, 0, NULL);
 		print_text(h, n->string);
 	}
 
 	print_stagq(h, tt);
 
 	if (NULL != n && NULL != n->next) {
 		print_otag(h, TAG_I, 0, NULL);
 		print_text(h, n->next->string);
 	}
 
 	print_stagq(h, tt);
 	h->flags |= HTML_NOSPACE;
 	print_text(h, "]");
 	return(0);
 }
 
 static int
 man_B_pre(MAN_ARGS)
 {
 
 	print_otag(h, TAG_B, 0, NULL);
 	return(1);
 }
 
 static int
 man_I_pre(MAN_ARGS)
 {
 
 	print_otag(h, TAG_I, 0, NULL);
 	return(1);
 }
 
 static int
 man_literal_pre(MAN_ARGS)
 {
 
 	if (MAN_fi == n->tok || MAN_EE == n->tok) {
 		print_otag(h, TAG_BR, 0, NULL);
 		mh->fl &= ~MANH_LITERAL;
 	} else
 		mh->fl |= MANH_LITERAL;
 
 	return(0);
 }
 
 static int
 man_in_pre(MAN_ARGS)
 {
 
 	print_otag(h, TAG_BR, 0, NULL);
 	return(0);
 }
 
 static int
 man_ign_pre(MAN_ARGS)
 {
 
 	return(0);
 }
 
 static int
 man_RS_pre(MAN_ARGS)
 {
 	struct htmlpair	 tag;
 	struct roffsu	 su;
 
 	if (MAN_HEAD == n->type)
 		return(0);
 	else if (MAN_BODY == n->type)
 		return(1);
 
 	SCALE_HS_INIT(&su, INDENT);
 	if (n->head->child)
 		a2width(n->head->child, &su);
 
 	bufinit(h);
 	bufcat_su(h, "margin-left", &su);
 	PAIR_STYLE_INIT(&tag, h);
 	print_otag(h, TAG_DIV, 1, &tag);
 	return(1);
 }
 
 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);
 }
Index: vendor/mdocml/dist/man_macro.c
===================================================================
--- vendor/mdocml/dist/man_macro.c	(revision 275396)
+++ vendor/mdocml/dist/man_macro.c	(revision 275397)
@@ -1,492 +1,473 @@
-/*	$Id: man_macro.c,v 1.87 2014/07/30 23:01:39 schwarze Exp $ */
+/*	$Id: man_macro.c,v 1.91 2014/11/28 05:51:32 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2012, 2013 Ingo Schwarze 
+ * Copyright (c) 2012, 2013, 2014 Ingo Schwarze 
  * Copyright (c) 2013 Franco Fichtner 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 #include 
 
 #include "man.h"
 #include "mandoc.h"
 #include "libmandoc.h"
 #include "libman.h"
 
 enum	rew {
 	REW_REWIND,
 	REW_NOHALT,
 	REW_HALT
 };
 
-static	int		 blk_close(MACRO_PROT_ARGS);
-static	int		 blk_exp(MACRO_PROT_ARGS);
-static	int		 blk_imp(MACRO_PROT_ARGS);
-static	int		 in_line_eoln(MACRO_PROT_ARGS);
+static	void		 blk_close(MACRO_PROT_ARGS);
+static	void		 blk_exp(MACRO_PROT_ARGS);
+static	void		 blk_imp(MACRO_PROT_ARGS);
+static	void		 in_line_eoln(MACRO_PROT_ARGS);
 static	int		 man_args(struct man *, int,
 				int *, char *, char **);
 
-static	int		 rew_scope(enum man_type,
+static	void		 rew_scope(enum man_type,
 				struct man *, enum mant);
 static	enum rew	 rew_dohalt(enum mant, enum man_type,
 				const struct man_node *);
 static	enum rew	 rew_block(enum mant, enum man_type,
 				const struct man_node *);
 
 const	struct man_macro __man_macros[MAN_MAX] = {
 	{ in_line_eoln, MAN_NSCOPED }, /* br */
 	{ in_line_eoln, MAN_BSCOPE }, /* TH */
 	{ blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SH */
 	{ blk_imp, MAN_BSCOPE | MAN_SCOPED }, /* SS */
 	{ blk_imp, MAN_BSCOPE | MAN_SCOPED | MAN_FSCOPED }, /* TP */
 	{ blk_imp, MAN_BSCOPE }, /* LP */
 	{ blk_imp, MAN_BSCOPE }, /* PP */
 	{ blk_imp, MAN_BSCOPE }, /* P */
 	{ blk_imp, MAN_BSCOPE }, /* IP */
 	{ blk_imp, MAN_BSCOPE }, /* HP */
-	{ in_line_eoln, MAN_SCOPED }, /* SM */
-	{ in_line_eoln, MAN_SCOPED }, /* SB */
+	{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SM */
+	{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* SB */
 	{ in_line_eoln, 0 }, /* BI */
 	{ in_line_eoln, 0 }, /* IB */
 	{ in_line_eoln, 0 }, /* BR */
 	{ in_line_eoln, 0 }, /* RB */
-	{ in_line_eoln, MAN_SCOPED }, /* R */
-	{ in_line_eoln, MAN_SCOPED }, /* B */
-	{ in_line_eoln, MAN_SCOPED }, /* I */
+	{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* R */
+	{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* B */
+	{ in_line_eoln, MAN_SCOPED | MAN_JOIN }, /* I */
 	{ in_line_eoln, 0 }, /* IR */
 	{ in_line_eoln, 0 }, /* RI */
 	{ in_line_eoln, MAN_NSCOPED }, /* na */
 	{ in_line_eoln, MAN_NSCOPED }, /* sp */
 	{ in_line_eoln, MAN_BSCOPE }, /* nf */
 	{ in_line_eoln, MAN_BSCOPE }, /* fi */
 	{ blk_close, 0 }, /* RE */
 	{ blk_exp, MAN_BSCOPE | MAN_EXPLICIT }, /* RS */
 	{ in_line_eoln, 0 }, /* DT */
 	{ in_line_eoln, 0 }, /* UC */
 	{ in_line_eoln, 0 }, /* PD */
 	{ in_line_eoln, 0 }, /* AT */
 	{ 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 */
 	{ in_line_eoln, 0 }, /* ll */
 };
 
 const	struct man_macro * const man_macros = __man_macros;
 
 
-int
+void
 man_unscope(struct man *man, const struct man_node *to)
 {
 	struct man_node	*n;
 
-	man->next = MAN_NEXT_SIBLING;
 	to = to->parent;
 	n = man->last;
 	while (n != to) {
 
 		/* Reached the end of the document? */
 
 		if (to == NULL && ! (n->flags & MAN_VALID)) {
 			if (man->flags & (MAN_BLINE | MAN_ELINE) &&
 			    man_macros[n->tok].flags & MAN_SCOPED) {
 				mandoc_vmsg(MANDOCERR_BLK_LINE,
 				    man->parse, n->line, n->pos,
 				    "EOF breaks %s",
 				    man_macronames[n->tok]);
 				if (man->flags & MAN_ELINE)
 					man->flags &= ~MAN_ELINE;
 				else {
 					assert(n->type == MAN_HEAD);
 					n = n->parent;
 					man->flags &= ~MAN_BLINE;
 				}
 				man->last = n;
 				n = n->parent;
 				man_node_delete(man, man->last);
 				continue;
 			}
 			if (n->type == MAN_BLOCK &&
 			    man_macros[n->tok].flags & MAN_EXPLICIT)
 				mandoc_msg(MANDOCERR_BLK_NOEND,
 				    man->parse, n->line, n->pos,
 				    man_macronames[n->tok]);
 		}
 
 		/*
 		 * We might delete the man->last node
 		 * in the post-validation phase.
 		 * Save a pointer to the parent such that
 		 * we know where to continue the iteration.
 		 */
+
 		man->last = n;
 		n = n->parent;
-		if ( ! man_valid_post(man))
-			return(0);
+		man_valid_post(man);
 	}
-	return(1);
+
+	/*
+	 * If we ended up at the parent of the node we were
+	 * supposed to rewind to, that means the target node
+	 * got deleted, so add the next node we parse as a child
+	 * of the parent instead of as a sibling of the target.
+	 */
+
+	man->next = (man->last == to) ?
+	    MAN_NEXT_CHILD : MAN_NEXT_SIBLING;
 }
 
 static enum rew
 rew_block(enum mant ntok, enum man_type type, const struct man_node *n)
 {
 
-	if (MAN_BLOCK == type && ntok == n->parent->tok &&
-	    MAN_BODY == n->parent->type)
+	if (type == MAN_BLOCK && ntok == n->parent->tok &&
+	    n->parent->type == MAN_BODY)
 		return(REW_REWIND);
 	return(ntok == n->tok ? REW_HALT : REW_NOHALT);
 }
 
 /*
  * There are three scope levels: scoped to the root (all), scoped to the
  * section (all less sections), and scoped to subsections (all less
  * sections and subsections).
  */
 static enum rew
 rew_dohalt(enum mant tok, enum man_type type, const struct man_node *n)
 {
 	enum rew	 c;
 
 	/* We cannot progress beyond the root ever. */
 	if (MAN_ROOT == n->type)
 		return(REW_HALT);
 
 	assert(n->parent);
 
 	/* Normal nodes shouldn't go to the level of the root. */
 	if (MAN_ROOT == n->parent->type)
 		return(REW_REWIND);
 
 	/* Already-validated nodes should be closed out. */
 	if (MAN_VALID & n->flags)
 		return(REW_NOHALT);
 
 	/* First: rewind to ourselves. */
 	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:
 	 * section, sub-section, etc.
 	 */
 
 	switch (tok) {
 	case MAN_SH:
 		break;
 	case MAN_SS:
 		/* Rewind to a section, if a block. */
 		if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
 			return(c);
 		break;
 	case MAN_RS:
 		/* Preserve empty paragraphs before RS. */
 		if (0 == n->nchild && (MAN_P == n->tok ||
 		    MAN_PP == n->tok || MAN_LP == n->tok))
 			return(REW_HALT);
 		/* Rewind to a subsection, if a block. */
 		if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
 			return(c);
 		/* Rewind to a section, if a block. */
 		if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
 			return(c);
 		break;
 	default:
 		/* Rewind to an offsetter, if a block. */
 		if (REW_NOHALT != (c = rew_block(MAN_RS, type, n)))
 			return(c);
 		/* Rewind to a subsection, if a block. */
 		if (REW_NOHALT != (c = rew_block(MAN_SS, type, n)))
 			return(c);
 		/* Rewind to a section, if a block. */
 		if (REW_NOHALT != (c = rew_block(MAN_SH, type, n)))
 			return(c);
 		break;
 	}
 
 	return(REW_NOHALT);
 }
 
 /*
  * Rewinding entails ascending the parse tree until a coherent point,
  * for example, the `SH' macro will close out any intervening `SS'
  * scopes.  When a scope is closed, it must be validated and actioned.
  */
-static int
+static void
 rew_scope(enum man_type type, struct man *man, enum mant tok)
 {
 	struct man_node	*n;
 	enum rew	 c;
 
 	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
 		 * rewinding (REW_NOHALT).
 		 */
 		c = rew_dohalt(tok, type, n);
 		if (REW_HALT == c)
-			return(1);
+			return;
 		if (REW_REWIND == c)
 			break;
 	}
 
 	/*
 	 * Rewind until the current point.  Warn if we're a roff
 	 * instruction that's mowing over explicit scopes.
 	 */
-	assert(n);
 
-	return(man_unscope(man, n));
+	man_unscope(man, n);
 }
 
 
 /*
  * Close out a generic explicit macro.
  */
-int
+void
 blk_close(MACRO_PROT_ARGS)
 {
 	enum mant		 ntok;
 	const struct man_node	*nn;
 
 	switch (tok) {
 	case MAN_RE:
 		ntok = MAN_RS;
 		break;
 	case MAN_UE:
 		ntok = MAN_UR;
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	for (nn = man->last->parent; nn; nn = nn->parent)
-		if (ntok == nn->tok && MAN_BLOCK == nn->type)
+		if (nn->tok == ntok && nn->type == MAN_BLOCK)
 			break;
 
-	if (NULL == nn) {
+	if (nn == NULL) {
 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, man->parse,
 		    line, ppos, man_macronames[tok]);
-		if ( ! rew_scope(MAN_BLOCK, man, MAN_PP))
-			return(0);
+		rew_scope(MAN_BLOCK, man, MAN_PP);
 	} else
 		man_unscope(man, nn);
-
-	return(1);
 }
 
-int
+void
 blk_exp(MACRO_PROT_ARGS)
 {
 	struct man_node	*n;
 	int		 la;
 	char		*p;
 
-	/* Close out prior implicit scopes. */
+	rew_scope(MAN_BLOCK, man, tok);
+	man_block_alloc(man, line, ppos, tok);
+	man_head_alloc(man, line, ppos, tok);
 
-	if ( ! rew_scope(MAN_BLOCK, man, tok))
-		return(0);
-
-	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(man, line, pos, buf, &p))
 			break;
-		if ( ! man_word_alloc(man, line, la, p))
-			return(0);
+		man_word_alloc(man, line, la, p);
 	}
 
 	assert(man);
 	assert(tok != MAN_MAX);
 
-	for (n = man->last; n; n = n->parent) {
-		if (n->tok != tok)
-			continue;
-		assert(MAN_HEAD == n->type);
-		man_unscope(man, n);
-		break;
-	}
+	for (n = man->last; n; n = n->parent)
+		if (n->tok == tok) {
+			assert(n->type == MAN_HEAD);
+			man_unscope(man, n);
+			break;
+		}
 
-	return(man_body_alloc(man, line, ppos, tok));
+	man_body_alloc(man, line, ppos, tok);
 }
 
 /*
  * Parse an implicit-block macro.  These contain a MAN_HEAD and a
  * MAN_BODY contained within a MAN_BLOCK.  Rules for closing out other
  * scopes, such as `SH' closing out an `SS', are defined in the rew
  * routines.
  */
-int
+void
 blk_imp(MACRO_PROT_ARGS)
 {
 	int		 la;
 	char		*p;
 	struct man_node	*n;
 
-	/* Close out prior scopes. */
-
-	if ( ! rew_scope(MAN_BODY, man, tok))
-		return(0);
-	if ( ! rew_scope(MAN_BLOCK, man, tok))
-		return(0);
-
-	/* Allocate new block & head scope. */
-
-	if ( ! man_block_alloc(man, line, ppos, tok))
-		return(0);
-	if ( ! man_head_alloc(man, line, ppos, tok))
-		return(0);
-
+	rew_scope(MAN_BODY, man, tok);
+	rew_scope(MAN_BLOCK, man, tok);
+	man_block_alloc(man, line, ppos, tok);
+	man_head_alloc(man, line, ppos, tok);
 	n = man->last;
 
 	/* Add line arguments. */
 
 	for (;;) {
 		la = *pos;
 		if ( ! man_args(man, line, pos, buf, &p))
 			break;
-		if ( ! man_word_alloc(man, line, la, p))
-			return(0);
+		man_word_alloc(man, line, la, p);
 	}
 
 	/* Close out head and open body (unless MAN_SCOPE). */
 
-	if (MAN_SCOPED & man_macros[tok].flags) {
+	if (man_macros[tok].flags & MAN_SCOPED) {
 		/* If we're forcing scope (`TP'), keep it open. */
-		if (MAN_FSCOPED & man_macros[tok].flags) {
+		if (man_macros[tok].flags & MAN_FSCOPED) {
 			man->flags |= MAN_BLINE;
-			return(1);
+			return;
 		} else if (n == man->last) {
 			man->flags |= MAN_BLINE;
-			return(1);
+			return;
 		}
 	}
-
-	if ( ! rew_scope(MAN_HEAD, man, tok))
-		return(0);
-	return(man_body_alloc(man, line, ppos, tok));
+	rew_scope(MAN_HEAD, man, tok);
+	man_body_alloc(man, line, ppos, tok);
 }
 
-int
+void
 in_line_eoln(MACRO_PROT_ARGS)
 {
 	int		 la;
 	char		*p;
 	struct man_node	*n;
 
-	if ( ! man_elem_alloc(man, line, ppos, tok))
-		return(0);
-
+	man_elem_alloc(man, line, ppos, tok);
 	n = man->last;
 
 	for (;;) {
 		la = *pos;
 		if ( ! man_args(man, line, pos, buf, &p))
 			break;
-		if ( ! man_word_alloc(man, line, la, p))
-			return(0);
+		if (man_macros[tok].flags & MAN_JOIN &&
+		    man->last->type == MAN_TEXT)
+			man_word_append(man, p);
+		else
+			man_word_alloc(man, line, la, p);
 	}
 
 	/*
 	 * 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)))
 		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 == man->last && MAN_SCOPED & man_macros[tok].flags) {
-		assert( ! (MAN_NSCOPED & man_macros[tok].flags));
+	if (n == man->last && man_macros[tok].flags & MAN_SCOPED) {
+		assert( ! (man_macros[tok].flags & MAN_NSCOPED));
 		man->flags |= MAN_ELINE;
-		return(1);
+		return;
 	}
 
-	assert(MAN_ROOT != man->last->type);
+	assert(man->last->type != MAN_ROOT);
 	man->next = MAN_NEXT_SIBLING;
 
 	/*
 	 * Rewind our element scope.  Note that when TH is pruned, we'll
 	 * be back at the root, so make sure that we don't clobber as
 	 * its sibling.
 	 */
 
 	for ( ; man->last; man->last = man->last->parent) {
 		if (man->last == n)
 			break;
 		if (man->last->type == MAN_ROOT)
 			break;
-		if ( ! man_valid_post(man))
-			return(0);
+		man_valid_post(man);
 	}
 
 	assert(man->last);
 
 	/*
 	 * Same here regarding whether we're back at the root.
 	 */
 
-	if (man->last->type != MAN_ROOT && ! man_valid_post(man))
-		return(0);
-
-	return(1);
+	if (man->last->type != MAN_ROOT)
+		man_valid_post(man);
 }
 
 
-int
+void
 man_macroend(struct man *man)
 {
 
-	return(man_unscope(man, man->first));
+	man_unscope(man, man->first);
 }
 
 static int
 man_args(struct man *man, int line, int *pos, char *buf, char **v)
 {
 	char	 *start;
 
 	assert(*pos);
 	*v = start = buf + *pos;
 	assert(' ' != *start);
 
 	if ('\0' == *start)
 		return(0);
 
 	*v = mandoc_getarg(man->parse, v, line, pos);
 	return(1);
 }
Index: vendor/mdocml/dist/man_term.c
===================================================================
--- vendor/mdocml/dist/man_term.c	(revision 275396)
+++ vendor/mdocml/dist/man_term.c	(revision 275397)
@@ -1,1190 +1,1193 @@
-/*	$Id: man_term.c,v 1.149 2014/06/20 23:02:31 schwarze Exp $ */
+/*	$Id: man_term.c,v 1.156 2014/11/21 01:52:53 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons 
  * Copyright (c) 2010-2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "out.h"
 #include "man.h"
 #include "term.h"
 #include "main.h"
 
 #define	MAXMARGINS	  64 /* maximum number of indented scopes */
 
 struct	mtermp {
 	int		  fl;
 #define	MANT_LITERAL	 (1 << 0)
 	size_t		  lmargin[MAXMARGINS]; /* margins (incl. visible page) */
 	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 *meta
 
 struct	termact {
 	int		(*pre)(DECL_ARGS);
 	void		(*post)(DECL_ARGS);
 	int		  flags;
 #define	MAN_NOTEXT	 (1 << 0) /* Never has text children. */
 };
 
 static	int		  a2width(const struct termp *, const char *);
 static	size_t		  a2height(const struct termp *, const char *);
 
 static	void		  print_man_nodelist(DECL_ARGS);
 static	void		  print_man_node(DECL_ARGS);
 static	void		  print_man_head(struct termp *, const void *);
 static	void		  print_man_foot(struct termp *, const void *);
 static	void		  print_bvspace(struct termp *,
 				const struct man_node *, int);
 
 static	int		  pre_B(DECL_ARGS);
 static	int		  pre_HP(DECL_ARGS);
 static	int		  pre_I(DECL_ARGS);
 static	int		  pre_IP(DECL_ARGS);
 static	int		  pre_OP(DECL_ARGS);
 static	int		  pre_PD(DECL_ARGS);
 static	int		  pre_PP(DECL_ARGS);
 static	int		  pre_RS(DECL_ARGS);
 static	int		  pre_SH(DECL_ARGS);
 static	int		  pre_SS(DECL_ARGS);
 static	int		  pre_TP(DECL_ARGS);
 static	int		  pre_UR(DECL_ARGS);
 static	int		  pre_alternate(DECL_ARGS);
 static	int		  pre_ft(DECL_ARGS);
 static	int		  pre_ign(DECL_ARGS);
 static	int		  pre_in(DECL_ARGS);
 static	int		  pre_literal(DECL_ARGS);
 static	int		  pre_ll(DECL_ARGS);
 static	int		  pre_sp(DECL_ARGS);
 
 static	void		  post_IP(DECL_ARGS);
 static	void		  post_HP(DECL_ARGS);
 static	void		  post_RS(DECL_ARGS);
 static	void		  post_SH(DECL_ARGS);
 static	void		  post_SS(DECL_ARGS);
 static	void		  post_TP(DECL_ARGS);
 static	void		  post_UR(DECL_ARGS);
 
 static	const struct termact termacts[MAN_MAX] = {
 	{ pre_sp, NULL, MAN_NOTEXT }, /* br */
 	{ NULL, NULL, 0 }, /* TH */
 	{ pre_SH, post_SH, 0 }, /* SH */
 	{ pre_SS, post_SS, 0 }, /* SS */
 	{ pre_TP, post_TP, 0 }, /* TP */
 	{ pre_PP, NULL, 0 }, /* LP */
 	{ pre_PP, NULL, 0 }, /* PP */
 	{ pre_PP, NULL, 0 }, /* P */
 	{ pre_IP, post_IP, 0 }, /* IP */
 	{ pre_HP, post_HP, 0 }, /* HP */
 	{ NULL, NULL, 0 }, /* SM */
 	{ pre_B, NULL, 0 }, /* SB */
 	{ pre_alternate, NULL, 0 }, /* BI */
 	{ pre_alternate, NULL, 0 }, /* IB */
 	{ pre_alternate, NULL, 0 }, /* BR */
 	{ pre_alternate, NULL, 0 }, /* RB */
 	{ NULL, NULL, 0 }, /* R */
 	{ pre_B, NULL, 0 }, /* B */
 	{ pre_I, NULL, 0 }, /* I */
 	{ pre_alternate, NULL, 0 }, /* IR */
 	{ pre_alternate, NULL, 0 }, /* RI */
 	{ pre_ign, NULL, MAN_NOTEXT }, /* na */
 	{ pre_sp, NULL, MAN_NOTEXT }, /* sp */
 	{ pre_literal, NULL, 0 }, /* nf */
 	{ pre_literal, NULL, 0 }, /* fi */
 	{ NULL, NULL, 0 }, /* RE */
 	{ pre_RS, post_RS, 0 }, /* RS */
 	{ pre_ign, NULL, 0 }, /* DT */
 	{ pre_ign, NULL, 0 }, /* UC */
 	{ 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 */
 	{ pre_ll, NULL, MAN_NOTEXT }, /* ll */
 };
 
 
 void
 terminal_man(void *arg, const struct man *man)
 {
 	struct termp		*p;
-	const struct man_node	*n;
 	const struct man_meta	*meta;
+	struct man_node		*n;
 	struct mtermp		 mt;
 
 	p = (struct termp *)arg;
 
-	if (0 == p->defindent)
-		p->defindent = 7;
-
 	p->overstep = 0;
-	p->maxrmargin = p->defrmargin;
+	p->rmargin = p->maxrmargin = p->defrmargin;
 	p->tabwidth = term_len(p, 5);
 
-	if (NULL == p->symtab)
-		p->symtab = mchars_alloc();
-
-	n = man_node(man);
+	n = man_node(man)->child;
 	meta = man_meta(man);
 
-	term_begin(p, print_man_head, print_man_foot, meta);
-	p->flags |= TERMP_NOSPACE;
-
 	memset(&mt, 0, sizeof(struct mtermp));
 
 	mt.lmargin[mt.lmargincur] = term_len(p, p->defindent);
 	mt.offset = term_len(p, p->defindent);
 	mt.pardist = 1;
 
-	if (n->child)
-		print_man_nodelist(p, &mt, n->child, meta);
-
-	term_end(p);
+	if (p->synopsisonly) {
+		while (n != NULL) {
+			if (n->tok == MAN_SH &&
+			    n->child->child->type == MAN_TEXT &&
+			    !strcmp(n->child->child->string, "SYNOPSIS")) {
+				if (n->child->next->child != NULL)
+					print_man_nodelist(p, &mt,
+					    n->child->next->child, meta);
+				term_newln(p);
+				break;
+			}
+			n = n->next;
+		}
+	} else {
+		if (p->defindent == 0)
+			p->defindent = 7;
+		term_begin(p, print_man_head, print_man_foot, meta);
+		p->flags |= TERMP_NOSPACE;
+		if (n != NULL)
+			print_man_nodelist(p, &mt, n, meta);
+		term_end(p);
+	}
 }
 
 
 static size_t
 a2height(const struct termp *p, const char *cp)
 {
 	struct roffsu	 su;
 
 	if ( ! a2roffsu(cp, &su, SCALE_VS))
 		SCALE_VS_INIT(&su, atoi(cp));
 
 	return(term_vspan(p, &su));
 }
 
 static int
 a2width(const struct termp *p, const char *cp)
 {
 	struct roffsu	 su;
 
 	if ( ! a2roffsu(cp, &su, SCALE_BU))
 		return(-1);
 
 	return((int)term_hspan(p, &su));
 }
 
 /*
  * Printing leading vertical space before a block.
  * This is used for the paragraph macros.
  * The rules are pretty simple, since there's very little nesting going
  * on here.  Basically, if we're the first within another block (SS/SH),
  * then don't emit vertical space.  If we are (RS), then do.  If not the
  * first, print it.
  */
 static void
 print_bvspace(struct termp *p, const struct man_node *n, int pardist)
 {
 	int	 i;
 
 	term_newln(p);
 
 	if (n->body && n->body->child)
 		if (MAN_TBL == n->body->child->type)
 			return;
 
 	if (MAN_ROOT == n->parent->type || MAN_RS != n->parent->tok)
 		if (NULL == n->prev)
 			return;
 
 	for (i = 0; i < pardist; i++)
 		term_vspace(p);
 }
 
 
 static int
 pre_ign(DECL_ARGS)
 {
 
 	return(0);
 }
 
 static int
 pre_ll(DECL_ARGS)
 {
 
 	term_setwidth(p, n->nchild ? n->child->string : NULL);
 	return(0);
 }
 
 static int
 pre_I(DECL_ARGS)
 {
 
 	term_fontrepl(p, TERMFONT_UNDER);
 	return(1);
 }
 
 static int
 pre_literal(DECL_ARGS)
 {
 
 	term_newln(p);
 
 	if (MAN_nf == n->tok || MAN_EX == n->tok)
 		mt->fl |= MANT_LITERAL;
 	else
 		mt->fl &= ~MANT_LITERAL;
 
 	/*
 	 * Unlike .IP and .TP, .HP does not have a HEAD.
 	 * So in case a second call to term_flushln() is needed,
 	 * indentation has to be set up explicitly.
 	 */
 	if (MAN_HP == n->parent->tok && p->rmargin < p->maxrmargin) {
 		p->offset = p->rmargin;
 		p->rmargin = p->maxrmargin;
 		p->trailspace = 0;
 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
 		p->flags |= TERMP_NOSPACE;
 	}
 
 	return(0);
 }
 
 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);
 }
 
 static int
 pre_alternate(DECL_ARGS)
 {
 	enum termfont		 font[2];
 	const struct man_node	*nn;
 	int			 savelit, i;
 
 	switch (n->tok) {
 	case MAN_RB:
 		font[0] = TERMFONT_NONE;
 		font[1] = TERMFONT_BOLD;
 		break;
 	case MAN_RI:
 		font[0] = TERMFONT_NONE;
 		font[1] = TERMFONT_UNDER;
 		break;
 	case MAN_BR:
 		font[0] = TERMFONT_BOLD;
 		font[1] = TERMFONT_NONE;
 		break;
 	case MAN_BI:
 		font[0] = TERMFONT_BOLD;
 		font[1] = TERMFONT_UNDER;
 		break;
 	case MAN_IR:
 		font[0] = TERMFONT_UNDER;
 		font[1] = TERMFONT_NONE;
 		break;
 	case MAN_IB:
 		font[0] = TERMFONT_UNDER;
 		font[1] = TERMFONT_BOLD;
 		break;
 	default:
 		abort();
 	}
 
 	savelit = MANT_LITERAL & mt->fl;
 	mt->fl &= ~MANT_LITERAL;
 
 	for (i = 0, nn = n->child; nn; nn = nn->next, i = 1 - i) {
 		term_fontrepl(p, font[i]);
 		if (savelit && NULL == nn->next)
 			mt->fl |= MANT_LITERAL;
 		print_man_node(p, mt, nn, meta);
 		if (nn->next)
 			p->flags |= TERMP_NOSPACE;
 	}
 
 	return(0);
 }
 
 static int
 pre_B(DECL_ARGS)
 {
 
 	term_fontrepl(p, TERMFONT_BOLD);
 	return(1);
 }
 
 static int
 pre_OP(DECL_ARGS)
 {
 
 	term_word(p, "[");
 	p->flags |= TERMP_NOSPACE;
 
 	if (NULL != (n = n->child)) {
 		term_fontrepl(p, TERMFONT_BOLD);
 		term_word(p, n->string);
 	}
 	if (NULL != n && NULL != n->next) {
 		term_fontrepl(p, TERMFONT_UNDER);
 		term_word(p, n->next->string);
 	}
 
 	term_fontrepl(p, TERMFONT_NONE);
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, "]");
 	return(0);
 }
 
 static int
 pre_ft(DECL_ARGS)
 {
 	const char	*cp;
 
 	if (NULL == n->child) {
 		term_fontlast(p);
 		return(0);
 	}
 
 	cp = n->child->string;
 	switch (*cp) {
 	case '4':
 		/* FALLTHROUGH */
 	case '3':
 		/* FALLTHROUGH */
 	case 'B':
 		term_fontrepl(p, TERMFONT_BOLD);
 		break;
 	case '2':
 		/* FALLTHROUGH */
 	case 'I':
 		term_fontrepl(p, TERMFONT_UNDER);
 		break;
 	case 'P':
 		term_fontlast(p);
 		break;
 	case '1':
 		/* FALLTHROUGH */
 	case 'C':
 		/* FALLTHROUGH */
 	case 'R':
 		term_fontrepl(p, TERMFONT_NONE);
 		break;
 	default:
 		break;
 	}
 	return(0);
 }
 
 static int
 pre_in(DECL_ARGS)
 {
 	int		 len, less;
 	size_t		 v;
 	const char	*cp;
 
 	term_newln(p);
 
 	if (NULL == n->child) {
 		p->offset = mt->offset;
 		return(0);
 	}
 
 	cp = n->child->string;
 	less = 0;
 
 	if ('-' == *cp)
 		less = -1;
 	else if ('+' == *cp)
 		less = 1;
 	else
 		cp--;
 
 	if ((len = a2width(p, ++cp)) < 0)
 		return(0);
 
 	v = (size_t)len;
 
 	if (less < 0)
 		p->offset -= p->offset > v ? v : p->offset;
 	else if (less > 0)
 		p->offset += v;
 	else
 		p->offset = v;
 
-	/* Don't let this creep beyond the right margin. */
-
-	if (p->offset > p->rmargin)
-		p->offset = p->rmargin;
-
 	return(0);
 }
 
 static int
 pre_sp(DECL_ARGS)
 {
 	char		*s;
 	size_t		 i, len;
 	int		 neg;
 
 	if ((NULL == n->prev && n->parent)) {
 		switch (n->parent->tok) {
 		case MAN_SH:
 			/* FALLTHROUGH */
 		case MAN_SS:
 			/* FALLTHROUGH */
 		case MAN_PP:
 			/* FALLTHROUGH */
 		case MAN_LP:
 			/* FALLTHROUGH */
 		case MAN_P:
 			/* FALLTHROUGH */
 			return(0);
 		default:
 			break;
 		}
 	}
 
 	neg = 0;
 	switch (n->tok) {
 	case MAN_br:
 		len = 0;
 		break;
 	default:
 		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);
 	else if (neg)
 		p->skipvsp += len;
 	else
 		for (i = 0; i < len; i++)
 			term_vspace(p);
 
 	return(0);
 }
 
 static int
 pre_HP(DECL_ARGS)
 {
 	size_t			 len, one;
 	int			 ival;
 	const struct man_node	*nn;
 
 	switch (n->type) {
 	case MAN_BLOCK:
 		print_bvspace(p, n, mt->pardist);
 		return(1);
 	case MAN_BODY:
 		break;
 	default:
 		return(0);
 	}
 
 	if ( ! (MANT_LITERAL & mt->fl)) {
 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
 		p->trailspace = 2;
 	}
 
 	len = mt->lmargin[mt->lmargincur];
 	ival = -1;
 
 	/* Calculate offset. */
 
 	if (NULL != (nn = n->parent->head->child))
 		if ((ival = a2width(p, nn->string)) >= 0)
 			len = (size_t)ival;
 
 	one = term_len(p, 1);
 	if (len < one)
 		len = one;
 
 	p->offset = mt->offset;
 	p->rmargin = mt->offset + len;
 
 	if (ival >= 0)
 		mt->lmargin[mt->lmargincur] = (size_t)ival;
 
 	return(1);
 }
 
 static void
 post_HP(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MAN_BODY:
 		term_newln(p);
 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND);
 		p->trailspace = 0;
 		p->offset = mt->offset;
 		p->rmargin = p->maxrmargin;
 		break;
 	default:
 		break;
 	}
 }
 
 static int
 pre_PP(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MAN_BLOCK:
 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 		print_bvspace(p, n, mt->pardist);
 		break;
 	default:
 		p->offset = mt->offset;
 		break;
 	}
 
 	return(MAN_HEAD != n->type);
 }
 
 static int
 pre_IP(DECL_ARGS)
 {
 	const struct man_node	*nn;
 	size_t			 len;
 	int			 savelit, ival;
 
 	switch (n->type) {
 	case MAN_BODY:
 		p->flags |= TERMP_NOSPACE;
 		break;
 	case MAN_HEAD:
 		p->flags |= TERMP_NOBREAK;
 		p->trailspace = 1;
 		break;
 	case MAN_BLOCK:
 		print_bvspace(p, n, mt->pardist);
 		/* FALLTHROUGH */
 	default:
 		return(1);
 	}
 
 	len = mt->lmargin[mt->lmargincur];
 	ival = -1;
 
 	/* Calculate the offset from the optional second argument. */
 	if (NULL != (nn = n->parent->head->child))
 		if (NULL != (nn = nn->next))
 			if ((ival = a2width(p, nn->string)) >= 0)
 				len = (size_t)ival;
 
 	switch (n->type) {
 	case MAN_HEAD:
 		/* Handle zero-width lengths. */
 		if (0 == len)
 			len = term_len(p, 1);
 
 		p->offset = mt->offset;
 		p->rmargin = mt->offset + len;
 		if (ival < 0)
 			break;
 
 		/* Set the saved left-margin. */
 		mt->lmargin[mt->lmargincur] = (size_t)ival;
 
 		savelit = MANT_LITERAL & mt->fl;
 		mt->fl &= ~MANT_LITERAL;
 
 		if (n->child)
 			print_man_node(p, mt, n->child, meta);
 
 		if (savelit)
 			mt->fl |= MANT_LITERAL;
 
 		return(0);
 	case MAN_BODY:
 		p->offset = mt->offset + len;
-		p->rmargin = p->maxrmargin > p->offset ?
-		    p->maxrmargin : p->offset;
+		p->rmargin = p->maxrmargin;
 		break;
 	default:
 		break;
 	}
 
 	return(1);
 }
 
 static void
 post_IP(DECL_ARGS)
 {
 
 	switch (n->type) {
 	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;
 	}
 }
 
 static int
 pre_TP(DECL_ARGS)
 {
 	const struct man_node	*nn;
 	size_t			 len;
 	int			 savelit, ival;
 
 	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, mt->pardist);
 		/* FALLTHROUGH */
 	default:
 		return(1);
 	}
 
 	len = (size_t)mt->lmargin[mt->lmargincur];
 	ival = -1;
 
 	/* Calculate offset. */
 
 	if (NULL != (nn = n->parent->head->child))
 		if (nn->string && 0 == (MAN_LINE & nn->flags))
 			if ((ival = a2width(p, nn->string)) >= 0)
 				len = (size_t)ival;
 
 	switch (n->type) {
 	case MAN_HEAD:
 		/* Handle zero-length properly. */
 		if (0 == len)
 			len = term_len(p, 1);
 
 		p->offset = mt->offset;
 		p->rmargin = mt->offset + len;
 
 		savelit = MANT_LITERAL & mt->fl;
 		mt->fl &= ~MANT_LITERAL;
 
 		/* Don't print same-line elements. */
 		nn = n->child;
 		while (NULL != nn && 0 == (MAN_LINE & nn->flags))
 			nn = nn->next;
 
 		while (NULL != nn) {
 			print_man_node(p, mt, nn, meta);
 			nn = nn->next;
 		}
 
 		if (savelit)
 			mt->fl |= MANT_LITERAL;
 		if (ival >= 0)
 			mt->lmargin[mt->lmargincur] = (size_t)ival;
 
 		return(0);
 	case MAN_BODY:
 		p->offset = mt->offset + len;
-		p->rmargin = p->maxrmargin > p->offset ?
-		    p->maxrmargin : p->offset;
+		p->rmargin = p->maxrmargin;
 		p->trailspace = 0;
 		p->flags &= ~TERMP_NOBREAK;
 		break;
 	default:
 		break;
 	}
 
 	return(1);
 }
 
 static void
 post_TP(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MAN_HEAD:
 		term_flushln(p);
 		break;
 	case MAN_BODY:
 		term_newln(p);
 		p->offset = mt->offset;
 		break;
 	default:
 		break;
 	}
 }
 
 static int
 pre_SS(DECL_ARGS)
 {
 	int	 i;
 
 	switch (n->type) {
 	case MAN_BLOCK:
 		mt->fl &= ~MANT_LITERAL;
 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 		mt->offset = term_len(p, p->defindent);
 		/* If following a prior empty `SS', no vspace. */
 		if (n->prev && MAN_SS == n->prev->tok)
 			if (NULL == n->prev->body->child)
 				break;
 		if (NULL == n->prev)
 			break;
 		for (i = 0; i < mt->pardist; i++)
 			term_vspace(p);
 		break;
 	case MAN_HEAD:
 		term_fontrepl(p, TERMFONT_BOLD);
 		p->offset = term_len(p, 3);
 		break;
 	case MAN_BODY:
 		p->offset = mt->offset;
 		break;
 	default:
 		break;
 	}
 
 	return(1);
 }
 
 static void
 post_SS(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MAN_HEAD:
 		term_newln(p);
 		break;
 	case MAN_BODY:
 		term_newln(p);
 		break;
 	default:
 		break;
 	}
 }
 
 static int
 pre_SH(DECL_ARGS)
 {
 	int	 i;
 
 	switch (n->type) {
 	case MAN_BLOCK:
 		mt->fl &= ~MANT_LITERAL;
 		mt->lmargin[mt->lmargincur] = term_len(p, p->defindent);
 		mt->offset = term_len(p, p->defindent);
 		/* If following a prior empty `SH', no vspace. */
 		if (n->prev && MAN_SH == n->prev->tok)
 			if (NULL == n->prev->body->child)
 				break;
 		/* If the first macro, no vspae. */
 		if (NULL == n->prev)
 			break;
 		for (i = 0; i < mt->pardist; i++)
 			term_vspace(p);
 		break;
 	case MAN_HEAD:
 		term_fontrepl(p, TERMFONT_BOLD);
 		p->offset = 0;
 		break;
 	case MAN_BODY:
 		p->offset = mt->offset;
 		break;
 	default:
 		break;
 	}
 
 	return(1);
 }
 
 static void
 post_SH(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MAN_HEAD:
 		term_newln(p);
 		break;
 	case MAN_BODY:
 		term_newln(p);
 		break;
 	default:
 		break;
 	}
 }
 
 static int
 pre_RS(DECL_ARGS)
 {
 	int		 ival;
 	size_t		 sz;
 
 	switch (n->type) {
 	case MAN_BLOCK:
 		term_newln(p);
 		return(1);
 	case MAN_HEAD:
 		return(0);
 	default:
 		break;
 	}
 
 	sz = term_len(p, p->defindent);
 
 	if (NULL != (n = n->parent->head->child))
 		if ((ival = a2width(p, n->string)) >= 0)
 			sz = (size_t)ival;
 
 	mt->offset += sz;
 	p->offset = mt->offset;
-	p->rmargin = p->maxrmargin > p->offset ?
-	    p->maxrmargin : p->offset;
+	p->rmargin = p->maxrmargin;
 
 	if (++mt->lmarginsz < MAXMARGINS)
 		mt->lmargincur = mt->lmarginsz;
 
 	mt->lmargin[mt->lmargincur] = mt->lmargin[mt->lmargincur - 1];
 	return(1);
 }
 
 static void
 post_RS(DECL_ARGS)
 {
 	int		 ival;
 	size_t		 sz;
 
 	switch (n->type) {
 	case MAN_BLOCK:
 		return;
 	case MAN_HEAD:
 		return;
 	default:
 		term_newln(p);
 		break;
 	}
 
 	sz = term_len(p, p->defindent);
 
 	if (NULL != (n = n->parent->head->child))
 		if ((ival = a2width(p, n->string)) >= 0)
 			sz = (size_t)ival;
 
 	mt->offset = mt->offset < sz ?  0 : mt->offset - sz;
 	p->offset = mt->offset;
 
 	if (--mt->lmarginsz < MAXMARGINS)
 		mt->lmargincur = mt->lmarginsz;
 }
 
 static int
 pre_UR(DECL_ARGS)
 {
 
 	return (MAN_HEAD != n->type);
 }
 
 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;
 	int		 c;
 
 	switch (n->type) {
 	case MAN_TEXT:
 		/*
 		 * If we have a blank line, output a vertical space.
 		 * If we have a space as the first character, break
 		 * before printing the line's data.
 		 */
 		if ('\0' == *n->string) {
 			term_vspace(p);
 			return;
 		} else if (' ' == *n->string && MAN_LINE & n->flags)
 			term_newln(p);
 
 		term_word(p, n->string);
 		goto out;
 
 	case MAN_EQN:
+		if ( ! (n->flags & MAN_LINE))
+			p->flags |= TERMP_NOSPACE;
 		term_eqn(p, n->eqn);
+		if (n->next != NULL && ! (n->next->flags & MAN_LINE))
+			p->flags |= TERMP_NOSPACE;
 		return;
 	case MAN_TBL:
 		/*
 		 * Tables are preceded by a newline.  Then process a
 		 * table line, which will cause line termination,
 		 */
 		if (TBL_SPAN_FIRST & n->span->flags)
 			term_newln(p);
 		term_tbl(p, n->span);
 		return;
 	default:
 		break;
 	}
 
 	if ( ! (MAN_NOTEXT & termacts[n->tok].flags))
 		term_fontrepl(p, TERMFONT_NONE);
 
 	c = 1;
 	if (termacts[n->tok].pre)
 		c = (*termacts[n->tok].pre)(p, mt, n, meta);
 
 	if (c && n->child)
 		print_man_nodelist(p, mt, n->child, meta);
 
 	if (termacts[n->tok].post)
 		(*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 || MAN_LINE & n->next->flags)) {
 		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;
 }
 
 
 static void
 print_man_nodelist(DECL_ARGS)
 {
 
 	print_man_node(p, mt, n, meta);
 	if ( ! n->next)
 		return;
 	print_man_nodelist(p, mt, n->next, meta);
 }
 
 static void
 print_man_foot(struct termp *p, const void *arg)
 {
 	const struct man_meta	*meta;
 	char			*title;
-	size_t			 datelen;
+	size_t			 datelen, titlen;
 
 	meta = (const struct man_meta *)arg;
 	assert(meta->title);
 	assert(meta->msec);
 	assert(meta->date);
 
 	term_fontrepl(p, TERMFONT_NONE);
 
 	if (meta->hasbody)
 		term_vspace(p);
 
 	/*
 	 * Temporary, undocumented option to imitate mdoc(7) output.
 	 * In the bottom right corner, use the source instead of
 	 * the title.
 	 */
 
 	if ( ! p->mdocstyle) {
 		if (meta->hasbody) {
 			term_vspace(p);
 			term_vspace(p);
 		}
 		mandoc_asprintf(&title, "%s(%s)",
 		    meta->title, meta->msec);
 	} else if (meta->source) {
 		title = mandoc_strdup(meta->source);
 	} else {
 		title = mandoc_strdup("");
 	}
 	datelen = term_strlen(p, meta->date);
 
 	/* 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;
+	p->rmargin = p->maxrmargin > datelen ?
+	    (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
 
 	if (meta->source)
 		term_word(p, meta->source);
 	term_flushln(p);
 
 	/* At the bottom in the middle: manual date. */
 
-	p->flags |= TERMP_NOSPACE;
 	p->offset = p->rmargin;
-	p->rmargin = p->maxrmargin - term_strlen(p, title);
-	if (p->offset + datelen >= p->rmargin)
-		p->rmargin = p->offset + datelen;
+	titlen = term_strlen(p, title);
+	p->rmargin = p->maxrmargin > titlen ? p->maxrmargin - titlen : 0;
+	p->flags |= TERMP_NOSPACE;
 
 	term_word(p, meta->date);
 	term_flushln(p);
 
 	/* Bottom right corner: manual title and section. */
 
 	p->flags &= ~TERMP_NOBREAK;
 	p->flags |= TERMP_NOSPACE;
 	p->trailspace = 0;
 	p->offset = p->rmargin;
 	p->rmargin = p->maxrmargin;
 
 	term_word(p, title);
 	term_flushln(p);
 	free(title);
 }
 
 static void
 print_man_head(struct termp *p, const void *arg)
 {
 	const struct man_meta	*meta;
 	const char		*volume;
 	char			*title;
 	size_t			 vollen, titlen;
 
 	meta = (const struct man_meta *)arg;
 	assert(meta->title);
 	assert(meta->msec);
 
 	volume = NULL == meta->vol ? "" : meta->vol;
 	vollen = term_strlen(p, volume);
 
 	/* Top left corner: manual title and section. */
 
 	mandoc_asprintf(&title, "%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) + vollen < p->maxrmargin ?
 	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
-	    p->maxrmargin - vollen;
+	    vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
 
 	term_word(p, title);
 	term_flushln(p);
 
 	/* At the top in the middle: manual volume. */
 
 	p->flags |= TERMP_NOSPACE;
 	p->offset = p->rmargin;
 	p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
 	    p->maxrmargin - titlen : p->maxrmargin;
 
 	term_word(p, volume);
 	term_flushln(p);
 
 	/* 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;
 		p->rmargin = p->maxrmargin;
 		term_word(p, title);
 		term_flushln(p);
 	}
 
 	p->flags &= ~TERMP_NOSPACE;
 	p->offset = 0;
 	p->rmargin = p->maxrmargin;
 
 	/*
 	 * Groff prints three blank lines before the content.
 	 * Do the same, except in the temporary, undocumented
 	 * mode imitating mdoc(7) output.
 	 */
 
 	term_vspace(p);
 	if ( ! p->mdocstyle) {
 		term_vspace(p);
 		term_vspace(p);
 	}
 	free(title);
 }
Index: vendor/mdocml/dist/man_validate.c
===================================================================
--- vendor/mdocml/dist/man_validate.c	(revision 275396)
+++ vendor/mdocml/dist/man_validate.c	(revision 275397)
@@ -1,546 +1,529 @@
-/*	$Id: man_validate.c,v 1.105 2014/08/06 15:09:05 schwarze Exp $ */
+/*	$OpenBSD$ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "man.h"
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "libman.h"
 #include "libmandoc.h"
 
 #define	CHKARGS	  struct man *man, struct man_node *n
 
-typedef	int	(*v_check)(CHKARGS);
+typedef	void	(*v_check)(CHKARGS);
 
-static	int	  check_eq0(CHKARGS);
-static	int	  check_eq2(CHKARGS);
-static	int	  check_le1(CHKARGS);
-static	int	  check_le5(CHKARGS);
-static	int	  check_par(CHKARGS);
-static	int	  check_part(CHKARGS);
-static	int	  check_root(CHKARGS);
-static	int	  check_text(CHKARGS);
+static	void	  check_eq0(CHKARGS);
+static	void	  check_eq2(CHKARGS);
+static	void	  check_le1(CHKARGS);
+static	void	  check_le5(CHKARGS);
+static	void	  check_par(CHKARGS);
+static	void	  check_part(CHKARGS);
+static	void	  check_root(CHKARGS);
+static	void	  check_text(CHKARGS);
 
-static	int	  post_AT(CHKARGS);
-static	int	  post_IP(CHKARGS);
-static	int	  post_vs(CHKARGS);
-static	int	  post_fi(CHKARGS);
-static	int	  post_ft(CHKARGS);
-static	int	  post_nf(CHKARGS);
-static	int	  post_TH(CHKARGS);
-static	int	  post_UC(CHKARGS);
-static	int	  post_UR(CHKARGS);
+static	void	  post_AT(CHKARGS);
+static	void	  post_IP(CHKARGS);
+static	void	  post_vs(CHKARGS);
+static	void	  post_fi(CHKARGS);
+static	void	  post_ft(CHKARGS);
+static	void	  post_nf(CHKARGS);
+static	void	  post_TH(CHKARGS);
+static	void	  post_UC(CHKARGS);
+static	void	  post_UR(CHKARGS);
 
 static	v_check man_valids[MAN_MAX] = {
 	post_vs,    /* br */
 	post_TH,    /* TH */
 	NULL,       /* SH */
 	NULL,       /* SS */
 	NULL,       /* TP */
 	check_par,  /* LP */
 	check_par,  /* PP */
 	check_par,  /* P */
 	post_IP,    /* IP */
 	NULL,       /* HP */
 	NULL,       /* SM */
 	NULL,       /* SB */
 	NULL,       /* BI */
 	NULL,       /* IB */
 	NULL,       /* BR */
 	NULL,       /* RB */
 	NULL,       /* R */
 	NULL,       /* B */
 	NULL,       /* I */
 	NULL,       /* IR */
 	NULL,       /* RI */
 	check_eq0,  /* na */
 	post_vs,    /* sp */
 	post_nf,    /* nf */
 	post_fi,    /* fi */
 	NULL,       /* RE */
 	check_part, /* RS */
 	NULL,       /* DT */
 	post_UC,    /* UC */
 	check_le1,  /* PD */
 	post_AT,    /* AT */
 	NULL,       /* in */
 	post_ft,    /* ft */
 	check_eq2,  /* OP */
 	post_nf,    /* EX */
 	post_fi,    /* EE */
 	post_UR,    /* UR */
 	NULL,       /* UE */
 	NULL,       /* ll */
 };
 
 
-int
+void
 man_valid_post(struct man *man)
 {
 	struct man_node	*n;
 	v_check		*cp;
 
 	n = man->last;
 	if (n->flags & MAN_VALID)
-		return(1);
+		return;
 	n->flags |= MAN_VALID;
 
 	switch (n->type) {
 	case MAN_TEXT:
-		return(check_text(man, n));
+		check_text(man, n);
+		break;
 	case MAN_ROOT:
-		return(check_root(man, n));
+		check_root(man, n);
+		break;
 	case MAN_EQN:
 		/* FALLTHROUGH */
 	case MAN_TBL:
-		return(1);
+		break;
 	default:
 		cp = man_valids + n->tok;
-		return(*cp ? (*cp)(man, n) : 1);
+		if (*cp)
+			(*cp)(man, n);
+		break;
 	}
 }
 
-static int
+static void
 check_root(CHKARGS)
 {
 
 	assert((man->flags & (MAN_BLINE | MAN_ELINE)) == 0);
 
 	if (NULL == man->first->child)
 		mandoc_msg(MANDOCERR_DOC_EMPTY, man->parse,
 		    n->line, n->pos, NULL);
 	else
 		man->meta.hasbody = 1;
 
 	if (NULL == man->meta.title) {
 		mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
 		    n->line, n->pos, NULL);
 
 		/*
 		 * If a title hasn't been set, do so now (by
 		 * implication, date and section also aren't set).
 		 */
 
 		man->meta.title = mandoc_strdup("");
 		man->meta.msec = mandoc_strdup("");
 		man->meta.date = man->quick ? mandoc_strdup("") :
 		    mandoc_normdate(man->parse, NULL, n->line, n->pos);
 	}
-
-	return(1);
 }
 
-static int
+static void
 check_text(CHKARGS)
 {
 	char		*cp, *p;
 
 	if (MAN_LITERAL & man->flags)
-		return(1);
+		return;
 
 	cp = n->string;
 	for (p = cp; NULL != (p = strchr(p, '\t')); p++)
 		mandoc_msg(MANDOCERR_FI_TAB, man->parse,
 		    n->line, n->pos + (p - cp), NULL);
-	return(1);
 }
 
 #define	INEQ_DEFINE(x, ineq, name) \
-static int \
+static void \
 check_##name(CHKARGS) \
 { \
 	if (n->nchild ineq (x)) \
-		return(1); \
+		return; \
 	mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line, n->pos, \
 	    "line arguments %s %d (have %d)", \
 	    #ineq, (x), n->nchild); \
-	return(1); \
 }
 
 INEQ_DEFINE(0, ==, eq0)
 INEQ_DEFINE(2, ==, eq2)
 INEQ_DEFINE(1, <=, le1)
 INEQ_DEFINE(5, <=, le5)
 
-static int
+static void
 post_UR(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(check_part(man, n));
+	check_part(man, n);
 }
 
-static int
+static void
 post_ft(CHKARGS)
 {
 	char	*cp;
 	int	 ok;
 
 	if (0 == n->nchild)
-		return(1);
+		return;
 
 	ok = 0;
 	cp = n->child->string;
 	switch (*cp) {
 	case '1':
 		/* FALLTHROUGH */
 	case '2':
 		/* FALLTHROUGH */
 	case '3':
 		/* FALLTHROUGH */
 	case '4':
 		/* FALLTHROUGH */
 	case 'I':
 		/* FALLTHROUGH */
 	case 'P':
 		/* FALLTHROUGH */
 	case 'R':
 		if ('\0' == cp[1])
 			ok = 1;
 		break;
 	case 'B':
 		if ('\0' == cp[1] || ('I' == cp[1] && '\0' == cp[2]))
 			ok = 1;
 		break;
 	case 'C':
 		if ('W' == cp[1] && '\0' == cp[2])
 			ok = 1;
 		break;
 	default:
 		break;
 	}
 
 	if (0 == ok) {
 		mandoc_vmsg(MANDOCERR_FT_BAD, man->parse,
 		    n->line, n->pos, "ft %s", cp);
 		*cp = '\0';
 	}
 
 	if (1 < n->nchild)
 		mandoc_vmsg(MANDOCERR_ARGCOUNT, man->parse, n->line,
 		    n->pos, "want one child (have %d)", n->nchild);
-
-	return(1);
 }
 
-static int
+static void
 check_part(CHKARGS)
 {
 
 	if (MAN_BODY == n->type && 0 == n->nchild)
 		mandoc_msg(MANDOCERR_ARGCWARN, man->parse, n->line,
 		    n->pos, "want children (have none)");
-
-	return(1);
 }
 
-static int
+static void
 check_par(CHKARGS)
 {
 
 	switch (n->type) {
 	case MAN_BLOCK:
 		if (0 == n->body->nchild)
 			man_node_delete(man, n);
 		break;
 	case MAN_BODY:
 		if (0 == n->nchild)
 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
 			    man->parse, n->line, n->pos,
 			    "%s empty", man_macronames[n->tok]);
 		break;
 	case MAN_HEAD:
 		if (n->nchild)
 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
 			    man->parse, n->line, n->pos,
 			    "%s %s%s", man_macronames[n->tok],
 			    n->child->string,
 			    n->nchild > 1 ? " ..." : "");
 		break;
 	default:
 		break;
 	}
-
-	return(1);
 }
 
-static int
+static void
 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)
 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
 			    man->parse, n->line, n->pos,
 			    "%s empty", man_macronames[n->tok]);
 		break;
 	default:
 		break;
 	}
-	return(1);
 }
 
-static int
+static void
 post_TH(CHKARGS)
 {
 	struct man_node	*nb;
 	const char	*p;
 
 	check_le5(man, n);
 
 	free(man->meta.title);
 	free(man->meta.vol);
 	free(man->meta.source);
 	free(man->meta.msec);
 	free(man->meta.date);
 
 	man->meta.title = man->meta.vol = man->meta.date =
 	    man->meta.msec = man->meta.source = NULL;
 
 	nb = n;
 
 	/* ->TITLE<- MSEC DATE SOURCE VOL */
 
 	n = n->child;
 	if (n && n->string) {
 		for (p = n->string; '\0' != *p; p++) {
 			/* Only warn about this once... */
 			if (isalpha((unsigned char)*p) &&
 			    ! isupper((unsigned char)*p)) {
 				mandoc_vmsg(MANDOCERR_TITLE_CASE,
 				    man->parse, n->line,
 				    n->pos + (p - n->string),
 				    "TH %s", n->string);
 				break;
 			}
 		}
 		man->meta.title = mandoc_strdup(n->string);
 	} else {
 		man->meta.title = mandoc_strdup("");
 		mandoc_msg(MANDOCERR_TH_NOTITLE, man->parse,
 		    nb->line, nb->pos, "TH");
 	}
 
 	/* TITLE ->MSEC<- DATE SOURCE VOL */
 
 	if (n)
 		n = n->next;
 	if (n && n->string)
 		man->meta.msec = mandoc_strdup(n->string);
 	else {
 		man->meta.msec = mandoc_strdup("");
 		mandoc_vmsg(MANDOCERR_MSEC_MISSING, man->parse,
 		    nb->line, nb->pos, "TH %s", man->meta.title);
 	}
 
 	/* TITLE MSEC ->DATE<- SOURCE VOL */
 
 	if (n)
 		n = n->next;
 	if (n && n->string && '\0' != n->string[0]) {
 		man->meta.date = man->quick ?
 		    mandoc_strdup(n->string) :
 		    mandoc_normdate(man->parse, n->string,
 			n->line, n->pos);
 	} else {
 		man->meta.date = mandoc_strdup("");
 		mandoc_msg(MANDOCERR_DATE_MISSING, man->parse,
 		    n ? n->line : nb->line,
 		    n ? n->pos : nb->pos, "TH");
 	}
 
 	/* TITLE MSEC DATE ->SOURCE<- VOL */
 
 	if (n && (n = n->next))
 		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))
 		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(man, man->last);
-	return(1);
 }
 
-static int
+static void
 post_nf(CHKARGS)
 {
 
 	check_eq0(man, n);
 
 	if (MAN_LITERAL & man->flags)
 		mandoc_msg(MANDOCERR_NF_SKIP, man->parse,
 		    n->line, n->pos, "nf");
 
 	man->flags |= MAN_LITERAL;
-	return(1);
 }
 
-static int
+static void
 post_fi(CHKARGS)
 {
 
 	check_eq0(man, n);
 
 	if ( ! (MAN_LITERAL & man->flags))
 		mandoc_msg(MANDOCERR_FI_SKIP, man->parse,
 		    n->line, n->pos, "fi");
 
 	man->flags &= ~MAN_LITERAL;
-	return(1);
 }
 
-static int
+static void
 post_UC(CHKARGS)
 {
 	static const char * const bsd_versions[] = {
 	    "3rd Berkeley Distribution",
 	    "4th Berkeley Distribution",
 	    "4.2 Berkeley Distribution",
 	    "4.3 Berkeley Distribution",
 	    "4.4 Berkeley Distribution",
 	};
 
 	const char	*p, *s;
 
 	n = n->child;
 
 	if (NULL == n || MAN_TEXT != n->type)
 		p = bsd_versions[0];
 	else {
 		s = n->string;
 		if (0 == strcmp(s, "3"))
 			p = bsd_versions[0];
 		else if (0 == strcmp(s, "4"))
 			p = bsd_versions[1];
 		else if (0 == strcmp(s, "5"))
 			p = bsd_versions[2];
 		else if (0 == strcmp(s, "6"))
 			p = bsd_versions[3];
 		else if (0 == strcmp(s, "7"))
 			p = bsd_versions[4];
 		else
 			p = bsd_versions[0];
 	}
 
 	free(man->meta.source);
 	man->meta.source = mandoc_strdup(p);
-	return(1);
 }
 
-static int
+static void
 post_AT(CHKARGS)
 {
 	static const char * const unix_versions[] = {
 	    "7th Edition",
 	    "System III",
 	    "System V",
 	    "System V Release 2",
 	};
 
 	const char	*p, *s;
 	struct man_node	*nn;
 
 	n = n->child;
 
 	if (NULL == n || MAN_TEXT != n->type)
 		p = unix_versions[0];
 	else {
 		s = n->string;
 		if (0 == strcmp(s, "3"))
 			p = unix_versions[0];
 		else if (0 == strcmp(s, "4"))
 			p = unix_versions[1];
 		else if (0 == strcmp(s, "5")) {
 			nn = n->next;
 			if (nn && MAN_TEXT == nn->type && nn->string[0])
 				p = unix_versions[3];
 			else
 				p = unix_versions[2];
 		} else
 			p = unix_versions[0];
 	}
 
 	free(man->meta.source);
 	man->meta.source = mandoc_strdup(p);
-	return(1);
 }
 
-static int
+static void
 post_vs(CHKARGS)
 {
 
 	if (n->tok == MAN_br)
 		check_eq0(man, n);
 	else
 		check_le1(man, n);
 
 	if (NULL != n->prev)
-		return(1);
+		return;
 
 	switch (n->parent->tok) {
 	case MAN_SH:
 		/* FALLTHROUGH */
 	case MAN_SS:
 		mandoc_vmsg(MANDOCERR_PAR_SKIP, man->parse, n->line, n->pos,
 		    "%s after %s", man_macronames[n->tok],
 		    man_macronames[n->parent->tok]);
 		/* 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);
 }
Index: vendor/mdocml/dist/mandoc.1
===================================================================
--- vendor/mdocml/dist/mandoc.1	(revision 275396)
+++ vendor/mdocml/dist/mandoc.1	(revision 275397)
@@ -1,1502 +1,1640 @@
-.\"	$Id: mandoc.1,v 1.106 2014/08/08 01:50:59 schwarze Exp $
+.\"	$Id: mandoc.1,v 1.125 2014/11/28 18:09:01 schwarze Exp $
 .\"
 .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
 .\" Copyright (c) 2012, 2014 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" copyright notice and this permission notice appear in all copies.
 .\"
 .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: August 8 2014 $
+.Dd $Mdocdate: November 28 2014 $
 .Dt MANDOC 1
 .Os
 .Sh NAME
 .Nm mandoc
 .Nd format and display UNIX manuals
 .Sh SYNOPSIS
 .Nm mandoc
-.Op Fl V
+.Op Fl acfhklV
 .Sm off
 .Op Fl I Cm os Li = Ar name
 .Sm on
+.Op Fl K Ns Ar encoding
 .Op Fl m Ns Ar format
 .Op Fl O Ns Ar option
 .Op Fl T Ns Ar output
 .Op Fl W Ns Ar level
 .Op Ar
 .Sh DESCRIPTION
 The
 .Nm
 utility formats
 .Ux
 manual pages for display.
 .Pp
 By default,
 .Nm
 reads
 .Xr mdoc 7
 or
 .Xr man 7
 text from stdin, implying
 .Fl m Ns Cm andoc ,
 and produces
 .Fl T Ns Cm ascii
 output.
 .Pp
-The arguments are as follows:
+The options are as follows:
 .Bl -tag -width Ds
+.It Fl a
+If the standard output is a terminal device and
+.Fl c
+is not specified, use
+.Xr more 1
+to paginate the output, just like
+.Xr man 1
+would.
+.It Fl c
+Copy the formatted manual pages to the standard output without using
+.Xr more 1
+to paginate them.
+This is the default.
+It can be specified to override
+.Fl a .
+.It Fl f
+A synonym for
+.Xr whatis 1 .
+This overrides any earlier
+.Fl k
+and
+.Fl l
+options.
 .Sm off
 .It Fl I Cm os Li = Ar name
 .Sm on
 Override the default operating system
 .Ar name
 for the
 .Xr mdoc 7
 .Sq \&Os
 macro.
+.It Fl h
+Display only the SYNOPSIS lines.
+Implies
+.Fl c .
+.It Fl K Ns Ar encoding
+Specify the input encoding.
+The supported
+.Ar encoding
+arguments are
+.Cm us-ascii ,
+.Cm iso-8859-1 ,
+and
+.Cm utf-8 .
+If not specified, autodetection uses the first match:
+.Bl -tag -width iso-8859-1
+.It Cm utf-8
+if the first three bytes of the input file
+are the UTF-8 byte order mark (BOM, 0xefbbbf)
+.It Ar encoding
+if the first or second line of the input file matches the
+.Sy emacs
+mode line format
+.Pp
+.D1 .\e" -*- Oo ...; Oc coding: Ar encoding ; No -*-
+.It Cm utf-8
+if the first non-ASCII byte in the file introduces a valid UTF-8 sequence
+.It Cm iso-8859-1
+otherwise
+.El
+.It Fl k
+A synonym for
+.Xr apropos 1 .
+This overrides any earlier
+.Fl f
+and
+.Fl l
+options.
+.It Fl l
+A synonym for
+.Fl a .
+Also reverts any earlier
+.Fl f
+and
+.Fl k
+options.
 .It Fl m Ns Ar format
 Input format.
 See
 .Sx Input Formats
 for available formats.
 Defaults to
 .Fl m Ns Cm andoc .
 .It Fl O Ns Ar option
 Comma-separated output options.
 .It Fl T Ns Ar output
 Output format.
 See
 .Sx Output Formats
 for available formats.
 Defaults to
 .Fl T Ns Cm ascii .
 .It Fl V
 Print version and exit.
 .It Fl W Ns Ar level
 Specify the minimum message
 .Ar level
 to be reported on the standard error output and to affect the exit status.
 The
 .Ar level
 can be
 .Cm warning ,
 .Cm error ,
 or
 .Cm fatal .
 The default is
 .Fl W Ns Cm fatal ;
 .Fl W Ns Cm all
 is an alias for
 .Fl W Ns Cm warning .
 See
 .Sx EXIT STATUS
 and
 .Sx DIAGNOSTICS
 for details.
 .Pp
 The special option
 .Fl W Ns Cm stop
 tells
 .Nm
 to exit after parsing a file that causes warnings or errors of at least
 the requested level.
 No formatted output will be produced from that file.
 If both a
 .Ar level
 and
 .Cm stop
 are requested, they can be joined with a comma, for example
 .Fl W Ns Cm error , Ns Cm stop .
 .It Ar file
 Read input from zero or more files.
 If unspecified, reads from stdin.
 If multiple files are specified,
 .Nm
 will halt with the first failed parse.
 .El
+.Pp
+In
+.Fl f
+and
+.Fl k
+mode,
+.Nm
+also supports the options
+.Fl CMmOSsw
+described in the
+.Xr apropos 1
+manual.
 .Ss Input Formats
 The
 .Nm
 utility accepts
 .Xr mdoc 7
 and
 .Xr man 7
 input with
 .Fl m Ns Cm doc
 and
 .Fl m Ns Cm an ,
 respectively.
 The
 .Xr mdoc 7
 format is
 .Em strongly
 recommended;
 .Xr man 7
 should only be used for legacy manuals.
 .Pp
 A third option,
 .Fl m Ns Cm andoc ,
 which is also the default, determines encoding on-the-fly: if the first
 non-comment macro is
 .Sq \&Dd
 or
 .Sq \&Dt ,
 the
 .Xr mdoc 7
 parser is used; otherwise, the
 .Xr man 7
 parser is used.
 .Pp
 If multiple
 files are specified with
 .Fl m Ns Cm andoc ,
 each has its file-type determined this way.
 If multiple files are
 specified and
 .Fl m Ns Cm doc
 or
 .Fl m Ns Cm an
 is specified, then this format is used exclusively.
 .Ss Output Formats
 The
 .Nm
 utility accepts the following
 .Fl T
 arguments, which correspond to output modes:
 .Bl -tag -width "-Tlocale"
 .It Fl T Ns Cm ascii
 Produce 7-bit ASCII output.
 This is the default.
 See
 .Sx ASCII Output .
 .It Fl T Ns Cm html
-Produce strict CSS1/HTML-4.01 output.
+Produce HTML5, CSS1, and MathML output.
 See
 .Sx HTML Output .
 .It Fl T Ns Cm lint
 Parse only: produce no output.
 Implies
 .Fl W Ns Cm warning .
 .It Fl T Ns Cm locale
 Encode output using the current locale.
 See
 .Sx Locale Output .
 .It Fl T Ns Cm man
 Produce
 .Xr man 7
 format output.
 See
 .Sx Man Output .
 .It Fl T Ns Cm pdf
 Produce PDF output.
 See
 .Sx PDF Output .
 .It Fl T Ns Cm ps
 Produce PostScript output.
 See
 .Sx PostScript Output .
 .It Fl T Ns Cm tree
 Produce an indented parse tree.
 .It Fl T Ns Cm utf8
 Encode output in the UTF\-8 multi-byte format.
 See
 .Sx UTF\-8 Output .
 .It Fl T Ns Cm xhtml
-Produce strict CSS1/XHTML-1.0 output.
-See
-.Sx XHTML Output .
+This is a synonym for
+.Fl T Ns Cm html .
 .El
 .Pp
 If multiple input files are specified, these will be processed by the
 corresponding filter in-order.
 .Ss ASCII Output
 Output produced by
 .Fl T Ns Cm ascii ,
 which is the default, is rendered in standard 7-bit ASCII documented in
 .Xr ascii 7 .
 .Pp
 Font styles are applied by using back-spaced encoding such that an
 underlined character
 .Sq c
 is rendered as
 .Sq _ Ns \e[bs] Ns c ,
 where
 .Sq \e[bs]
 is the back-space character number 8.
 Emboldened characters are rendered as
 .Sq c Ns \e[bs] Ns c .
 .Pp
 The special characters documented in
 .Xr mandoc_char 7
 are rendered best-effort in an ASCII equivalent.
 If no equivalent is found,
 .Sq \&?
 is used instead.
 .Pp
 Output width is limited to 78 visible columns unless literal input lines
 exceed this limit.
 .Pp
 The following
 .Fl O
 arguments are accepted:
 .Bl -tag -width Ds
 .It Cm indent Ns = Ns Ar indent
 The left margin for normal text is set to
 .Ar indent
 blank characters instead of the default of five for
 .Xr mdoc 7
 and seven for
 .Xr man 7 .
 Increasing this is not recommended; it may result in degraded formatting,
 for example overfull lines or ugly line breaks.
 .It Cm width Ns = Ns Ar width
 The output width is set to
 .Ar width ,
 which will normalise to \(>=60.
 .El
 .Ss HTML Output
 Output produced by
 .Fl T Ns Cm html
-conforms to HTML-4.01 strict.
+conforms to HTML5 using optional self-closing tags.
+Default styles use only CSS1.
+Equations rendered from
+.Xr eqn 7
+blocks use MathML.
 .Pp
 The
 .Pa example.style.css
 file documents style-sheet classes available for customising output.
 If a style-sheet is not specified with
 .Fl O Ns Ar style ,
 .Fl T Ns Cm html
-defaults to simple output readable in any graphical or text-based web
+defaults to simple output (via an embedded style-sheet)
+readable in any graphical or text-based web
 browser.
 .Pp
 Special characters are rendered in decimal-encoded UTF\-8.
 .Pp
 The following
 .Fl O
 arguments are accepted:
 .Bl -tag -width Ds
 .It Cm fragment
-Omit the
-.Aq !DOCTYPE
-declaration and the
-.Aq html ,
-.Aq head ,
-and
-.Aq body
-elements and only emit the subtree below the
-.Aq body
-element.
+Omit the  declaration and the , , and 
+elements and only emit the subtree below the  element.
 The
 .Cm style
 argument will be ignored.
 This is useful when embedding manual content within existing documents.
 .It Cm includes Ns = Ns Ar fmt
 The string
 .Ar fmt ,
 for example,
 .Ar ../src/%I.html ,
 is used as a template for linked header files (usually via the
 .Sq \&In
 macro).
 Instances of
 .Sq \&%I
 are replaced with the include filename.
 The default is not to present a
 hyperlink.
 .It Cm man Ns = Ns Ar fmt
 The string
 .Ar fmt ,
 for example,
 .Ar ../html%S/%N.%S.html ,
 is used as a template for linked manuals (usually via the
 .Sq \&Xr
 macro).
 Instances of
 .Sq \&%N
 and
 .Sq %S
 are replaced with the linked manual's name and section, respectively.
 If no section is included, section 1 is assumed.
 The default is not to
 present a hyperlink.
 .It Cm style Ns = Ns Ar style.css
 The file
 .Ar style.css
 is used for an external style-sheet.
 This must be a valid absolute or
 relative URI.
 .El
 .Ss Locale Output
 Locale-depending output encoding is triggered with
 .Fl T Ns Cm locale .
 This option is not available on all systems: systems without locale
 support, or those whose internal representation is not natively UCS-4,
 will fall back to
 .Fl T Ns Cm ascii .
 See
 .Sx ASCII Output
 for font style specification and available command-line arguments.
 .Ss Man Output
 Translate input format into
 .Xr man 7
 output format.
 This is useful for distributing manual sources to legacy systems
 lacking
 .Xr mdoc 7
 formatters.
 .Pp
 If
 .Xr mdoc 7
 is passed as input, it is translated into
 .Xr man 7 .
 If the input format is
 .Xr man 7 ,
 the input is copied to the output, expanding any
 .Xr roff 7
 .Sq so
 requests.
 The parser is also run, and as usual, the
 .Fl W
 level controls which
 .Sx DIAGNOSTICS
 are displayed before copying the input to the output.
 .Ss PDF Output
 PDF-1.1 output may be generated by
 .Fl T Ns Cm pdf .
 See
 .Sx PostScript Output
 for
 .Fl O
 arguments and defaults.
 .Ss PostScript Output
 PostScript
 .Qq Adobe-3.0
 Level-2 pages may be generated by
 .Fl T Ns Cm ps .
 Output pages default to letter sized and are rendered in the Times font
 family, 11-point.
 Margins are calculated as 1/9 the page length and width.
 Line-height is 1.4m.
 .Pp
 Special characters are rendered as in
 .Sx ASCII Output .
 .Pp
 The following
 .Fl O
 arguments are accepted:
 .Bl -tag -width Ds
 .It Cm paper Ns = Ns Ar name
 The paper size
 .Ar name
 may be one of
 .Ar a3 ,
 .Ar a4 ,
 .Ar a5 ,
 .Ar legal ,
 or
 .Ar letter .
 You may also manually specify dimensions as
 .Ar NNxNN ,
 width by height in millimetres.
 If an unknown value is encountered,
 .Ar letter
 is used.
 .El
 .Ss UTF\-8 Output
 Use
 .Fl T Ns Cm utf8
 to force a UTF\-8 locale.
 See
 .Sx Locale Output
 for details and options.
-.Ss XHTML Output
-Output produced by
-.Fl T Ns Cm xhtml
-conforms to XHTML-1.0 strict.
-.Pp
-See
-.Sx HTML Output
-for details; beyond generating XHTML tags instead of HTML tags, these
-output modes are identical.
+.Sh ENVIRONMENT
+.Bl -tag -width MANPAGER
+.It Ev MANPAGER
+Any non-empty value of the environment variable
+.Ev MANPAGER
+will be used instead of the standard pagination program,
+.Xr more 1 .
+.It Ev PAGER
+Specifies the pagination program to use when
+.Ev MANPAGER
+is not defined.
+If neither PAGER nor MANPAGER is defined,
+.Pa /usr/bin/more Fl s
+will be used.
+.El
 .Sh EXIT STATUS
 The
 .Nm
 utility exits with one of the following values, controlled by the message
 .Ar level
 associated with the
 .Fl W
 option:
 .Pp
 .Bl -tag -width Ds -compact
 .It 0
 No warnings or errors occurred, or those that did were ignored because
 they were lower than the requested
 .Ar level .
 .It 2
 At least one warning occurred, but no error, and
 .Fl W Ns Cm warning
 was specified.
 .It 3
 At least one parsing error occurred, but no fatal error, and
 .Fl W Ns Cm error
 or
 .Fl W Ns Cm warning
 was specified.
 .It 4
 A fatal parsing error occurred.
 .It 5
 Invalid command line arguments were specified.
 No input files have been read.
 .It 6
 An operating system error occurred, for example memory exhaustion or an
 error accessing input files.
 Such errors cause
 .Nm
 to exit at once, possibly in the middle of parsing or formatting a file.
 .El
 .Pp
 Note that selecting
 .Fl T Ns Cm lint
 output mode implies
 .Fl W Ns Cm warning .
 .Sh EXAMPLES
 To page manuals to the terminal:
 .Pp
 .Dl $ mandoc \-Wall,stop mandoc.1 2\*(Gt&1 | less
 .Dl $ mandoc mandoc.1 mdoc.3 mdoc.7 | less
 .Pp
 To produce HTML manuals with
 .Ar style.css
 as the style-sheet:
 .Pp
 .Dl $ mandoc \-Thtml -Ostyle=style.css mdoc.7 \*(Gt mdoc.7.html
 .Pp
 To check over a large set of manuals:
 .Pp
 .Dl $ mandoc \-Tlint `find /usr/src -name \e*\e.[1-9]`
 .Pp
 To produce a series of PostScript manuals for A4 paper:
 .Pp
 .Dl $ mandoc \-Tps \-Opaper=a4 mdoc.7 man.7 \*(Gt manuals.ps
 .Pp
 Convert a modern
 .Xr mdoc 7
 manual to the older
 .Xr man 7
 format, for use on systems lacking an
 .Xr mdoc 7
 parser:
 .Pp
 .Dl $ mandoc \-Tman foo.mdoc \*(Gt foo.man
 .Sh DIAGNOSTICS
 Messages displayed by
 .Nm
 follow this format:
 .Pp
 .D1 Nm Ns : Ar file : Ns Ar line : Ns Ar column : level : message : macro args
 .Pp
 Line and column numbers start at 1.
 Both are omitted for messages referring to an input file as a whole.
 Macro names and arguments are omitted where meaningless.
 Fatal messages about invalid command line arguments
 or operating system errors, for example when memory is exhausted,
 may also omit the
 .Ar file
 and
 .Ar level
 fields.
 .Pp
 Message levels have the following meanings:
 .Bl -tag -width "warning"
 .It Cm syserr
 Opening or reading an input file failed, so the parser cannot
 even be started and no output is produced from that input file.
 .It Cm fatal
 The parser is unable to parse a given input file at all.
 No formatted output is produced from that input file.
 .It Cm error
 An input file contains syntax that cannot be safely interpreted,
 either because it is invalid or because
 .Nm
 does not implement it yet.
 By discarding part of the input or inserting missing tokens,
 the parser is able to continue, and the error does not prevent
 generation of formatted output, but typically, preparing that
 output involves information loss, broken document structure
 or unintended formatting.
 .It Cm warning
 An input file uses obsolete, discouraged or non-portable syntax.
 All the same, the meaning of the input is unambiguous and a correct
 rendering can be produced.
 Documents causing warnings may render poorly when using other
 formatting tools instead of
 .Nm .
 .El
 .Pp
 Messages of the
 .Cm warning
 and
 .Cm error
 levels are hidden unless their level, or a lower level, is requested using a
 .Fl W
 option or
 .Fl T Ns Cm lint
 output mode.
 .Ss Warnings related to the document prologue
 .Bl -ohang
 .It Sy "missing manual title, using UNTITLED"
 .Pq mdoc
 A
 .Ic \&Dt
 macro has no arguments, or there is no
 .Ic \&Dt
 macro before the first non-prologue macro.
 .It Sy "missing manual title, using \(dq\(dq"
 .Pq man
 There is no
 .Ic \&TH
 macro, or it has no arguments.
 .It Sy "lower case character in document title"
 .Pq mdoc , man
 The title is still used as given in the
 .Ic \&Dt
 or
 .Ic \&TH
 macro.
 .It Sy "missing manual section, using \(dq\(dq"
 .Pq mdoc , man
 A
 .Ic \&Dt
 or
 .Ic \&TH
 macro lacks the mandatory section argument.
 .It Sy "unknown manual section"
 .Pq mdoc
 The section number in a
 .Ic \&Dt
 line is invalid, but still used.
-.It Sy "unknown manual volume or arch"
-.Pq mdoc
-The volume name in a
-.Ic \&Dt
-line is invalid, but still used.
-The manual is assumed to be architecture-independent.
 .It Sy "missing date, using today's date"
 .Pq mdoc, man
 The document was parsed as
 .Xr mdoc 7
 and it has no
 .Ic \&Dd
 macro, or the
 .Ic \&Dd
 macro has no arguments or only empty arguments;
 or the document was parsed as
 .Xr man 7
 and it has no
 .Ic \&TH
 macro, or the
 .Ic \&TH
 macro has less than three arguments or its third argument is empty.
 .It Sy "cannot parse date, using it verbatim"
 .Pq mdoc , man
 The date given in a
 .Ic \&Dd
 or
 .Ic \&TH
 macro does not follow the conventional format.
 .It Sy "missing Os macro, using \(dq\(dq"
 .Pq mdoc
 The default or current system is not shown in this case.
 .It Sy "duplicate prologue macro"
 .Pq mdoc
 One of the prologue macros occurs more than once.
 The last instance overrides all previous ones.
 .It Sy "late prologue macro"
 .Pq mdoc
 A
 .Ic \&Dd
 or
 .Ic \&Os
 macro occurs after some non-prologue macro, but still takes effect.
 .It Sy "skipping late title macro"
 .Pq mdoc
 The
 .Ic \&Dt
 macro can only occur before the first non-prologue macro
 because traditional formatters write the page header
 before parsing the document body.
 Even though this technical restriction does not apply to
 .Nm ,
 traditional semantics is preserved.
 The late macro is discarded including its arguments.
 .It Sy "prologue macros out of order"
 .Pq mdoc
 The prologue macros are not given in the conventional order
 .Ic \&Dd ,
 .Ic \&Dt ,
 .Ic \&Os .
 All three macros are used even when given in another order.
 .El
 .Ss Warnings regarding document structure
 .Bl -ohang
 .It Sy ".so is fragile, better use ln(1)"
 .Pq roff
 Including files only works when the parser program runs with the correct
 current working directory.
 .It Sy "no document body"
 .Pq mdoc , man
 The document body contains neither text nor macros.
 An empty document is shown, consisting only of a header and a footer line.
 .It Sy "content before first section header"
 .Pq mdoc , man
 Some macros or text precede the first
 .Ic \&Sh
 or
 .Ic \&SH
 section header.
 The offending macros and text are parsed and added to the top level
 of the syntax tree, outside any section block.
 .It Sy "first section is not NAME"
 .Pq mdoc
 The argument of the first
 .Ic \&Sh
 macro is not
 .Sq NAME .
 This may confuse
 .Xr makewhatis 8
 and
 .Xr apropos 1 .
 .It Sy "bad NAME section contents"
 .Pq mdoc
 The last node in the NAME section is not an
 .Ic \&Nd
 macro, or any preceding macro is not
 .Ic \&Nm ,
 or the NAME section is completely empty.
 This may confuse
 .Xr makewhatis 8
 and
 .Xr apropos 1 .
 .It Sy "sections out of conventional order"
 .Pq mdoc
 A standard section occurs after another section it usually precedes.
 All section titles are used as given,
 and the order of sections is not changed.
 .It Sy "duplicate section title"
 .Pq mdoc
 The same standard section title occurs more than once.
 .It Sy "unexpected section"
 .Pq mdoc
 A standard section header occurs in a section of the manual
 where it normally isn't useful.
+.It Sy "unusual Xr order"
+.Pq mdoc
+In the SEE ALSO section, an
+.Ic \&Xr
+macro with a lower section number follows one with a higher number,
+or two
+.Ic \&Xr
+macros refering to the same section are out of alphabetical order.
+.It Sy "unusual Xr punctuation"
+.Pq mdoc
+In the SEE ALSO section, punctuation between two
+.Ic \&Xr
+macros differs from a single comma, or there is trailing punctuation
+after the last
+.Ic \&Xr
+macro.
+.It Sy "AUTHORS section without An macro"
+.Pq mdoc
+An AUTHORS sections contains no
+.Ic \&An
+macros, or only empty ones.
+Probably, there are author names lacking markup.
 .El
 .Ss "Warnings related to macros and nesting"
 .Bl -ohang
 .It Sy "obsolete macro"
 .Pq mdoc
 See the
 .Xr mdoc 7
 manual for replacements.
 .It Sy "skipping paragraph macro"
 In
 .Xr mdoc 7
 documents, this happens
 .Bl -dash -compact
 .It
 at the beginning and end of sections and subsections
 .It
 right before non-compact lists and displays
 .It
 at the end of items in non-column, non-compact lists
 .It
 and for multiple consecutive paragraph macros.
 .El
 In
 .Xr man 7
 documents, it happens
 .Bl -dash -compact
 .It
 for empty
 .Ic \&P ,
 .Ic \&PP ,
 and
 .Ic \&LP
 macros
 .It
 for
 .Ic \&IP
 macros having neither head nor body arguments
 .It
 for
 .Ic \&br
 or
 .Ic \&sp
 right after
 .Ic \&SH
 or
 .Ic \&SS
 .El
 .It Sy "moving paragraph macro out of list"
 .Pq mdoc
 A list item in a
 .Ic \&Bl
 list contains a trailing paragraph macro.
 The paragraph macro is moved after the end of the list.
 .It Sy "skipping no-space macro"
 .Pq mdoc
 An input line begins with an
 .Ic \&Ns
 macro.
 The macro is ignored.
 .It Sy "blocks badly nested"
 .Pq mdoc
 If two blocks intersect, one should completely contain the other.
 Otherwise, rendered output is likely to look strange in any output
 format, and rendering in SGML-based output formats is likely to be
 outright wrong because such languages do not support badly nested
 blocks at all.
 Typical examples of badly nested blocks are
 .Qq Ic \&Ao \&Bo \&Ac \&Bc
 and
 .Qq Ic \&Ao \&Bq \&Ac .
 In these examples,
 .Ic \&Ac
 breaks
 .Ic \&Bo
 and
 .Ic \&Bq ,
 respectively.
 .It Sy "nested displays are not portable"
 .Pq mdoc
 A
 .Ic \&Bd ,
 .Ic \&D1 ,
 or
 .Ic \&Dl
 display occurs nested inside another
 .Ic \&Bd
 display.
 This works with
 .Nm ,
 but fails with most other implementations.
 .It Sy "moving content out of list"
 .Pq mdoc
 A
 .Ic \&Bl
 list block contains text or macros before the first
 .Ic \&It
 macro.
 The offending children are moved before the beginning of the list.
 .It Sy ".Vt block has child macro"
 .Pq mdoc
 The
 .Ic \&Vt
 macro supports plain text arguments only.
 Formatting may be ugly and semantic searching
 for the affected content might not work.
 .It Sy "fill mode already enabled, skipping"
 .Pq man
 A
 .Ic \&fi
 request occurs even though the document is still in fill mode,
 or already switched back to fill mode.
 It has no effect.
 .It Sy "fill mode already disabled, skipping"
 .Pq man
 An
 .Ic \&nf
 request occurs even though the document already switched to no-fill mode
 and did not switch back to fill mode yet.
 It has no effect.
 .It Sy "line scope broken"
 .Pq man
 While parsing the next-line scope of the previous macro,
 another macro is found that prematurely terminates the previous one.
 The previous, interrupted macro is deleted from the parse tree.
 .El
 .Ss "Warnings related to missing arguments"
 .Bl -ohang
 .It Sy "skipping empty request"
-.Pq roff
-The macro name is missing from a macro definition request.
+.Pq roff , eqn
+The macro name is missing from a macro definition request,
+or an
+.Xr eqn 7
+control statement or operation keyword lacks its required argument.
 .It Sy "conditional request controls empty scope"
 .Pq roff
 A conditional request is only useful if any of the following
 follows it on the same logical input line:
 .Bl -dash -compact
 .It
 The
 .Sq \e{
 keyword to open a multi-line scope.
 .It
 A request or macro or some text, resulting in a single-line scope.
 .It
 The immediate end of the logical line without any intervening whitespace,
 resulting in next-line scope.
 .El
 Here, a conditional request is followed by trailing whitespace only,
 and there is no other content on its logical input line.
 Note that it doesn't matter whether the logical input line is split
 across multiple physical input lines using
 .Sq \e
 line continuation characters.
 This is one of the rare cases
 where trailing whitespace is syntactically significant.
 The conditional request controls a scope containing whitespace only,
 so it is unlikely to have a significant effect,
 except that it may control a following
 .Ic \&el
 clause.
 .It Sy "skipping empty macro"
 .Pq mdoc
 The indicated macro has no arguments and hence no effect.
 .It Sy "empty argument, using 0n"
 .Pq mdoc
 The required width is missing after
 .Ic \&Bd
 or
 .Ic \&Bl
 .Fl offset
 or
 .Fl width.
 .It Sy "argument count wrong"
 .Pq mdoc , man
 The indicated macro has too few or too many arguments.
 The syntax tree will contain the wrong number of arguments as given.
 Formatting behaviour depends on the specific macro in question.
 Note that the same message may also occur as an ERROR, see below.
 .It Sy "missing display type, using -ragged"
 .Pq mdoc
 The
 .Ic \&Bd
 macro is invoked without the required display type.
 .It Sy "list type is not the first argument"
 .Pq mdoc
 In a
 .Ic \&Bl
 macro, at least one other argument precedes the type argument.
 The
 .Nm
 utility copes with any argument order, but some other
 .Xr mdoc 7
 implementations do not.
 .It Sy "missing -width in -tag list, using 8n"
 .Pq mdoc
 Every
 .Ic \&Bl
 macro having the
 .Fl tag
 argument requires
 .Fl width ,
 too.
 .It Sy "missing utility name, using \(dq\(dq"
 .Pq mdoc
 The
 .Ic \&Ex Fl std
 macro is called without an argument before
 .Ic \&Nm
 has first been called with an argument.
 .It Sy "empty head in list item"
 .Pq mdoc
 In a
 .Ic \&Bl
 .Fl diag ,
 .Fl hang ,
 .Fl inset ,
 .Fl ohang ,
 or
 .Fl tag
 list, an
 .Ic \&It
 macro lacks the required argument.
 The item head is left empty.
 .It Sy "empty list item"
 .Pq mdoc
 In a
 .Ic \&Bl
 .Fl bullet ,
 .Fl dash ,
 .Fl enum ,
 or
 .Fl hyphen
 list, an
 .Ic \&It
 block is empty.
 An empty list item is shown.
 .It Sy "missing font type"
 .Pq mdoc
 A
 .Ic \&Bf
 macro has no argument.
 It switches to the default font,
 .Cm \efR .
 .It Sy "unknown font type"
 .Pq mdoc
 The
 .Ic \&Bf
 argument is invalid.
 The default font
 .Cm \efR
 is used instead.
 .It Sy "missing -std argument, adding it"
 .Pq mdoc
 An
 .Ic \&Ex
 or
 .Ic \&Rv
 macro lacks the required
 .Fl std
 argument.
 The
 .Nm
 utility assumes
 .Fl std
 even when it is not specified, but other implementations may not.
+.It Sy "missing eqn box, using \(dq\(dq"
+.Pq eqn
+A diacritic mark or a binary operator is found,
+but there is nothing to the left of it.
+An empty box is inserted.
 .El
 .Ss "Warnings related to bad macro arguments"
 .Bl -ohang
 .It Sy "unterminated quoted argument"
 .Pq roff
 Macro arguments can be enclosed in double quote characters
 such that space characters and macro names contained in the quoted
 argument need not be escaped.
 The closing quote of the last argument of a macro can be omitted.
 However, omitting it is not recommended because it makes the code
 harder to read.
 .It Sy "duplicate argument"
 .Pq mdoc
 A
 .Ic \&Bd
 or
 .Ic \&Bl
 macro has more than one
 .Fl compact ,
 more than one
 .Fl offset ,
 or more than one
 .Fl width
 argument.
 All but the last instances of these arguments are ignored.
 .It Sy "skipping duplicate argument"
 .Pq mdoc
 An
 .Ic \&An
 macro has more than one
 .Fl split
 or
 .Fl nosplit
 argument.
 All but the first of these arguments are ignored.
 .It Sy "skipping duplicate display type"
 .Pq mdoc
 A
 .Ic \&Bd
 macro has more than one type argument; the first one is used.
 .It Sy "skipping duplicate list type"
 .Pq mdoc
 A
 .Ic \&Bl
 macro has more than one type argument; the first one is used.
 .It Sy "skipping -width argument"
 .Pq mdoc
 A
 .Ic \&Bl
 .Fl column ,
 .Fl diag ,
 .Fl ohang ,
 .Fl inset ,
 or
 .Fl item
 list has a
 .Fl width
 argument.
 That has no effect.
 .It Sy "unknown AT&T UNIX version"
 .Pq mdoc
 An
 .Ic \&At
 macro has an invalid argument.
 It is used verbatim, with
 .Qq "AT&T UNIX "
 prefixed to it.
+.It Sy "comma in function argument"
+.Pq mdoc
+An argument of an
+.Ic \&Fa
+or
+.Ic \&Fn
+macro contains a comma; it should probably be split into two arguments.
+.It Sy "parenthesis in function name"
+.Pq mdoc
+The first argument of an
+.Ic \&Fc
+or
+.Ic \&Fn
+macro contains an opening or closing parenthesis; that's probably wrong,
+parentheses are added automatically.
 .It Sy "invalid content in Rs block"
 .Pq mdoc
 An
 .Ic \&Rs
 block contains plain text or non-% macros.
 The bogus content is left in the syntax tree.
 Formatting may be poor.
 .It Sy "invalid Boolean argument"
 .Pq mdoc
 An
 .Ic \&Sm
 macro has an argument other than
 .Cm on
 or
 .Cm off .
 The invalid argument is moved out of the macro, which leaves the macro
 empty, causing it to toggle the spacing mode.
 .It Sy "unknown font, skipping request"
-.Pq man
+.Pq man , tbl
 A
 .Xr roff 7
 .Ic \&ft
-request has an invalid argument.
+request or a
+.Xr tbl 7
+.Ic \&f
+layout modifier has an unknown
+.Ar font
+argument.
 .El
 .Ss "Warnings related to plain text"
 .Bl -ohang
 .It Sy "blank line in fill mode, using .sp"
 .Pq mdoc
 The meaning of blank input lines is only well-defined in non-fill mode:
 In fill mode, line breaks of text input lines are not supposed to be
 significant.
 However, for compatibility with groff, blank lines in fill mode
 are replaced with
 .Ic \&sp
 requests.
 .It Sy "tab in filled text"
 .Pq mdoc , man
 The meaning of tab characters is only well-defined in non-fill mode:
 In fill mode, whitespace is not supposed to be significant
 on text input lines.
 As an implementation dependent choice, tab characters on text lines
 are passed through to the formatters in any case.
 Given that the text before the tab character will be filled,
 it is hard to predict which tab stop position the tab will advance to.
 .It Sy "whitespace at end of input line"
 .Pq mdoc , man , roff
 Whitespace at the end of input lines is almost never semantically
 significant \(em but in the odd case where it might be, it is
 extremely confusing when reviewing and maintaining documents.
 .It Sy "bad comment style"
 .Pq roff
 Comment lines start with a dot, a backslash, and a double-quote character.
 The
 .Nm
 utility treats the line as a comment line even without the backslash,
 but leaving out the backslash might not be portable.
 .It Sy "invalid escape sequence"
 .Pq roff
 An escape sequence has an invalid opening argument delimiter, lacks the
 closing argument delimiter, or the argument has too few characters.
 If the argument is incomplete,
 .Ic \e*
 and
 .Ic \en
 expand to an empty string,
 .Ic \eB
 to the digit
 .Sq 0 ,
 and
 .Ic \ew
 to the length of the incomplete argument.
 All other invalid escape sequences are ignored.
 .It Sy "undefined string, using \(dq\(dq"
 .Pq roff
 If a string is used without being defined before,
 its value is implicitly set to the empty string.
 However, defining strings explicitly before use
 keeps the code more readable.
 .El
 .Ss "Errors related to equations"
 .Bl -inset -compact
 .It "unexpected equation scope closure"
 .It "equation scope open on exit"
 .It "overlapping equation scopes"
 .It "unexpected end of equation"
-.It "equation syntax error"
 .El
 .Ss "Errors related to tables"
 .Bl -inset -compact
 .It "bad table syntax"
 .It "bad table option"
 .It "bad table layout"
 .It "no table layout cells specified"
 .It "no table data cells specified"
 .It "ignore data in cell"
 .It "data block still open"
 .It "ignoring extra data cells"
 .El
 .Ss "Errors related to roff, mdoc, and man code"
 .Bl -ohang
 .It Sy "input stack limit exceeded, infinite loop?"
 .Pq roff
 Explicit recursion limits are implemented for the following features,
 in order to prevent infinite loops:
 .Bl -dash -compact
 .It
 expansion of nested escape sequences
 including expansion of strings and number registers,
 .It
 expansion of nested user-defined macros,
 .It
 and
 .Ic \&so
 file inclusion.
 .El
 When a limit is hit, the output is incorrect, typically losing
 some content, but the parser can continue.
 .It Sy "skipping bad character"
 .Pq mdoc , man , roff
 The input file contains a byte that is not a printable
 .Xr ascii 7
 character.
 The message mentions the character number.
 The offending byte is replaced with a question mark
 .Pq Sq \&? .
 Consider editing the input file to replace the byte with an ASCII
 transliteration of the intended character.
 .It Sy "skipping unknown macro"
 .Pq mdoc , man , roff
 The first identifier on a request or macro line is neither recognized as a
 .Xr roff 7
 request, nor as a user-defined macro, nor, respectively, as an
 .Xr mdoc 7
 or
 .Xr man 7
 macro.
 It may be mistyped or unsupported.
 The request or macro is discarded including its arguments.
 .It Sy "skipping item outside list"
-.Pq mdoc
+.Pq mdoc , eqn
 An
 .Ic \&It
 macro occurs outside any
 .Ic \&Bl
-list.
+list, or an
+.Xr eqn 7
+.Ic above
+delimiter occurs outside any pile.
 It is discarded including its arguments.
 .It Sy "skipping column outside column list"
 .Pq mdoc
 A
 .Ic \&Ta
 macro occurs outside any
 .Ic \&Bl Fl column
 block.
 It is discarded including its arguments.
 .It Sy "skipping end of block that is not open"
 .Pq mdoc , man , eqn , tbl , roff
 Various syntax elements can only be used to explicitly close blocks
 that have previously been opened.
 An
 .Xr mdoc 7
 block closing macro, a
 .Xr man 7
 .Ic \&RE
 or
 .Ic \&UE
-macro, or the end of an equation, table, or
+macro, an
+.Xr eqn 7
+right delimiter or closing brace, or the end of an equation, table, or
 .Xr roff 7
 conditional request is encountered but no matching block is open.
 The offending request or macro is discarded.
 .It Sy "inserting missing end of block"
 .Pq mdoc , tbl
 Various
 .Xr mdoc 7
 macros as well as tables require explicit closing by dedicated macros.
 A block that doesn't support bad nesting
 ends before all of its children are properly closed.
 The open child nodes are closed implicitly.
 .It Sy "scope open on exit"
 .Pq mdoc , man , eqn , tbl , roff
 At the end of the document, an explicit
 .Xr mdoc 7
 block, a
 .Xr man 7
 next-line scope or
 .Ic \&RS
 or
 .Ic \&UR
 block, an equation, table, or
 .Xr roff 7
 conditional or ignore block is still open.
 The open block is closed implicitly.
 .It Sy "escaped character not allowed in a name"
 .Pq roff
 Macro, string and register identifiers consist of printable,
 non-whitespace ASCII characters.
 Escape sequences and characters and strings expressed in terms of them
 cannot form part of a name.
 The first argument of an
 .Ic \&am ,
 .Ic \&as ,
 .Ic \&de ,
 .Ic \&ds ,
 .Ic \&nr ,
 or
 .Ic \&rr
 request, or any argument of an
 .Ic \&rm
 request, or the name of a request or user defined macro being called,
 is terminated by an escape sequence.
 In the cases of
 .Ic \&as ,
 .Ic \&ds ,
 and
 .Ic \&nr ,
 the request has no effect at all.
 In the cases of
 .Ic \&am ,
 .Ic \&de ,
 .Ic \&rr ,
 and
 .Ic \&rm ,
 what was parsed up to this point is used as the arguments to the request,
 and the rest of the input line is discarded including the escape sequence.
 When parsing for a request or a user-defined macro name to be called,
 only the escape sequence is discarded.
 The characters preceding it are used as the request or macro name,
 the characters following it are used as the arguments to the request or macro.
 .It Sy "argument count wrong"
 .Pq mdoc , man , roff
 The indicated request or macro has too few or too many arguments.
 The syntax tree will contain the wrong number of arguments as given.
 Formatting behaviour depends on the specific request or macro in question.
 Note that the same message may also occur as a WARNING, see above.
+.It Sy "NOT IMPLEMENTED: Bd -file"
+.Pq mdoc
+For security reasons, the
+.Ic \&Bd
+macro does not support the
+.Fl file
+argument.
+By requesting the inclusion of a sensitive file, a malicious document
+might otherwise trick a privileged user into inadvertently displaying
+the file on the screen, revealing the file content to bystanders.
+The argument is ignored including the file name following it.
 .It Sy "missing list type, using -item"
 .Pq mdoc
 A
 .Ic \&Bl
 macro fails to specify the list type.
 .It Sy "missing manual name, using \(dq\(dq"
 .Pq mdoc
 The first call to
 .Ic \&Nm
 lacks the required argument.
 .It Sy "uname(3) system call failed, using UNKNOWN"
 .Pq mdoc
 The
 .Ic \&Os
 macro is called without arguments, and the
 .Xr uname 3
 system call failed.
 As a workaround,
 .Nm
 can be compiled with
 .Sm off
 .Fl D Cm OSNAME=\(dq\e\(dq Ar string Cm \e\(dq\(dq .
 .Sm on
 .It Sy "unknown standard specifier"
 .Pq mdoc
 An
 .Ic \&St
 macro has an unknown argument and is discarded.
 .It Sy "skipping request without numeric argument"
-.Pq roff
+.Pq roff , eqn
 An
 .Ic \&it
-request has a non-numeric or negative argument or no argument at all.
-The invalid request is ignored.
+request or an
+.Xr eqn 7
+.Ic \&size
+or
+.Ic \&gsize
+statement has a non-numeric or negative argument or no argument at all.
+The invalid request or statement is ignored.
 .It Sy "skipping all arguments"
 .Pq mdoc , man , eqn , roff
 An
 .Xr mdoc 7
 .Ic \&Bt ,
 .Ic \&Ed ,
 .Ic \&Ef ,
 .Ic \&Ek ,
 .Ic \&El ,
 .Ic \&Re ,
 or
 .Ic \&Ud
 macro, an
 .Ic \&It
 macro in a list that don't support item heads, a
 .Xr man 7
 .Ic \&LP ,
 .Ic \&P ,
 or
 .Ic \&PP
 macro, an
 .Xr eqn 7
+.Ic \&EQ
+or
 .Ic \&EN
 macro, or a
 .Xr roff 7
 .Sq \&..
 block closing request is invoked with at least one argument.
 All arguments are ignored.
 .It Sy "skipping excess arguments"
 .Pq mdoc , roff
 The
 .Ic \&Bf
 macro is invoked with more than one argument, or a request of the
 .Ic \&de
 family is invoked with more than two arguments.
 The excess arguments are ignored.
 .El
 .Ss FATAL errors
 .Bl -ohang
 .It Sy "input too large"
 .Pq mdoc , man
 Currently,
 .Nm
 cannot handle input files larger than its arbitrary size limit
 of 2^31 bytes (2 Gigabytes).
 Since useful manuals are always small, this is not a problem in practice.
 Parsing is aborted as soon as the condition is detected.
-.It Sy "NOT IMPLEMENTED: Bd -file"
-.Pq mdoc
-For security reasons, the
-.Ic \&Bd
-macro does not support the
-.Fl file
-argument.
-By requesting the inclusion of a sensitive file, a malicious document
-might otherwise trick a privileged user into inadvertently displaying
-the file on the screen, revealing the file content to bystanders.
-The parser exits immediately.
 .It Sy "NOT IMPLEMENTED: .so with absolute path or \(dq..\(dq"
 .Pq roff
 For security reasons,
 .Nm
 allows
 .Ic \&so
 file inclusion requests only with relative paths
 and only without ascending to any parent directory.
 By requesting the inclusion of a sensitive file, a malicious document
 might otherwise trick a privileged user into inadvertently displaying
 the file on the screen, revealing the file content to bystanders.
 The parser exits immediately.
 .It Sy ".so request failed"
 .Pq roff
 Servicing a
 .Ic \&so
 request requires reading an external file.
 While trying to do so, an
 .Xr open 2 ,
 .Xr stat 2 ,
 or
 .Xr read 2
 system call failed.
 The parser exits immediately.
 Before showing this message,
 .Nm
 always shows another message explaining why the system call failed.
 .El
 .Sh COMPATIBILITY
 This section summarises
 .Nm
 compatibility with GNU troff.
 Each input and output format is separately noted.
 .Ss ASCII Compatibility
 .Bl -bullet -compact
 .It
 Unrenderable unicode codepoints specified with
 .Sq \e[uNNNN]
 escapes are printed as
 .Sq \&?
 in mandoc.
 In GNU troff, these raise an error.
 .It
 The
 .Sq \&Bd \-literal
 and
 .Sq \&Bd \-unfilled
 macros of
 .Xr mdoc 7
 in
 .Fl T Ns Cm ascii
 are synonyms, as are \-filled and \-ragged.
 .It
 In historic GNU troff, the
 .Sq \&Pa
 .Xr mdoc 7
 macro does not underline when scoped under an
 .Sq \&It
 in the FILES section.
 This behaves correctly in
 .Nm .
 .It
 A list or display following the
 .Sq \&Ss
 .Xr mdoc 7
 macro in
 .Fl T Ns Cm ascii
 does not assert a prior vertical break, just as it doesn't with
 .Sq \&Sh .
 .It
 The
 .Sq \&na
 .Xr man 7
 macro in
 .Fl T Ns Cm ascii
 has no effect.
 .It
 Words aren't hyphenated.
 .El
-.Ss HTML/XHTML Compatibility
+.Ss HTML Compatibility
 .Bl -bullet -compact
 .It
 The
 .Sq \efP
 escape will revert the font to the previous
 .Sq \ef
 escape, not to the last rendered decoration, which is now dictated by
 CSS instead of hard-coded.
 It also will not span past the current scope,
 for the same reason.
 Note that in
 .Sx ASCII Output
 mode, this will work fine.
 .It
 The
 .Xr mdoc 7
 .Sq \&Bl \-hang
 and
 .Sq \&Bl \-tag
 list types render similarly (no break following overreached left-hand
 side) due to the expressive constraints of HTML.
 .It
 The
 .Xr man 7
 .Sq IP
 and
 .Sq TP
 lists render similarly.
 .El
 .Sh SEE ALSO
 .Xr eqn 7 ,
 .Xr man 7 ,
 .Xr mandoc_char 7 ,
 .Xr mdoc 7 ,
 .Xr roff 7 ,
 .Xr tbl 7
 .Sh AUTHORS
 The
 .Nm
 utility was written by
 .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
 .Sh CAVEATS
 In
 .Fl T Ns Cm html
 and
 .Fl T Ns Cm xhtml ,
 the maximum size of an element attribute is determined by
 .Dv BUFSIZ ,
 which is usually 1024 bytes.
 Be aware of this when setting long link
 formats such as
 .Fl O Ns Cm style Ns = Ns Ar really/long/link .
 .Pp
 Nesting elements within next-line element scopes of
 .Fl m Ns Cm an ,
 such as
 .Sq br
 within an empty
 .Sq B ,
 will confuse
 .Fl T Ns Cm html
 and
 .Fl T Ns Cm xhtml
 and cause them to forget the formatting of the prior next-line scope.
 .Pp
 The
 .Sq \(aq
 control character is an alias for the standard macro control character
 and does not emit a line-break as stipulated in GNU troff.
Index: vendor/mdocml/dist/mandoc.3
===================================================================
--- vendor/mdocml/dist/mandoc.3	(revision 275396)
+++ vendor/mdocml/dist/mandoc.3	(revision 275397)
@@ -1,661 +1,738 @@
-.\"	$Id: mandoc.3,v 1.25 2014/08/05 05:48:56 schwarze Exp $
+.\"	$Id: mandoc.3,v 1.29 2014/11/26 23:42:14 schwarze Exp $
 .\"
 .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
 .\" Copyright (c) 2010 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" copyright notice and this permission notice appear in all copies.
 .\"
 .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: August 5 2014 $
+.Dd $Mdocdate: November 26 2014 $
 .Dt MANDOC 3
 .Os
 .Sh NAME
 .Nm mandoc ,
 .Nm man_deroff ,
 .Nm man_meta ,
 .Nm man_mparse ,
 .Nm man_node ,
 .Nm mdoc_deroff ,
 .Nm mdoc_meta ,
 .Nm mdoc_node ,
 .Nm mparse_alloc ,
 .Nm mparse_free ,
 .Nm mparse_getkeep ,
 .Nm mparse_keep ,
+.Nm mparse_open ,
 .Nm mparse_readfd ,
 .Nm mparse_reset ,
 .Nm mparse_result ,
 .Nm mparse_strerror ,
 .Nm mparse_strlevel
+.Nm mparse_wait ,
 .Nd mandoc macro compiler library
 .Sh LIBRARY
 .Lb libmandoc
 .Sh SYNOPSIS
 .In sys/types.h
 .In mandoc.h
 .Fd "#define ASCII_NBRSP"
 .Fd "#define ASCII_HYPH"
 .Fd "#define ASCII_BREAK"
 .Ft struct mparse *
 .Fo mparse_alloc
 .Fa "int options"
 .Fa "enum mandoclevel wlevel"
 .Fa "mandocmsg mmsg"
+.Fa "const struct mchars *mchars"
 .Fa "char *defos"
 .Fc
 .Ft void
 .Fo (*mandocmsg)
 .Fa "enum mandocerr errtype"
 .Fa "enum mandoclevel level"
 .Fa "const char *file"
 .Fa "int line"
 .Fa "int col"
 .Fa "const char *msg"
 .Fc
 .Ft void
 .Fo mparse_free
 .Fa "struct mparse *parse"
 .Fc
 .Ft const char *
 .Fo mparse_getkeep
 .Fa "const struct mparse *parse"
 .Fc
 .Ft void
 .Fo mparse_keep
 .Fa "struct mparse *parse"
 .Fc
 .Ft "enum mandoclevel"
+.Fo mparse_open
+.Fa "struct mparse *parse"
+.Fa "int *fd"
+.Fa "const char *fname"
+.Fc
+.Ft "enum mandoclevel"
 .Fo mparse_readfd
 .Fa "struct mparse *parse"
 .Fa "int fd"
 .Fa "const char *fname"
 .Fc
 .Ft void
 .Fo mparse_reset
 .Fa "struct mparse *parse"
 .Fc
 .Ft void
 .Fo mparse_result
 .Fa "struct mparse *parse"
 .Fa "struct mdoc **mdoc"
 .Fa "struct man **man"
 .Fa "char **sodest"
 .Fc
 .Ft "const char *"
 .Fo mparse_strerror
 .Fa "enum mandocerr"
 .Fc
 .Ft "const char *"
 .Fo mparse_strlevel
 .Fa "enum mandoclevel"
 .Fc
+.Ft "enum mandoclevel"
+.Fo mparse_wait
+.Fa "struct mparse *parse"
+.Fc
 .In sys/types.h
 .In mandoc.h
 .In mdoc.h
 .Ft void
 .Fo mdoc_deroff
 .Fa "char **dest"
 .Fa "const struct mdoc_node *node"
 .Fc
 .Ft "const struct mdoc_meta *"
 .Fo mdoc_meta
 .Fa "const struct mdoc *mdoc"
 .Fc
 .Ft "const struct mdoc_node *"
 .Fo mdoc_node
 .Fa "const struct mdoc *mdoc"
 .Fc
 .Vt extern const char * const * mdoc_argnames;
 .Vt extern const char * const * mdoc_macronames;
 .In sys/types.h
 .In mandoc.h
 .In man.h
 .Ft void
 .Fo man_deroff
 .Fa "char **dest"
 .Fa "const struct man_node *node"
 .Fc
 .Ft "const struct man_meta *"
 .Fo man_meta
 .Fa "const struct man *man"
 .Fc
 .Ft "const struct mparse *"
 .Fo man_mparse
 .Fa "const struct man *man"
 .Fc
 .Ft "const struct man_node *"
 .Fo man_node
 .Fa "const struct man *man"
 .Fc
 .Vt extern const char * const * man_macronames;
 .Sh DESCRIPTION
 The
 .Nm mandoc
 library parses a
 .Ux
 manual into an abstract syntax tree (AST).
 .Ux
 manuals are composed of
 .Xr mdoc 7
 or
 .Xr man 7 ,
 and may be mixed with
 .Xr roff 7 ,
 .Xr tbl 7 ,
 and
 .Xr eqn 7
 invocations.
 .Pp
 The following describes a general parse sequence:
 .Bl -enum
 .It
 initiate a parsing sequence with
+.Xr mchars_alloc 3
+and
 .Fn mparse_alloc ;
 .It
 parse files or file descriptors with
 .Fn mparse_readfd ;
 .It
 retrieve a parsed syntax tree, if the parse was successful, with
 .Fn mparse_result ;
 .It
 iterate over parse nodes with
 .Fn mdoc_node
 or
 .Fn man_node ;
 .It
 free all allocated memory with
-.Fn mparse_free ,
+.Fn mparse_free
+and
+.Xr mchars_free 3 ,
 or invoke
 .Fn mparse_reset
 and parse new files.
 .El
 .Sh REFERENCE
 This section documents the functions, types, and variables available
 via
 .In mandoc.h ,
 with the exception of those documented in
 .Xr mandoc_escape 3
 and
 .Xr mchars_alloc 3 .
 .Ss Types
 .Bl -ohang
 .It Vt "enum mandocerr"
 A fatal error, error, or warning message during parsing.
 .It Vt "enum mandoclevel"
 A classification of an
 .Vt "enum mandocerr"
 as regards system operation.
+.It Vt "struct mchars"
+An opaque pointer to a a character table.
+Created with
+.Xr mchars_alloc 3
+and freed with
+.Xr mchars_free 3 .
 .It Vt "struct mparse"
 An opaque pointer to a running parse sequence.
 Created with
 .Fn mparse_alloc
 and freed with
 .Fn mparse_free .
 This may be used across parsed input if
 .Fn mparse_reset
 is called between parses.
 .It Vt "mandocmsg"
 A prototype for a function to handle fatal error, error, and warning
 messages emitted by the parser.
 .El
 .Ss Functions
 .Bl -ohang
 .It Fn man_deroff
 Obtain a text-only representation of a
 .Vt struct man_node ,
 including text contained in its child nodes.
 To be used on children of the pointer returned from
 .Fn man_node .
 When it is no longer needed, the pointer returned from
 .Fn man_deroff
 can be passed to
 .Xr free 3 .
 .It Fn man_meta
 Obtain the meta-data of a successful
 .Xr man 7
 parse.
 This may only be used on a pointer returned by
 .Fn mparse_result .
 Declared in
 .In man.h ,
 implemented in
 .Pa man.c .
 .It Fn man_mparse
 Get the parser used for the current output.
 Declared in
 .In man.h ,
 implemented in
 .Pa man.c .
 .It Fn man_node
 Obtain the root node of a successful
 .Xr man 7
 parse.
 This may only be used on a pointer returned by
 .Fn mparse_result .
 Declared in
 .In man.h ,
 implemented in
 .Pa man.c .
 .It Fn mdoc_deroff
 Obtain a text-only representation of a
 .Vt struct mdoc_node ,
 including text contained in its child nodes.
 To be used on children of the pointer returned from
 .Fn mdoc_node .
 When it is no longer needed, the pointer returned from
 .Fn mdoc_deroff
 can be passed to
 .Xr free 3 .
 .It Fn mdoc_meta
 Obtain the meta-data of a successful
 .Xr mdoc
 parse.
 This may only be used on a pointer returned by
 .Fn mparse_result .
 Declared in
 .In mdoc.h ,
 implemented in
 .Pa mdoc.c .
 .It Fn mdoc_node
 Obtain the root node of a successful
 .Xr mdoc
 parse.
 This may only be used on a pointer returned by
 .Fn mparse_result .
 Declared in
 .In mdoc.h ,
 implemented in
 .Pa mdoc.c .
 .It Fn mparse_alloc
 Allocate a parser.
 The arguments have the following effect:
 .Bl -tag -offset 5n -width inttype
 .It Ar options
 When the
 .Dv MPARSE_MDOC
 or
 .Dv MPARSE_MAN
 bit is set, only that parser is used.
 Otherwise, the document type is automatically detected.
 .Pp
 When the
 .Dv MPARSE_SO
 bit is set,
 .Xr roff 7
 .Ic \&so
 file inclusion requests are always honoured.
 Otherwise, if the request is the only content in an input file,
 only the file name is remembered, to be returned in the
 .Fa sodest
 argument of
 .Fn mparse_result .
 .Pp
 When the
 .Dv MPARSE_QUICK
 bit is set, parsing is aborted after the NAME section.
 This is for example useful in
 .Xr makewhatis 8
 .Fl Q
 to quickly build minimal databases.
 .It Ar wlevel
 Can be set to
 .Dv MANDOCLEVEL_FATAL ,
 .Dv MANDOCLEVEL_ERROR ,
 or
 .Dv MANDOCLEVEL_WARNING .
 Messages below the selected level will be suppressed.
 .It Ar mmsg
 A callback function to handle errors and warnings.
 See
 .Pa main.c
 for an example.
+.It Ar mchars
+An opaque pointer to a a character table obtained from
+.Xr mchars_alloc 3 .
 .It Ar defos
 A default string for the
 .Xr mdoc 7
 .Sq \&Os
 macro, overriding the
 .Dv OSNAME
 preprocessor definition and the results of
 .Xr uname 3 .
 .El
 .Pp
 The same parser may be used for multiple files so long as
 .Fn mparse_reset
 is called between parses.
 .Fn mparse_free
 must be called to free the memory allocated by this function.
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
 .It Fn mparse_free
 Free all memory allocated by
 .Fn mparse_alloc .
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
 .It Fn mparse_getkeep
 Acquire the keep buffer.
 Must follow a call of
 .Fn mparse_keep .
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
 .It Fn mparse_keep
 Instruct the parser to retain a copy of its parsed input.
 This can be acquired with subsequent
 .Fn mparse_getkeep
 calls.
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
+.It Fn mparse_open
+If the
+.Fa fname
+ends in
+.Pa .gz ,
+open with
+.Xr gunzip 1 ;
+otherwise, with
+.Xr open 2 .
+If
+.Xr open 2
+fails, append
+.Pa .gz
+and try with
+.Xr gunzip 1 .
+Return a file descriptor open for reading in
+.Fa fd ,
+or -1 on failure.
+It can be passed to
+.Fn mparse_readfd
+or used directly.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
 .It Fn mparse_readfd
 Parse a file or file descriptor.
 If
 .Va fd
-is -1,
+is -1, open
 .Va fname
-is opened for reading.
+with
+.Fn mparse_open .
 Otherwise,
 .Va fname
 is assumed to be the name associated with
 .Va fd .
-This may be called multiple times with different parameters; however,
+Calls
+.Fn mparse_wait
+before returning.
+This function may be called multiple times with different parameters; however,
 .Fn mparse_reset
 should be invoked between parses.
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
 .It Fn mparse_reset
 Reset a parser so that
 .Fn mparse_readfd
 may be used again.
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
 .It Fn mparse_result
 Obtain the result of a parse.
 Only successful parses
 .Po
 i.e., those where
 .Fn mparse_readfd
 returned less than MANDOCLEVEL_FATAL
 .Pc
 should invoke this function, in which case one of the three pointers will
 be filled in.
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
 .It Fn mparse_strerror
 Return a statically-allocated string representation of an error code.
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
 .It Fn mparse_strlevel
 Return a statically-allocated string representation of a level code.
+Declared in
+.In mandoc.h ,
+implemented in
+.Pa read.c .
+.It Fn mparse_wait
+Bury a
+.Xr gunzip 1
+child process that was spawned with
+.Fn mparse_open .
+To be called after the parse sequence is complete.
+Not needed after
+.Fn mparse_readfd ,
+but does no harm in that case, either.
+Returns
+.Dv MANDOCLEVEL_OK
+on success and
+.Dv MANDOCLEVEL_SYSERR
+on failure, that is, when
+.Xr wait 2
+fails, or when
+.Xr gunzip 1
+died from a signal or exited with non-zero status.
 Declared in
 .In mandoc.h ,
 implemented in
 .Pa read.c .
 .El
 .Ss Variables
 .Bl -ohang
 .It Va man_macronames
 The string representation of a man macro as indexed by
 .Vt "enum mant" .
 .It Va mdoc_argnames
 The string representation of a mdoc macro argument as indexed by
 .Vt "enum mdocargt" .
 .It Va mdoc_macronames
 The string representation of a mdoc macro as indexed by
 .Vt "enum mdoct" .
 .El
 .Sh IMPLEMENTATION NOTES
 This section consists of structural documentation for
 .Xr mdoc 7
 and
 .Xr man 7
 syntax trees and strings.
 .Ss Man and Mdoc Strings
 Strings may be extracted from mdoc and man meta-data, or from text
 nodes (MDOC_TEXT and MAN_TEXT, respectively).
 These strings have special non-printing formatting cues embedded in the
 text itself, as well as
 .Xr roff 7
 escapes preserved from input.
 Implementing systems will need to handle both situations to produce
 human-readable text.
 In general, strings may be assumed to consist of 7-bit ASCII characters.
 .Pp
 The following non-printing characters may be embedded in text strings:
 .Bl -tag -width Ds
 .It Dv ASCII_NBRSP
 A non-breaking space character.
 .It Dv ASCII_HYPH
 A soft hyphen.
 .It Dv ASCII_BREAK
 A breakable zero-width space.
 .El
 .Pp
 Escape characters are also passed verbatim into text strings.
 An escape character is a sequence of characters beginning with the
 backslash
 .Pq Sq \e .
 To construct human-readable text, these should be intercepted with
 .Xr mandoc_escape 3
 and converted with one the functions described in
 .Xr mchars_alloc 3 .
 .Ss Man Abstract Syntax Tree
 This AST is governed by the ontological rules dictated in
 .Xr man 7
 and derives its terminology accordingly.
 .Pp
 The AST is composed of
 .Vt struct man_node
 nodes with element, root and text types as declared by the
 .Va type
 field.
 Each node also provides its parse point (the
 .Va line ,
 .Va sec ,
 and
 .Va pos
 fields), its position in the tree (the
 .Va parent ,
 .Va child ,
 .Va next
 and
 .Va prev
 fields) and some type-specific data.
 .Pp
 The tree itself is arranged according to the following normal form,
 where capitalised non-terminals represent nodes.
 .Pp
 .Bl -tag -width "ELEMENTXX" -compact
 .It ROOT
 \(<- mnode+
 .It mnode
 \(<- ELEMENT | TEXT | BLOCK
 .It BLOCK
 \(<- HEAD BODY
 .It HEAD
 \(<- mnode*
 .It BODY
 \(<- mnode*
 .It ELEMENT
 \(<- ELEMENT | TEXT*
 .It TEXT
 \(<- [[:ascii:]]*
 .El
 .Pp
 The only elements capable of nesting other elements are those with
 next-line scope as documented in
 .Xr man 7 .
 .Ss Mdoc Abstract Syntax Tree
 This AST is governed by the ontological
 rules dictated in
 .Xr mdoc 7
 and derives its terminology accordingly.
 .Qq In-line
 elements described in
 .Xr mdoc 7
 are described simply as
 .Qq elements .
 .Pp
 The AST is composed of
 .Vt struct mdoc_node
 nodes with block, head, body, element, root and text types as declared
 by the
 .Va type
 field.
 Each node also provides its parse point (the
 .Va line ,
 .Va sec ,
 and
 .Va pos
 fields), its position in the tree (the
 .Va parent ,
 .Va child ,
 .Va nchild ,
 .Va next
 and
 .Va prev
 fields) and some type-specific data, in particular, for nodes generated
 from macros, the generating macro in the
 .Va tok
 field.
 .Pp
 The tree itself is arranged according to the following normal form,
 where capitalised non-terminals represent nodes.
 .Pp
 .Bl -tag -width "ELEMENTXX" -compact
 .It ROOT
 \(<- mnode+
 .It mnode
 \(<- BLOCK | ELEMENT | TEXT
 .It BLOCK
 \(<- HEAD [TEXT] (BODY [TEXT])+ [TAIL [TEXT]]
 .It ELEMENT
 \(<- TEXT*
 .It HEAD
 \(<- mnode*
 .It BODY
 \(<- mnode* [ENDBODY mnode*]
 .It TAIL
 \(<- mnode*
 .It TEXT
 \(<- [[:ascii:]]*
 .El
 .Pp
 Of note are the TEXT nodes following the HEAD, BODY and TAIL nodes of
 the BLOCK production: these refer to punctuation marks.
 Furthermore, although a TEXT node will generally have a non-zero-length
 string, in the specific case of
 .Sq \&.Bd \-literal ,
 an empty line will produce a zero-length string.
 Multiple body parts are only found in invocations of
 .Sq \&Bl \-column ,
 where a new body introduces a new phrase.
 .Pp
 The
 .Xr mdoc 7
 syntax tree accommodates for broken block structures as well.
 The ENDBODY node is available to end the formatting associated
 with a given block before the physical end of that block.
 It has a non-null
 .Va end
 field, is of the BODY
 .Va type ,
 has the same
 .Va tok
 as the BLOCK it is ending, and has a
 .Va pending
 field pointing to that BLOCK's BODY node.
 It is an indirect child of that BODY node
 and has no children of its own.
 .Pp
 An ENDBODY node is generated when a block ends while one of its child
 blocks is still open, like in the following example:
 .Bd -literal -offset indent
 \&.Ao ao
 \&.Bo bo ac
 \&.Ac bc
 \&.Bc end
 .Ed
 .Pp
 This example results in the following block structure:
 .Bd -literal -offset indent
 BLOCK Ao
     HEAD Ao
     BODY Ao
         TEXT ao
         BLOCK Bo, pending -> Ao
             HEAD Bo
             BODY Bo
                 TEXT bo
                 TEXT ac
                 ENDBODY Ao, pending -> Ao
                 TEXT bc
 TEXT end
 .Ed
 .Pp
 Here, the formatting of the
 .Sq \&Ao
 block extends from TEXT ao to TEXT ac,
 while the formatting of the
 .Sq \&Bo
 block extends from TEXT bo to TEXT bc.
 It renders as follows in
 .Fl T Ns Cm ascii
 mode:
 .Pp
 .Dl  bc] end
 .Pp
 Support for badly-nested blocks is only provided for backward
 compatibility with some older
 .Xr mdoc 7
 implementations.
 Using badly-nested blocks is
 .Em strongly discouraged ;
 for example, the
 .Fl T Ns Cm html
 and
 .Fl T Ns Cm xhtml
 front-ends to
 .Xr mandoc 1
 are unable to render them in any meaningful way.
 Furthermore, behaviour when encountering badly-nested blocks is not
 consistent across troff implementations, especially when using multiple
 levels of badly-nested blocks.
 .Sh SEE ALSO
 .Xr mandoc 1 ,
 .Xr mandoc_escape 3 ,
 .Xr mandoc_malloc 3 ,
 .Xr mchars_alloc 3 ,
 .Xr eqn 7 ,
 .Xr man 7 ,
 .Xr mandoc_char 7 ,
 .Xr mdoc 7 ,
 .Xr roff 7 ,
 .Xr tbl 7
 .Sh AUTHORS
 The
 .Nm
 library was written by
 .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
Index: vendor/mdocml/dist/mandoc.c
===================================================================
--- vendor/mdocml/dist/mandoc.c	(revision 275396)
+++ vendor/mdocml/dist/mandoc.c	(revision 275397)
@@ -1,600 +1,603 @@
-/*	$Id: mandoc.c,v 1.83 2014/07/06 19:09:00 schwarze Exp $ */
+/*	$Id: mandoc.c,v 1.88 2014/10/28 13:24:44 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "libmandoc.h"
 
 #define DATESIZE 32
 
 static	int	 a2time(time_t *, const char *, const char *);
 static	char	*time2a(time_t);
 
 
 enum mandoc_esc
 mandoc_escape(const char **end, const char **start, int *sz)
 {
 	const char	*local_start;
 	int		 local_sz;
 	char		 term;
 	enum mandoc_esc	 gly;
 
 	/*
 	 * When the caller doesn't provide return storage,
 	 * use local storage.
 	 */
 
 	if (NULL == start)
 		start = &local_start;
 	if (NULL == sz)
 		sz = &local_sz;
 
 	/*
 	 * Beyond the backslash, at least one input character
 	 * is part of the escape sequence.  With one exception
 	 * (see below), that character won't be returned.
 	 */
 
 	gly = ESCAPE_ERROR;
 	*start = ++*end;
 	*sz = 0;
 	term = '\0';
 
 	switch ((*start)[-1]) {
 	/*
 	 * First the glyphs.  There are several different forms of
 	 * these, but each eventually returns a substring of the glyph
 	 * name.
 	 */
 	case '(':
 		gly = ESCAPE_SPECIAL;
 		*sz = 2;
 		break;
 	case '[':
 		gly = ESCAPE_SPECIAL;
-		/*
-		 * Unicode escapes are defined in groff as \[uXXXX] to
-		 * \[u10FFFF], where the contained value must be a valid
-		 * Unicode codepoint.  Here, however, only check whether
-		 * it's not a zero-width escape.
-		 */
-		if ('u' == (*start)[0] && ']' != (*start)[1])
-			gly = ESCAPE_UNICODE;
 		term = ']';
 		break;
 	case 'C':
 		if ('\'' != **start)
 			return(ESCAPE_ERROR);
 		*start = ++*end;
-		if ('u' == (*start)[0] && '\'' != (*start)[1])
-			gly = ESCAPE_UNICODE;
-		else
-			gly = ESCAPE_SPECIAL;
+		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.
 	 */
 	case 'F':
 		/* FALLTHROUGH */
 	case 'g':
 		/* FALLTHROUGH */
 	case 'k':
 		/* FALLTHROUGH */
 	case 'M':
 		/* FALLTHROUGH */
 	case 'm':
 		/* FALLTHROUGH */
 	case 'n':
 		/* FALLTHROUGH */
 	case 'V':
 		/* FALLTHROUGH */
 	case 'Y':
 		gly = ESCAPE_IGNORE;
 		/* FALLTHROUGH */
 	case 'f':
 		if (ESCAPE_ERROR == gly)
 			gly = ESCAPE_FONT;
 		switch (**start) {
 		case '(':
 			*start = ++*end;
 			*sz = 2;
 			break;
 		case '[':
 			*start = ++*end;
 			term = ']';
 			break;
 		default:
 			*sz = 1;
 			break;
 		}
 		break;
 
 	/*
 	 * These escapes are of the form \X'Y', where 'X' is the trigger
 	 * and 'Y' is any string.  These have opaque sub-strings.
 	 * The \B and \w escapes are handled in roff.c, roff_res().
 	 */
 	case 'A':
 		/* FALLTHROUGH */
 	case 'b':
 		/* FALLTHROUGH */
 	case 'D':
 		/* FALLTHROUGH */
 	case 'o':
 		/* FALLTHROUGH */
 	case 'R':
 		/* FALLTHROUGH */
 	case 'X':
 		/* FALLTHROUGH */
 	case 'Z':
 		if ('\0' == **start)
 			return(ESCAPE_ERROR);
 		gly = ESCAPE_IGNORE;
 		term = **start;
 		*start = ++*end;
 		break;
 
 	/*
 	 * These escapes are of the form \X'N', where 'X' is the trigger
 	 * and 'N' resolves to a numerical expression.
 	 */
 	case 'h':
 		/* FALLTHROUGH */
 	case 'H':
 		/* FALLTHROUGH */
 	case 'L':
 		/* FALLTHROUGH */
 	case 'l':
 		/* FALLTHROUGH */
 	case 'S':
 		/* FALLTHROUGH */
 	case 'v':
 		/* FALLTHROUGH */
 	case 'x':
 		if (strchr(" %&()*+-./0123456789:<=>", **start)) {
-			++*end;
+			if ('\0' != **start)
+				++*end;
 			return(ESCAPE_ERROR);
 		}
 		gly = ESCAPE_IGNORE;
 		term = **start;
 		*start = ++*end;
 		break;
 
 	/*
 	 * Special handling for the numbered character escape.
 	 * XXX Do any other escapes need similar handling?
 	 */
 	case 'N':
 		if ('\0' == **start)
 			return(ESCAPE_ERROR);
 		(*end)++;
 		if (isdigit((unsigned char)**start)) {
 			*sz = 1;
 			return(ESCAPE_IGNORE);
 		}
 		(*start)++;
 		while (isdigit((unsigned char)**end))
 			(*end)++;
 		*sz = *end - *start;
 		if ('\0' != **end)
 			(*end)++;
 		return(ESCAPE_NUMBERED);
 
 	/*
 	 * Sizes get a special category of their own.
 	 */
 	case 's':
 		gly = ESCAPE_IGNORE;
 
 		/* See +/- counts as a sign. */
 		if ('+' == **end || '-' == **end || ASCII_HYPH == **end)
 			(*end)++;
 
 		switch (**end) {
 		case '(':
 			*start = ++*end;
 			*sz = 2;
 			break;
 		case '[':
 			*start = ++*end;
 			term = ']';
 			break;
 		case '\'':
 			*start = ++*end;
 			term = '\'';
 			break;
 		default:
 			*sz = 1;
 			break;
 		}
 
 		break;
 
 	/*
 	 * Anything else is assumed to be a glyph.
 	 * In this case, pass back the character after the backslash.
 	 */
 	default:
 		gly = ESCAPE_SPECIAL;
 		*start = --*end;
 		*sz = 1;
 		break;
 	}
 
 	assert(ESCAPE_ERROR != gly);
 
 	/*
 	 * Read up to the terminating character,
 	 * paying attention to nested escapes.
 	 */
 
 	if ('\0' != term) {
 		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);
 		*end += *sz;
 	}
 
 	/* Run post-processors. */
 
 	switch (gly) {
 	case ESCAPE_FONT:
 		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 (**start) {
 		case '3':
 			/* FALLTHROUGH */
 		case 'B':
 			gly = ESCAPE_FONTBOLD;
 			break;
 		case '2':
 			/* FALLTHROUGH */
 		case 'I':
 			gly = ESCAPE_FONTITALIC;
 			break;
 		case 'P':
 			gly = ESCAPE_FONTPREV;
 			break;
 		case '1':
 			/* FALLTHROUGH */
 		case 'R':
 			gly = ESCAPE_FONTROMAN;
 			break;
 		}
 		break;
 	case ESCAPE_SPECIAL:
 		if (1 == *sz && 'c' == **start)
 			gly = ESCAPE_NOSPACE;
+		/*
+		 * Unicode escapes are defined in groff as \[u0000]
+		 * to \[u10FFFF], where the contained value must be
+		 * a valid Unicode codepoint.  Here, however, only
+		 * check the length and range.
+		 */
+		if (**start != 'u' || *sz < 5 || *sz > 7)
+			break;
+		if (*sz == 7 && ((*start)[1] != '1' || (*start)[2] != '0'))
+			break;
+		if (*sz == 6 && (*start)[1] == '0')
+			break;
+		if ((int)strspn(*start + 1, "0123456789ABCDEFabcdef")
+		    + 1 == *sz)
+			gly = ESCAPE_UNICODE;
 		break;
 	default:
 		break;
 	}
 
 	return(gly);
 }
 
 /*
  * 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.
  * NUL-terminate the argument in place.
  * Collapse pairs of quotes inside quoted arguments.
  * Advance the argument pointer to the next argument,
  * or to the NUL byte terminating the argument line.
  */
 char *
 mandoc_getarg(struct mparse *parse, char **cpp, int ln, int *pos)
 {
 	char	 *start, *cp;
 	int	  quoted, pairs, white;
 
 	/* Quoting can only start with a new word. */
 	start = *cpp;
 	quoted = 0;
 	if ('"' == *start) {
 		quoted = 1;
 		start++;
 	}
 
 	pairs = 0;
 	white = 0;
 	for (cp = start; '\0' != *cp; cp++) {
 
 		/*
 		 * Move the following text left
 		 * after quoted quotes and after "\\" and "\t".
 		 */
 		if (pairs)
 			cp[-pairs] = cp[0];
 
 		if ('\\' == cp[0]) {
 			/*
 			 * 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++;
 				break;
 			case ' ':
 				/* Skip escaped blanks. */
 				if (0 == quoted)
 					cp++;
 				break;
 			default:
 				break;
 			}
 		} else if (0 == quoted) {
 			if (' ' == cp[0]) {
 				/* Unescaped blanks end unquoted args. */
 				white = 1;
 				break;
 			}
 		} else if ('"' == cp[0]) {
 			if ('"' == cp[1]) {
 				/* Quoted quotes collapse. */
 				pairs++;
 				cp++;
 			} else {
 				/* Unquoted quotes end quoted args. */
 				quoted = 2;
 				break;
 			}
 		}
 	}
 
 	/* Quoted argument without a closing quote. */
 	if (1 == quoted)
 		mandoc_msg(MANDOCERR_ARG_QUOTE, parse, ln, *pos, NULL);
 
 	/* NUL-terminate this argument and move to the next one. */
 	if (pairs)
 		cp[-pairs] = '\0';
 	if ('\0' != *cp) {
 		*cp++ = '\0';
 		while (' ' == *cp)
 			cp++;
 	}
 	*pos += (int)(cp - start) + (quoted ? 1 : 0);
 	*cpp = cp;
 
 	if ('\0' == *cp && (white || ' ' == cp[-1]))
 		mandoc_msg(MANDOCERR_SPACE_EOL, parse, ln, *pos, NULL);
 
 	return(start);
 }
 
 static int
 a2time(time_t *t, const char *fmt, const char *p)
 {
 	struct tm	 tm;
 	char		*pp;
 
 	memset(&tm, 0, sizeof(struct tm));
 
 	pp = NULL;
-#ifdef	HAVE_STRPTIME
+#if HAVE_STRPTIME
 	pp = strptime(p, fmt, &tm);
 #endif
 	if (NULL != pp && '\0' == *pp) {
 		*t = mktime(&tm);
 		return(1);
 	}
 
 	return(0);
 }
 
 static char *
 time2a(time_t t)
 {
 	struct tm	*tm;
 	char		*buf, *p;
 	size_t		 ssz;
 	int		 isz;
 
 	tm = localtime(&t);
 
 	/*
 	 * Reserve space:
 	 * up to 9 characters for the month (September) + blank
 	 * up to 2 characters for the day + comma + blank
 	 * 4 characters for the year and a terminating '\0'
 	 */
 	p = buf = mandoc_malloc(10 + 4 + 4 + 1);
 
 	if (0 == (ssz = strftime(p, 10 + 1, "%B ", tm)))
 		goto fail;
 	p += (int)ssz;
 
 	if (-1 == (isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)))
 		goto fail;
 	p += isz;
 
 	if (0 == strftime(p, 4 + 1, "%Y", tm))
 		goto fail;
 	return(buf);
 
 fail:
 	free(buf);
 	return(NULL);
 }
 
 char *
 mandoc_normdate(struct mparse *parse, char *in, int ln, int pos)
 {
 	char		*out;
 	time_t		 t;
 
 	if (NULL == in || '\0' == *in ||
 	    0 == strcmp(in, "$" "Mdocdate$")) {
 		mandoc_msg(MANDOCERR_DATE_MISSING, parse, ln, pos, NULL);
 		time(&t);
 	}
 	else if (a2time(&t, "%Y-%m-%d", in))
 		t = 0;
 	else if (!a2time(&t, "$" "Mdocdate: %b %d %Y $", in) &&
 	    !a2time(&t, "%b %d, %Y", in)) {
 		mandoc_msg(MANDOCERR_DATE_BAD, parse, ln, pos, in);
 		t = 0;
 	}
 	out = t ? time2a(t) : NULL;
 	return(out ? out : mandoc_strdup(in));
 }
 
 int
 mandoc_eos(const char *p, size_t sz)
 {
 	const char	*q;
 	int		 enclosed, found;
 
 	if (0 == sz)
 		return(0);
 
 	/*
 	 * End-of-sentence recognition must include situations where
 	 * some symbols, such as `)', allow prior EOS punctuation to
 	 * propagate outward.
 	 */
 
 	enclosed = found = 0;
 	for (q = p + (int)sz - 1; q >= p; q--) {
 		switch (*q) {
 		case '\"':
 			/* FALLTHROUGH */
 		case '\'':
 			/* FALLTHROUGH */
 		case ']':
 			/* FALLTHROUGH */
 		case ')':
 			if (0 == found)
 				enclosed = 1;
 			break;
 		case '.':
 			/* FALLTHROUGH */
 		case '!':
 			/* FALLTHROUGH */
 		case '?':
 			found = 1;
 			break;
 		default:
 			return(found && (!enclosed || isalnum((unsigned char)*q)));
 		}
 	}
 
 	return(found && !enclosed);
 }
 
 /*
  * Convert a string to a long that may not be <0.
  * If the string is invalid, or is less than 0, return -1.
  */
 int
 mandoc_strntoi(const char *p, size_t sz, int base)
 {
 	char		 buf[32];
 	char		*ep;
 	long		 v;
 
 	if (sz > 31)
 		return(-1);
 
 	memcpy(buf, p, sz);
 	buf[(int)sz] = '\0';
 
 	errno = 0;
 	v = strtol(buf, &ep, base);
 
 	if (buf[0] == '\0' || *ep != '\0')
 		return(-1);
 
 	if (v > INT_MAX)
 		v = INT_MAX;
 	if (v < INT_MIN)
 		v = INT_MIN;
 
 	return((int)v);
 }
Index: vendor/mdocml/dist/mandoc.db.5
===================================================================
--- vendor/mdocml/dist/mandoc.db.5	(revision 275396)
+++ vendor/mdocml/dist/mandoc.db.5	(revision 275397)
@@ -1,144 +1,157 @@
-.\"	$Id: mandoc.db.5,v 1.1 2014/04/15 20:18:26 schwarze Exp $
+.\"	$Id: mandoc.db.5,v 1.2 2014/09/03 18:09:14 schwarze Exp $
 .\"
 .\" Copyright (c) 2014 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" copyright notice and this permission notice appear in all copies.
 .\"
 .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: April 15 2014 $
+.Dd $Mdocdate: September 3 2014 $
 .Dt MANDOC.DB 5
 .Os
 .Sh NAME
 .Nm mandoc.db
 .Nd manual page database
 .Sh DESCRIPTION
 The
 .Nm
 SQLite3 file format is used to store information about installed manual
 pages to facilitate semantic searching for manuals.
 Each manual page tree contains its own
 .Nm
 file; see
 .Sx FILES
 for examples.
 .Pp
 Such database files are generated by
 .Xr makewhatis 8
 and used by
 .Xr apropos 1
 and
 .Xr whatis 1 .
 .Pp
 One line in the following tables describes:
 .Bl -tag -width Ds
 .It Sy mpages
 One physical manual page file, no matter how many times and under which
 names it may appear in the file system.
 .It Sy mlinks
 One entry in the file system, no matter which content it points to.
 .It Sy names
 One manual page name, no matter whether it appears in a page header,
 in a NAME or SYNOPSIS section, or as a file name.
 .It Sy keys
 One chunk of text from some macro invocation.
 .El
 .Pp
 Each record in the latter three tables uses its
 .Va pageid
 column to point to a record in the
 .Sy mpages
 table.
 .Pp
 The other columns are as follows; unless stated otherwise, they are
 of type
 .Vt TEXT .
 .Bl -tag -width mpages.desc
 .It Sy mpages.desc
 The description line
 .Pq Sq \&Nd
 of the page.
 .It Sy mpages.form
-The
+An
 .Vt INTEGER
-1 if the page is unformatted, i.e. in
+bit field.
+If bit
+.Dv FORM_GZ
+is set, the page is compressed and requires
+.Xr gunzip 1
+for display.
+If bit
+.Dv FORM_SRC
+is set, the page is unformatted, that is in
 .Xr mdoc 7
 or
 .Xr man 7
-format, and 2 if it is formatted, i.e. a
+format, and requires
+.Xr mandoc 1
+for display.
+If bit
+.Dv FORM_SRC
+is not set, the page is formatted, i.e. a
 .Sq cat
 page.
 .It Sy mlinks.sec
 The manual section as found in the subdirectory name.
 .It Sy mlinks.arch
 The manual architecture as found in the subdirectory name, or
 .Qq any .
 .It Sy mlinks.name
 The manual name as found in the file name.
 .It Sy names.bits
 An
 .Vt INTEGER
 bit mask telling whether the name came from a header line, from the
 NAME or SYNOPSIS section, or from a file name.
 Bits are defined in
 .In mansearch.h .
 .It Sy names.name
 The name itself.
 .It Sy keys.bits
 An
 .Vt INTEGER
 bit mask telling which semantic contexts the key was found in;
 defined in
 .In mansearch.h ,
 documented in
 .Xr apropos 1 .
 .It Sy keys.key
 The string found in those contexts.
 .El
 .Sh FILES
 .Bl -tag -width /usr/share/mandoc.db -compact
 .It Pa /usr/share/mandoc.db
 The manual page database for the base system.
 .It Pa /usr/X11R6/mandoc.db
 The same for the
 .Xr X 7
 Window System.
 .It Pa /usr/local/mandoc.db
 The same for
 .Xr packages 7 .
 .El
 .Sh SEE ALSO
 .Xr apropos 1 ,
 .Xr man 1 ,
 .Xr sqlite3 1 ,
 .Xr whatis 1 ,
 .Xr mansearch 3 ,
 .Xr makewhatis 8
 .Sh HISTORY
 A manual page database
 .Pa /usr/lib/whatis
 first appeared in
 .Bx 2 .
 The present format first appeared in
 .Ox 5.6 .
 .Sh AUTHORS
 .An -nosplit
 The original version of
 .Xr makewhatis 8
 was written by
 .An Bill Joy
 in 1979.
 An SQLite3 version was first implemented by
 .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
 in 2012.
 The present database format was designed by
 .An Ingo Schwarze Aq Mt schwarze@openbsd.org
 in 2014.
Index: vendor/mdocml/dist/mandoc.h
===================================================================
--- vendor/mdocml/dist/mandoc.h	(revision 275396)
+++ vendor/mdocml/dist/mandoc.h	(revision 275397)
@@ -1,438 +1,452 @@
-/*	$Id: mandoc.h,v 1.152 2014/08/06 15:09:05 schwarze Exp $ */
+/*	$Id: mandoc.h,v 1.171 2014/11/28 18:09:01 schwarze Exp $ */
 /*
- * Copyright (c) 2010, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons 
  * Copyright (c) 2010-2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef MANDOC_H
 #define MANDOC_H
 
 #define ASCII_NBRSP	 31  /* non-breaking space */
 #define	ASCII_HYPH	 30  /* breakable hyphen */
 #define	ASCII_BREAK	 29  /* breakable zero-width space */
 
 /*
  * Status level.  This refers to both internal status (i.e., whilst
  * running, when warnings/errors are reported) and an indicator of a
  * threshold of when to halt (when said internal state exceeds the
  * threshold).
  */
 enum	mandoclevel {
 	MANDOCLEVEL_OK = 0,
 	MANDOCLEVEL_RESERVED,
 	MANDOCLEVEL_WARNING, /* warnings: syntax, whitespace, etc. */
 	MANDOCLEVEL_ERROR, /* input has been thrown away */
 	MANDOCLEVEL_FATAL, /* input is borked */
 	MANDOCLEVEL_BADARG, /* bad argument in invocation */
 	MANDOCLEVEL_SYSERR, /* system error */
 	MANDOCLEVEL_MAX
 };
 
 /*
  * All possible things that can go wrong within a parse, be it libroff,
  * libmdoc, or libman.
  */
 enum	mandocerr {
 	MANDOCERR_OK,
 
 	MANDOCERR_WARNING, /* ===== start of warnings ===== */
 
 	/* related to the prologue */
 	MANDOCERR_DT_NOTITLE, /* missing manual title, using UNTITLED: line */
 	MANDOCERR_TH_NOTITLE, /* missing manual title, using "": [macro] */
 	MANDOCERR_TITLE_CASE, /* lower case character in document title */
 	MANDOCERR_MSEC_MISSING, /* missing manual section, using "": macro */
 	MANDOCERR_MSEC_BAD, /* unknown manual section: Dt ... section */
-	MANDOCERR_ARCH_BAD, /* unknown manual volume or arch: Dt ... volume */
 	MANDOCERR_DATE_MISSING, /* missing date, using today's date */
 	MANDOCERR_DATE_BAD, /* cannot parse date, using it verbatim: date */
 	MANDOCERR_OS_MISSING, /* missing Os macro, using "" */
 	MANDOCERR_PROLOG_REP, /* duplicate prologue macro: macro */
 	MANDOCERR_PROLOG_LATE, /* late prologue macro: macro */
 	MANDOCERR_DT_LATE, /* skipping late title macro: Dt args */
 	MANDOCERR_PROLOG_ORDER, /* prologue macros out of order: macros */
 
 	/* related to document structure */
 	MANDOCERR_SO, /* .so is fragile, better use ln(1): so path */
 	MANDOCERR_DOC_EMPTY, /* no document body */
 	MANDOCERR_SEC_BEFORE, /* content before first section header: macro */
 	MANDOCERR_NAMESEC_FIRST, /* first section is not NAME: Sh title */
 	MANDOCERR_NAMESEC_BAD, /* bad NAME section contents: macro */
 	MANDOCERR_SEC_ORDER, /* sections out of conventional order: Sh title */
 	MANDOCERR_SEC_REP, /* duplicate section title: Sh title */
 	MANDOCERR_SEC_MSEC, /* unexpected section: Sh title for ... only */
+	MANDOCERR_XR_ORDER, /* unusual Xr order: ... after ... */
+	MANDOCERR_XR_PUNCT, /* unusual Xr punctuation: ... after ... */
+	MANDOCERR_AN_MISSING, /* AUTHORS section without An macro */
 
 	/* related to macros and nesting */
 	MANDOCERR_MACRO_OBS, /* obsolete macro: macro */
 	MANDOCERR_PAR_SKIP, /* skipping paragraph macro: macro ... */
 	MANDOCERR_PAR_MOVE, /* moving paragraph macro out of list: macro */
 	MANDOCERR_NS_SKIP, /* skipping no-space macro */
 	MANDOCERR_BLK_NEST, /* blocks badly nested: macro ... */
 	MANDOCERR_BD_NEST, /* nested displays are not portable: macro ... */
 	MANDOCERR_BL_MOVE, /* moving content out of list: macro */
 	MANDOCERR_VT_CHILD, /* .Vt block has child macro: macro */
 	MANDOCERR_FI_SKIP, /* fill mode already enabled, skipping: fi */
 	MANDOCERR_NF_SKIP, /* fill mode already disabled, skipping: nf */
 	MANDOCERR_BLK_LINE, /* line scope broken: macro breaks macro */
 
 	/* related to missing arguments */
 	MANDOCERR_REQ_EMPTY, /* skipping empty request: request */
 	MANDOCERR_COND_EMPTY, /* conditional request controls empty scope */
 	MANDOCERR_MACRO_EMPTY, /* skipping empty macro: macro */
 	MANDOCERR_ARG_EMPTY, /* empty argument, using 0n: macro arg */
 	MANDOCERR_ARGCWARN, /* argument count wrong */
 	MANDOCERR_BD_NOTYPE, /* missing display type, using -ragged: Bd */
 	MANDOCERR_BL_LATETYPE, /* list type is not the first argument: Bl arg */
 	MANDOCERR_BL_NOWIDTH, /* missing -width in -tag list, using 8n */
 	MANDOCERR_EX_NONAME, /* missing utility name, using "": Ex */
 	MANDOCERR_IT_NOHEAD, /* empty head in list item: Bl -type It */
 	MANDOCERR_IT_NOBODY, /* empty list item: Bl -type It */
 	MANDOCERR_BF_NOFONT, /* missing font type, using \fR: Bf */
 	MANDOCERR_BF_BADFONT, /* unknown font type, using \fR: Bf font */
 	MANDOCERR_ARG_STD, /* missing -std argument, adding it: macro */
+	MANDOCERR_EQN_NOBOX, /* missing eqn box, using "": op */
 
 	/* related to bad arguments */
 	MANDOCERR_ARG_QUOTE, /* unterminated quoted argument */
 	MANDOCERR_ARG_REP, /* duplicate argument: macro arg */
 	MANDOCERR_AN_REP, /* skipping duplicate argument: An -arg */
 	MANDOCERR_BD_REP, /* skipping duplicate display type: Bd -type */
 	MANDOCERR_BL_REP, /* skipping duplicate list type: Bl -type */
 	MANDOCERR_BL_SKIPW, /* skipping -width argument: Bl -type */
 	MANDOCERR_AT_BAD, /* unknown AT&T UNIX version: At version */
+	MANDOCERR_FA_COMMA, /* comma in function argument: arg */
+	MANDOCERR_FN_PAREN, /* parenthesis in function name: arg */
 	MANDOCERR_RS_BAD, /* invalid content in Rs block: macro */
 	MANDOCERR_SM_BAD, /* invalid Boolean argument: macro arg */
 	MANDOCERR_FT_BAD, /* unknown font, skipping request: ft font */
 
 	/* related to plain text */
 	MANDOCERR_FI_BLANK, /* blank line in fill mode, using .sp */
 	MANDOCERR_FI_TAB, /* tab in filled text */
 	MANDOCERR_SPACE_EOL, /* whitespace at end of input line */
 	MANDOCERR_COMMENT_BAD, /* bad comment style */
 	MANDOCERR_ESC_BAD, /* invalid escape sequence: esc */
 	MANDOCERR_STR_UNDEF, /* undefined string, using "": name */
 
 	MANDOCERR_ERROR, /* ===== start of errors ===== */
 
 	/* related to equations */
 	MANDOCERR_EQNNSCOPE, /* unexpected equation scope closure*/
 	MANDOCERR_EQNSCOPE, /* equation scope open on exit */
 	MANDOCERR_EQNBADSCOPE, /* overlapping equation scopes */
 	MANDOCERR_EQNEOF, /* unexpected end of equation */
-	MANDOCERR_EQNSYNT, /* equation syntax error */
 
 	/* related to tables */
 	MANDOCERR_TBL, /* bad table syntax */
 	MANDOCERR_TBLOPT, /* bad table option */
 	MANDOCERR_TBLLAYOUT, /* bad table layout */
 	MANDOCERR_TBLNOLAYOUT, /* no table layout cells specified */
 	MANDOCERR_TBLNODATA, /* no table data cells specified */
 	MANDOCERR_TBLIGNDATA, /* ignore data in cell */
 	MANDOCERR_TBLBLOCK, /* data block still open */
 	MANDOCERR_TBLEXTRADAT, /* ignoring extra data cells */
 
 	/* related to document structure and macros */
 	MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */
 	MANDOCERR_BADCHAR, /* skipping bad character: number */
 	MANDOCERR_MACRO, /* skipping unknown macro: macro */
 	MANDOCERR_IT_STRAY, /* skipping item outside list: It ... */
 	MANDOCERR_TA_STRAY, /* skipping column outside column list: Ta */
 	MANDOCERR_BLK_NOTOPEN, /* skipping end of block that is not open */
 	MANDOCERR_BLK_BROKEN, /* inserting missing end of block: macro ... */
 	MANDOCERR_BLK_NOEND, /* appending missing end of block: macro */
 
 	/* related to request and macro arguments */
 	MANDOCERR_NAMESC, /* escaped character not allowed in a name: name */
 	MANDOCERR_ARGCOUNT, /* argument count wrong */
+	MANDOCERR_BD_FILE, /* NOT IMPLEMENTED: Bd -file */
 	MANDOCERR_BL_NOTYPE, /* missing list type, using -item: Bl */
 	MANDOCERR_NM_NONAME, /* missing manual name, using "": Nm */
 	MANDOCERR_OS_UNAME, /* uname(3) system call failed, using UNKNOWN */
 	MANDOCERR_ST_BAD, /* unknown standard specifier: St standard */
 	MANDOCERR_IT_NONUM, /* skipping request without numeric argument */
 	MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */
 	MANDOCERR_ARG_EXCESS, /* skipping excess arguments: macro ... args */
+	MANDOCERR_DIVZERO, /* divide by zero */
 
 	MANDOCERR_FATAL, /* ===== start of fatal errors ===== */
 
 	MANDOCERR_TOOLARGE, /* input too large */
-	MANDOCERR_BD_FILE, /* NOT IMPLEMENTED: Bd -file */
 	MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */
 	MANDOCERR_SO_FAIL, /* .so request failed */
 
 	/* ===== system errors ===== */
 
+	MANDOCERR_SYSDUP, /* cannot dup file descriptor */
+	MANDOCERR_SYSEXEC, /* cannot exec */
+	MANDOCERR_SYSEXIT, /* gunzip failed with code */
+	MANDOCERR_SYSFORK, /* cannot fork */
 	MANDOCERR_SYSOPEN, /* cannot open file */
-	MANDOCERR_SYSSTAT, /* cannot stat file */
+	MANDOCERR_SYSPIPE, /* cannot open pipe */
 	MANDOCERR_SYSREAD, /* cannot read file */
+	MANDOCERR_SYSSIG, /* gunzip died from signal */
+	MANDOCERR_SYSSTAT, /* cannot stat file */
+	MANDOCERR_SYSWAIT, /* wait failed */
 
 	MANDOCERR_MAX
 };
 
 struct	tbl_opts {
 	char		  tab; /* cell-separator */
 	char		  decimal; /* decimal point */
 	int		  linesize;
 	int		  opts;
 #define	TBL_OPT_CENTRE	 (1 << 0)
 #define	TBL_OPT_EXPAND	 (1 << 1)
 #define	TBL_OPT_BOX	 (1 << 2)
 #define	TBL_OPT_DBOX	 (1 << 3)
 #define	TBL_OPT_ALLBOX	 (1 << 4)
 #define	TBL_OPT_NOKEEP	 (1 << 5)
 #define	TBL_OPT_NOSPACE	 (1 << 6)
 	int		  cols; /* number of columns */
 };
 
 /*
  * The head of a table specifies all of its columns.  When formatting a
  * tbl_span, iterate over these and plug in data from the tbl_span when
  * appropriate, using tbl_cell as a guide to placement.
  */
 struct	tbl_head {
 	int		  ident; /* 0 <= unique id < cols */
 	int		  vert; /* width of preceding vertical line */
 	struct tbl_head	 *next;
 	struct tbl_head	 *prev;
 };
 
 enum	tbl_cellt {
 	TBL_CELL_CENTRE, /* c, C */
 	TBL_CELL_RIGHT, /* r, R */
 	TBL_CELL_LEFT, /* l, L */
 	TBL_CELL_NUMBER, /* n, N */
 	TBL_CELL_SPAN, /* s, S */
 	TBL_CELL_LONG, /* a, A */
 	TBL_CELL_DOWN, /* ^ */
 	TBL_CELL_HORIZ, /* _, - */
 	TBL_CELL_DHORIZ, /* = */
 	TBL_CELL_MAX
 };
 
 /*
  * A cell in a layout row.
  */
 struct	tbl_cell {
 	struct tbl_cell	 *next;
 	int		  vert; /* width of preceding vertical line */
 	enum tbl_cellt	  pos;
 	size_t		  spacing;
 	int		  flags;
 #define	TBL_CELL_TALIGN	 (1 << 0) /* t, T */
 #define	TBL_CELL_BALIGN	 (1 << 1) /* d, D */
 #define	TBL_CELL_BOLD	 (1 << 2) /* fB, B, b */
 #define	TBL_CELL_ITALIC	 (1 << 3) /* fI, I, i */
 #define	TBL_CELL_EQUAL	 (1 << 4) /* e, E */
 #define	TBL_CELL_UP	 (1 << 5) /* u, U */
 #define	TBL_CELL_WIGN	 (1 << 6) /* z, Z */
+#define	TBL_CELL_WMAX	 (1 << 7) /* x, X */
 	struct tbl_head	 *head;
 };
 
 /*
  * A layout row.
  */
 struct	tbl_row {
 	struct tbl_row	 *next;
 	struct tbl_cell	 *first;
 	struct tbl_cell	 *last;
 	int		  vert; /* trailing vertical line */
 };
 
 enum	tbl_datt {
 	TBL_DATA_NONE, /* has no data */
 	TBL_DATA_DATA, /* consists of data/string */
 	TBL_DATA_HORIZ, /* horizontal line */
 	TBL_DATA_DHORIZ, /* double-horizontal line */
 	TBL_DATA_NHORIZ, /* squeezed horizontal line */
 	TBL_DATA_NDHORIZ /* squeezed double-horizontal line */
 };
 
 /*
  * A cell within a row of data.  The "string" field contains the actual
  * string value that's in the cell.  The rest is layout.
  */
 struct	tbl_dat {
 	struct tbl_cell	 *layout; /* layout cell */
 	int		  spans; /* how many spans follow */
 	struct tbl_dat	 *next;
 	char		 *string; /* data (NULL if not TBL_DATA_DATA) */
 	enum tbl_datt	  pos;
 };
 
 enum	tbl_spant {
 	TBL_SPAN_DATA, /* span consists of data */
 	TBL_SPAN_HORIZ, /* span is horizontal line */
 	TBL_SPAN_DHORIZ /* span is double horizontal line */
 };
 
 /*
  * A row of data in a table.
  */
 struct	tbl_span {
 	struct tbl_opts	 *opts;
 	struct tbl_head	 *head;
 	struct tbl_row	 *layout; /* layout row */
 	struct tbl_dat	 *first;
 	struct tbl_dat	 *last;
 	int		  line; /* parse line */
 	int		  flags;
 #define	TBL_SPAN_FIRST	 (1 << 0)
 #define	TBL_SPAN_LAST	 (1 << 1)
 	enum tbl_spant	  pos;
 	struct tbl_span	 *next;
 };
 
 enum	eqn_boxt {
 	EQN_ROOT, /* root of parse tree */
 	EQN_TEXT, /* text (number, variable, whatever) */
 	EQN_SUBEXPR, /* nested `eqn' subexpression */
-	EQN_LIST, /* subexpressions list */
-	EQN_MATRIX /* matrix subexpression */
+	EQN_LIST, /* list (braces, etc.) */
+	EQN_LISTONE, /* singleton list */
+	EQN_PILE, /* vertical pile */
+	EQN_MATRIX /* pile of piles */
 };
 
-enum	eqn_markt {
-	EQNMARK_NONE = 0,
-	EQNMARK_DOT,
-	EQNMARK_DOTDOT,
-	EQNMARK_HAT,
-	EQNMARK_TILDE,
-	EQNMARK_VEC,
-	EQNMARK_DYAD,
-	EQNMARK_BAR,
-	EQNMARK_UNDER,
-	EQNMARK__MAX
-};
-
 enum	eqn_fontt {
 	EQNFONT_NONE = 0,
 	EQNFONT_ROMAN,
 	EQNFONT_BOLD,
 	EQNFONT_FAT,
 	EQNFONT_ITALIC,
 	EQNFONT__MAX
 };
 
 enum	eqn_post {
 	EQNPOS_NONE = 0,
-	EQNPOS_OVER,
 	EQNPOS_SUP,
+	EQNPOS_SUBSUP,
 	EQNPOS_SUB,
 	EQNPOS_TO,
 	EQNPOS_FROM,
+	EQNPOS_FROMTO,
+	EQNPOS_OVER,
+	EQNPOS_SQRT,
 	EQNPOS__MAX
 };
 
 enum	eqn_pilet {
 	EQNPILE_NONE = 0,
 	EQNPILE_PILE,
 	EQNPILE_CPILE,
 	EQNPILE_RPILE,
 	EQNPILE_LPILE,
 	EQNPILE_COL,
 	EQNPILE_CCOL,
 	EQNPILE_RCOL,
 	EQNPILE_LCOL,
 	EQNPILE__MAX
 };
 
  /*
  * A "box" is a parsed mathematical expression as defined by the eqn.7
  * grammar.
  */
 struct	eqn_box {
 	int		  size; /* font size of expression */
 #define	EQN_DEFSIZE	  INT_MIN
 	enum eqn_boxt	  type; /* type of node */
 	struct eqn_box	 *first; /* first child node */
 	struct eqn_box	 *last; /* last child node */
 	struct eqn_box	 *next; /* node sibling */
+	struct eqn_box	 *prev; /* node sibling */
 	struct eqn_box	 *parent; /* node sibling */
 	char		 *text; /* text (or NULL) */
-	char		 *left;
-	char		 *right;
+	char		 *left; /* fence left-hand */
+	char		 *right; /* fence right-hand */
+	char		 *top; /* expression over-symbol */
+	char		 *bottom; /* expression under-symbol */
+	size_t		  args; /* arguments in parent */
+	size_t		  expectargs; /* max arguments in parent */
 	enum eqn_post	  pos; /* position of next box */
-	enum eqn_markt	  mark; /* a mark about the box */
 	enum eqn_fontt	  font; /* font of box */
 	enum eqn_pilet	  pile; /* equation piling */
 };
 
 /*
  * An equation consists of a tree of expressions starting at a given
  * line and position.
  */
 struct	eqn {
 	char		 *name; /* identifier (or NULL) */
 	struct eqn_box	 *root; /* root mathematical expression */
 	int		  ln; /* invocation line */
 	int		  pos; /* invocation position */
 };
 
 /*
  * Parse options.
  */
 #define	MPARSE_MDOC	1  /* assume -mdoc */
 #define	MPARSE_MAN	2  /* assume -man */
 #define	MPARSE_SO	4  /* honour .so requests */
 #define	MPARSE_QUICK	8  /* abort the parse early */
+#define	MPARSE_UTF8	16 /* accept UTF-8 input */
+#define	MPARSE_LATIN1	32 /* accept ISO-LATIN-1 input */
 
 enum	mandoc_esc {
 	ESCAPE_ERROR = 0, /* bail! unparsable escape */
 	ESCAPE_IGNORE, /* escape to be ignored */
 	ESCAPE_SPECIAL, /* a regular special character */
 	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_SKIPCHAR /* skip the next character */
 };
 
 typedef	void	(*mandocmsg)(enum mandocerr, enum mandoclevel,
 			const char *, int, int, const char *);
 
 struct	mparse;
 struct	mchars;
 struct	mdoc;
 struct	man;
 
 __BEGIN_DECLS
 
 enum mandoc_esc	  mandoc_escape(const char **, const char **, int *);
 struct mchars	 *mchars_alloc(void);
 void		  mchars_free(struct mchars *);
-char		  mchars_num2char(const char *, size_t);
+int		  mchars_num2char(const char *, size_t);
+const char	 *mchars_uc2str(int);
 int		  mchars_num2uc(const char *, size_t);
 int		  mchars_spec2cp(const struct mchars *,
 			const char *, size_t);
 const char	 *mchars_spec2str(const struct mchars *,
 			const char *, size_t, size_t *);
 struct mparse	 *mparse_alloc(int, enum mandoclevel, mandocmsg,
-			const char *);
+			const struct mchars *, const char *);
 void		  mparse_free(struct mparse *);
 void		  mparse_keep(struct mparse *);
+enum mandoclevel  mparse_open(struct mparse *, int *, const char *);
 enum mandoclevel  mparse_readfd(struct mparse *, int, const char *);
 enum mandoclevel  mparse_readmem(struct mparse *, const void *, size_t,
 			const char *);
 void		  mparse_reset(struct mparse *);
 void		  mparse_result(struct mparse *,
 			struct mdoc **, struct man **, char **);
 const char	 *mparse_getkeep(const struct mparse *);
 const char	 *mparse_strerror(enum mandocerr);
 const char	 *mparse_strlevel(enum mandoclevel);
+enum mandoclevel  mparse_wait(struct mparse *);
 
 __END_DECLS
 
 #endif /*!MANDOC_H*/
Index: vendor/mdocml/dist/mandoc_aux.c
===================================================================
--- vendor/mdocml/dist/mandoc_aux.c	(revision 275396)
+++ vendor/mdocml/dist/mandoc_aux.c	(revision 275397)
@@ -1,121 +1,119 @@
-/*	$Id: mandoc_aux.c,v 1.3 2014/07/09 08:20:34 schwarze Exp $ */
+/*	$Id: mandoc_aux.c,v 1.4 2014/08/10 23:54:41 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2011 Kristaps Dzonsons 
  * Copyright (c) 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 
 int
 mandoc_asprintf(char **dest, const char *fmt, ...)
 {
 	va_list	 ap;
 	int	 ret;
 
 	va_start(ap, fmt);
 	ret = vasprintf(dest, fmt, ap);
 	va_end(ap);
 
 	if (-1 == ret) {
 		perror(NULL);
 		exit((int)MANDOCLEVEL_SYSERR);
 	}
 	return(ret);
 }
 
 void *
 mandoc_calloc(size_t num, size_t size)
 {
 	void	*ptr;
 
 	ptr = calloc(num, size);
 	if (NULL == ptr) {
 		perror(NULL);
 		exit((int)MANDOCLEVEL_SYSERR);
 	}
 	return(ptr);
 }
 
 void *
 mandoc_malloc(size_t size)
 {
 	void	*ptr;
 
 	ptr = malloc(size);
 	if (NULL == ptr) {
 		perror(NULL);
 		exit((int)MANDOCLEVEL_SYSERR);
 	}
 	return(ptr);
 }
 
 void *
 mandoc_realloc(void *ptr, size_t size)
 {
 
 	ptr = realloc(ptr, size);
 	if (NULL == ptr) {
 		perror(NULL);
 		exit((int)MANDOCLEVEL_SYSERR);
 	}
 	return(ptr);
 }
 
 void *
 mandoc_reallocarray(void *ptr, size_t num, size_t size)
 {
 
 	ptr = reallocarray(ptr, num, size);
 	if (NULL == ptr) {
 		perror(NULL);
 		exit((int)MANDOCLEVEL_SYSERR);
 	}
 	return(ptr);
 }
 
 char *
 mandoc_strdup(const char *ptr)
 {
 	char	*p;
 
 	p = strdup(ptr);
 	if (NULL == p) {
 		perror(NULL);
 		exit((int)MANDOCLEVEL_SYSERR);
 	}
 	return(p);
 }
 
 char *
 mandoc_strndup(const char *ptr, size_t sz)
 {
 	char	*p;
 
 	p = mandoc_malloc(sz + 1);
 	memcpy(p, ptr, sz);
 	p[(int)sz] = '\0';
 	return(p);
 }
Index: vendor/mdocml/dist/mandoc_escape.3
===================================================================
--- vendor/mdocml/dist/mandoc_escape.3	(revision 275396)
+++ vendor/mdocml/dist/mandoc_escape.3	(revision 275397)
@@ -1,362 +1,366 @@
-.\"	$Id: mandoc_escape.3,v 1.1 2014/08/05 05:48:56 schwarze Exp $
+.\"	$Id: mandoc_escape.3,v 1.2 2014/10/28 14:06:31 schwarze Exp $
 .\"
 .\" Copyright (c) 2014 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" copyright notice and this permission notice appear in all copies.
 .\"
 .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: August 5 2014 $
+.Dd $Mdocdate: October 28 2014 $
 .Dt MANDOC_ESCAPE 3
 .Os
 .Sh NAME
 .Nm mandoc_escape
 .Nd parse roff escape sequences
 .Sh LIBRARY
 .Lb libmandoc
 .Sh SYNOPSIS
 .In sys/types.h
 .In mandoc.h
 .Ft "enum mandoc_esc"
 .Fo mandoc_escape
 .Fa "const char **end"
 .Fa "const char **start"
 .Fa "int *sz"
 .Fc
 .Sh DESCRIPTION
 This function scans a
 .Xr roff 7
 escape sequence.
 .Pp
 An escape sequence consists of
 .Bl -dash -compact -width 2n
 .It
 an initial backslash character
 .Pq Sq \e ,
 .It
 a single ASCII character called the escape sequence identifier,
 .It
 and, with only a few exceptions, an argument.
 .El
 .Pp
 Arguments can be given in the following forms; some escape sequence
 identifiers only accept some of these forms as specified below.
 The first three forms are called the standard forms.
 .Bl -tag -width 2n
 .It \&In brackets: Ic \&[ Ns Ar argument Ns Ic \&]
 The argument starts after the initial
 .Sq \&[ ,
 ends before the final
 .Sq \&] ,
 and the escape sequence ends with the final
 .Sq \&] .
 .It Two-character argument short form: Ic \&( Ns Ar ar
 This form can only be used for arguments
 consisting of exactly two characters.
 It has the same effect as
 .Ic \&[ Ns Ar ar Ns Ic \&] .
 .It One-character argument short form: Ar a
 This form can only be used for arguments
 consisting of exactly one character.
 It has the same effect as
 .Ic \&[ Ns Ar a Ns Ic \&] .
 .It Delimited form: Ar C Ns Ar argument Ns Ar C
 The argument starts after the initial delimiter character
 .Ar C ,
 ends before the next occurrence of the delimiter character
 .Ar C ,
 and the escape sequence ends with that second
 .Ar C .
 Some escape sequences allow arbitrary characters
 .Ar C
 as quoting characters, some restrict the range of characters
 that can be used as quoting characters.
 .El
 .Pp
 Upon function entry,
 .Fa end
 is expected to point to the escape sequence identifier.
 The values passed in as
 .Fa start
 and
 .Fa sz
 are ignored and overwritten.
 .Pp
 By design, this function cannot handle those
 .Xr roff 7
 escape sequences that require in-place expansion, in particular
 user-defined strings
 .Ic \e* ,
 number registers
 .Ic \en ,
 width measurements
 .Ic \ew ,
 and numerical expression control
 .Ic \eB .
 These are handled by
 .Fn roff_res ,
 a private preprocessor function called from
 .Fn roff_parseln ,
 see the file
 .Pa roff.c .
 .Pp
 The function
 .Fn mandoc_escape
 is used
 .Bl -dash -compact -width 2n
 .It
 recursively by itself, because some escape sequence arguments can
 in turn contain other escape sequences,
 .It
 for error detection internally by the
 .Xr roff 7
 parser part of the
 .Lb libmandoc ,
 see the file
 .Pa roff.c ,
 .It
 above all externally by the
 .Xr mandoc
 formatting modules, in particular
 .Fl Tascii
 and
 .Fl Thtml ,
 for formatting purposes, see the files
 .Pa term.c
 and
 .Pa html.c ,
 .It
 and rarely externally by high-level utilities using the mandoc library,
 for example
 .Xr makewhatis 8 ,
 to purge escape sequences from text.
 .El
 .Sh RETURN VALUES
 Upon function return, the pointer
 .Fa end
 is set to the character after the end of the escape sequence,
 such that the calling higher-level parser can easily continue.
 .Pp
 For escape sequences taking an argument, the pointer
 .Fa start
 is set to the beginning of the argument and
 .Fa sz
 is set to the length of the argument.
 For escape sequences not taking an argument,
 .Fa start
 is set to the character after the end of the sequence and
 .Fa sz
 is set to 0.
 Both
 .Fa start
 and
 .Fa sz
 may be
 .Dv NULL ;
 in that case, the argument and the length are not returned.
 .Pp
 For sequences taking an argument, the function
 .Fn mandoc_escape
 returns one of the following values:
 .Bl -tag -width 2n
 .It Dv ESCAPE_FONT
 The escape sequence
 .Ic \ef
 taking an argument in standard form:
 .Ic \ef[ , \ef( , \ef Ns Ar a .
 Two-character arguments starting with the character
 .Sq C
 are reduced to one-character arguments by skipping the
 .Sq C .
 More specific values are returned for the most commonly used arguments:
 .Bl -column "argument" "ESCAPE_FONTITALIC"
 .It argument Ta return value
 .It Cm R No or Cm 1 Ta Dv ESCAPE_FONTROMAN
 .It Cm I No or Cm 2 Ta Dv ESCAPE_FONTITALIC
 .It Cm B No or Cm 3 Ta Dv ESCAPE_FONTBOLD
 .It Cm P Ta Dv ESCAPE_FONTPREV
 .It Cm BI Ta Dv ESCAPE_FONTBI
 .El
 .It Dv ESCAPE_SPECIAL
 The escape sequence
 .Ic \eC
 taking an argument delimited with the single quote character
 and, as a special exception, the escape sequences
 .Em not
 having an identifier, that is, those where the argument, in standard
 form, directly follows the initial backslash:
 .Ic \eC' , \e[ , \e( , \e Ns Ar a .
 Note that the one-character argument short form can only be used for
 argument characters that do not clash with escape sequence identifiers.
 .Pp
-If the argument consists of more than one character
-and starts with the character
-.Sq u ,
-.Dv ESCAPE_UNICODE
-is returned as described below.
-If the argument is just the single character
-.Sq u ,
-.Dv ESCAPE_ERROR
-is returned.
+If the argument matches one of the forms described below under
+.Dv ESCAPE_UNICODE ,
+that value is returned instead.
 .Pp
 The
 .Dv ESCAPE_SPECIAL
 special character escape sequences can be rendered using the functions
 .Fn mchars_spec2cp
 and
 .Fn mchars_spec2str
 described in the
 .Xr mchars_alloc 3
 manual.
 .It Dv ESCAPE_UNICODE
 Escape sequences of the same format as described above under
 .Dv ESCAPE_SPECIAL ,
-but with an argument starting with the character
-.Sq u :
+but with an argument of the forms
+.Ic u Ns Ar XXXX ,
+.Ic u Ns Ar YXXXX ,
+or
+.Ic u10 Ns Ar XXXX
+where
+.Ar X
+and
+.Ar Y
+are hexadecimal digits and
+.Ar Y
+is not zero:
 .Ic \eC'u , \e[u .
 As a special exception,
 .Fa start
 is set to the character after the
-.Sq u ,
+.Ic u ,
 and the
 .Fa sz
 return value does not include the
-.Sq u
+.Ic u
 either.
 .Pp
 Such Unicode character escape sequences can be rendered using the function
 .Fn mchars_num2uc
 described in the
 .Xr mchars_alloc 3
 manual.
 .It Dv ESCAPE_NUMBERED
 The escape sequence
 .Ic \eN
 followed by a delimited argument.
 The delimiter character is arbitrary except that digits cannot be used.
 If a digit is encountered instead of the opening delimiter, that
 digit is considered to be the argument and the end of the sequence, and
 .Dv ESCAPE_IGNORE
 is returned.
 .Pp
 Such ASCII character escape sequences can be rendered using the function
 .Fn mchars_num2char
 described in the
 .Xr mchars_alloc 3
 manual.
 .It Dv ESCAPE_IGNORE
 .Bl -bullet -width 2n
 .It
 The escape sequence
 .Ic \es
 followed by an argument in standard form or by an argument delimited
 by the single quote character:
 .Ic \es' , \es[ , \es( , \es Ns Ar a .
 As a special exception, an optional
 .Sq +
 or
 .Sq \-
 character is allowed after the
 .Sq s
 for all forms.
 .It
 The escape sequences
 .Ic \eF ,
 .Ic \eg ,
 .Ic \ek ,
 .Ic \eM ,
 .Ic \em ,
 .Ic \en ,
 .Ic \eV ,
 and
 .Ic \eY
 followed by an argument in standard form.
 .It
 The escape sequences
 .Ic \eA ,
 .Ic \eb ,
 .Ic \eD ,
 .Ic \eo ,
 .Ic \eR ,
 .Ic \eX ,
 and
 .Ic \eZ
 followed by an argument delimited by an arbitrary character.
 .It
 The escape sequences
 .Ic \eH ,
 .Ic \eh ,
 .Ic \eL ,
 .Ic \el ,
 .Ic \eS ,
 .Ic \ev ,
 and
 .Ic \ex
 followed by an argument delimited by a character that cannot occur
 in numerical expressions.
 However, if any character that can occur in numerical expressions
 is found instead of a delimiter, the sequence is considered to end
 with that character, and
 .Dv ESCAPE_ERROR
 is returned.
 .El
 .It Dv ESCAPE_ERROR
 Escape sequences taking an argument but not matching any of the above patterns.
 In particular, that happens if the end of the logical input line
 is reached before the end of the argument.
 .El
 .Pp
 For sequences that do not take an argument, the function
 .Fn mandoc_escape
 returns one of the following values:
 .Bl -tag -width 2n
 .It Dv ESCAPE_SKIPCHAR
 The escape sequence
 .Qq \ez .
 .It Dv ESCAPE_NOSPACE
 The escape sequence
 .Qq \ec .
 .It Dv ESCAPE_IGNORE
 The escape sequences
 .Qq \ed
 and
 .Qq \eu .
 .El
 .Sh FILES
 This function is implemented in
 .Pa mandoc.c .
 .Sh SEE ALSO
 .Xr mchars_alloc 3 ,
 .Xr mandoc_char 7 ,
 .Xr roff 7
 .Sh HISTORY
 This function has been available since mandoc 1.11.2.
 .Sh AUTHORS
 .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
 .An Ingo Schwarze Aq Mt schwarze@openbsd.org
 .Sh BUGS
 The function doesn't cleanly distinguish between sequences that are
 valid and supported, valid and ignored, valid and unsupported,
 syntactically invalid, or undefined.
 For sequences that are ignored or unsupported, it doesn't tell
 whether that deficiency is likely to cause major formatting problems
 and/or loss of document content.
 The function is already rather complicated and still parses some
 sequences incorrectly.
 .
 .ig
 For these sequences, the list given below specifies a starting string
 and either the length of the argument or an ending character.
 The argument starts after the starting string.
 In the former case, the sequence ends with the end of the argument.
 In the latter case, the argument ends before the ending character,
 and the sequence ends with the ending character.
 ..
Index: vendor/mdocml/dist/mandocdb.c
===================================================================
--- vendor/mdocml/dist/mandocdb.c	(revision 275396)
+++ vendor/mdocml/dist/mandocdb.c	(revision 275397)
@@ -1,2491 +1,2477 @@
-/*	$Id: mandocdb.c,v 1.155 2014/08/06 15:09:05 schwarze Exp $ */
+/*	$Id: mandocdb.c,v 1.171 2014/11/27 01:58:21 schwarze Exp $ */
 /*
  * Copyright (c) 2011, 2012 Kristaps Dzonsons 
  * Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
 #include 
 #include 
 
 #include 
 #include 
 #include 
 #include 
+#if HAVE_FTS
 #include 
+#else
+#include "compat_fts.h"
+#endif
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
-#ifdef HAVE_OHASH
+#if HAVE_OHASH
 #include 
 #else
 #include "compat_ohash.h"
 #endif
 #include 
 
 #include "mdoc.h"
 #include "man.h"
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "manpath.h"
 #include "mansearch.h"
 
 extern int mansearch_keymax;
 extern const char *const mansearch_keynames[];
 
 #define	SQL_EXEC(_v) \
 	if (SQLITE_OK != sqlite3_exec(db, (_v), NULL, NULL, NULL)) \
 		say("", "%s: %s", (_v), sqlite3_errmsg(db))
 #define	SQL_BIND_TEXT(_s, _i, _v) \
 	if (SQLITE_OK != sqlite3_bind_text \
 		((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \
 		say(mlink->file, "%s", sqlite3_errmsg(db))
 #define	SQL_BIND_INT(_s, _i, _v) \
 	if (SQLITE_OK != sqlite3_bind_int \
 		((_s), (_i)++, (_v))) \
 		say(mlink->file, "%s", sqlite3_errmsg(db))
 #define	SQL_BIND_INT64(_s, _i, _v) \
 	if (SQLITE_OK != sqlite3_bind_int64 \
 		((_s), (_i)++, (_v))) \
 		say(mlink->file, "%s", sqlite3_errmsg(db))
 #define SQL_STEP(_s) \
 	if (SQLITE_DONE != sqlite3_step((_s))) \
 		say(mlink->file, "%s", sqlite3_errmsg(db))
 
 enum	op {
 	OP_DEFAULT = 0, /* new dbs from dir list or default config */
 	OP_CONFFILE, /* new databases from custom config file */
 	OP_UPDATE, /* delete/add entries in existing database */
 	OP_DELETE, /* delete entries from existing database */
 	OP_TEST /* change no databases, report potential problems */
 };
 
-enum	form {
-	FORM_NONE,  /* format is unknown */
-	FORM_SRC,   /* format is -man or -mdoc */
-	FORM_CAT    /* format is cat */
-};
-
 struct	str {
 	char		*rendered; /* key in UTF-8 or ASCII form */
 	const struct mpage *mpage; /* if set, the owning parse */
 	uint64_t	 mask; /* bitmask in sequence */
 	char		 key[]; /* may contain escape sequences */
 };
 
 struct	inodev {
 	ino_t		 st_ino;
 	dev_t		 st_dev;
 };
 
 struct	mpage {
 	struct inodev	 inodev;  /* used for hashing routine */
 	int64_t		 pageid;  /* pageid in mpages SQL table */
-	enum form	 form;    /* format from file content */
 	char		*sec;     /* section from file content */
 	char		*arch;    /* architecture from file content */
 	char		*title;   /* title from file content */
 	char		*desc;    /* description from file content */
 	struct mlink	*mlinks;  /* singly linked list */
+	int		 form;    /* format from file content */
 };
 
 struct	mlink {
 	char		 file[PATH_MAX]; /* filename rel. to manpath */
-	enum form	 dform;   /* format from directory */
-	enum form	 fform;   /* format from file name suffix */
 	char		*dsec;    /* section from directory */
 	char		*arch;    /* architecture from directory */
 	char		*name;    /* name from file name (not empty) */
 	char		*fsec;    /* section from file name suffix */
 	struct mlink	*next;    /* singly linked list */
 	struct mpage	*mpage;   /* parent */
+	int		 dform;   /* format from directory */
+	int		 fform;   /* format from file name suffix */
 	int		 gzip;	  /* filename has a .gz suffix */
 };
 
 enum	stmt {
 	STMT_DELETE_PAGE = 0,	/* delete mpage */
 	STMT_INSERT_PAGE,	/* insert mpage */
 	STMT_INSERT_LINK,	/* insert mlink */
 	STMT_INSERT_NAME,	/* insert name */
 	STMT_INSERT_KEY,	/* insert parsed key */
 	STMT__MAX
 };
 
 typedef	int (*mdoc_fp)(struct mpage *, const struct mdoc_node *);
 
 struct	mdoc_handler {
 	mdoc_fp		 fp; /* optional handler */
 	uint64_t	 mask;  /* set unless handler returns 0 */
 };
 
 static	void	 dbclose(int);
 static	void	 dbadd(struct mpage *, struct mchars *);
 static	void	 dbadd_mlink(const struct mlink *mlink);
+static	void	 dbadd_mlink_name(const struct mlink *mlink);
 static	int	 dbopen(int);
 static	void	 dbprune(void);
 static	void	 filescan(const char *);
 static	void	*hash_alloc(size_t, void *);
 static	void	 hash_free(void *, void *);
 static	void	*hash_calloc(size_t, size_t, void *);
 static	void	 mlink_add(struct mlink *, const struct stat *);
 static	void	 mlink_check(struct mpage *, struct mlink *);
 static	void	 mlink_free(struct mlink *);
 static	void	 mlinks_undupe(struct mpage *);
 static	void	 mpages_free(void);
 static	void	 mpages_merge(struct mchars *, struct mparse *);
 static	void	 names_check(void);
 static	void	 parse_cat(struct mpage *, int);
 static	void	 parse_man(struct mpage *, const struct man_node *);
 static	void	 parse_mdoc(struct mpage *, const struct mdoc_node *);
 static	int	 parse_mdoc_body(struct mpage *, const struct mdoc_node *);
 static	int	 parse_mdoc_head(struct mpage *, const struct mdoc_node *);
 static	int	 parse_mdoc_Fd(struct mpage *, const struct mdoc_node *);
 static	int	 parse_mdoc_Fn(struct mpage *, const struct mdoc_node *);
 static	int	 parse_mdoc_Nd(struct mpage *, const struct mdoc_node *);
 static	int	 parse_mdoc_Nm(struct mpage *, const struct mdoc_node *);
 static	int	 parse_mdoc_Sh(struct mpage *, const struct mdoc_node *);
 static	int	 parse_mdoc_Xr(struct mpage *, const struct mdoc_node *);
 static	void	 putkey(const struct mpage *, char *, uint64_t);
 static	void	 putkeys(const struct mpage *,
 			const char *, size_t, uint64_t);
 static	void	 putmdockey(const struct mpage *,
 			const struct mdoc_node *, uint64_t);
 static	void	 render_key(struct mchars *, struct str *);
 static	void	 say(const char *, const char *, ...);
-static	int	 set_basedir(const char *);
+static	int	 set_basedir(const char *, int);
 static	int	 treescan(void);
 static	size_t	 utf8(unsigned int, char [7]);
 
 static	char		 tempfilename[32];
 static	char		*progname;
 static	int		 nodb; /* no database changes */
 static	int		 mparse_options; /* abort the parse early */
 static	int		 use_all; /* use all found files */
 static	int		 debug; /* print what we're doing */
 static	int		 warnings; /* warn about crap */
 static	int		 write_utf8; /* write UTF-8 output; else ASCII */
 static	int		 exitcode; /* to be returned by main */
 static	enum op		 op; /* operational mode */
 static	char		 basedir[PATH_MAX]; /* current base directory */
 static	struct ohash	 mpages; /* table of distinct manual pages */
 static	struct ohash	 mlinks; /* table of directory entries */
 static	struct ohash	 names; /* table of all names */
 static	struct ohash	 strings; /* table of all strings */
 static	sqlite3		*db = NULL; /* current database */
 static	sqlite3_stmt	*stmts[STMT__MAX]; /* current statements */
 static	uint64_t	 name_mask;
 
 static	const struct mdoc_handler mdocs[MDOC_MAX] = {
 	{ NULL, 0 },  /* Ap */
 	{ NULL, 0 },  /* Dd */
 	{ NULL, 0 },  /* Dt */
 	{ NULL, 0 },  /* Os */
 	{ parse_mdoc_Sh, TYPE_Sh }, /* Sh */
 	{ parse_mdoc_head, TYPE_Ss }, /* Ss */
 	{ NULL, 0 },  /* Pp */
 	{ NULL, 0 },  /* D1 */
 	{ NULL, 0 },  /* Dl */
 	{ NULL, 0 },  /* Bd */
 	{ NULL, 0 },  /* Ed */
 	{ NULL, 0 },  /* Bl */
 	{ NULL, 0 },  /* El */
 	{ NULL, 0 },  /* It */
 	{ NULL, 0 },  /* Ad */
 	{ NULL, TYPE_An },  /* An */
 	{ NULL, TYPE_Ar },  /* Ar */
 	{ NULL, TYPE_Cd },  /* Cd */
 	{ NULL, TYPE_Cm },  /* Cm */
 	{ NULL, TYPE_Dv },  /* Dv */
 	{ NULL, TYPE_Er },  /* Er */
 	{ NULL, TYPE_Ev },  /* Ev */
 	{ NULL, 0 },  /* Ex */
 	{ NULL, TYPE_Fa },  /* Fa */
 	{ parse_mdoc_Fd, 0 },  /* Fd */
 	{ NULL, TYPE_Fl },  /* Fl */
 	{ parse_mdoc_Fn, 0 },  /* Fn */
 	{ NULL, TYPE_Ft },  /* Ft */
 	{ NULL, TYPE_Ic },  /* Ic */
 	{ NULL, TYPE_In },  /* In */
 	{ NULL, TYPE_Li },  /* Li */
 	{ parse_mdoc_Nd, 0 },  /* Nd */
 	{ parse_mdoc_Nm, 0 },  /* Nm */
 	{ NULL, 0 },  /* Op */
 	{ NULL, 0 },  /* Ot */
 	{ NULL, TYPE_Pa },  /* Pa */
 	{ NULL, 0 },  /* Rv */
 	{ NULL, TYPE_St },  /* St */
 	{ NULL, TYPE_Va },  /* Va */
 	{ parse_mdoc_body, TYPE_Va },  /* Vt */
 	{ parse_mdoc_Xr, 0 },  /* Xr */
 	{ NULL, 0 },  /* %A */
 	{ NULL, 0 },  /* %B */
 	{ NULL, 0 },  /* %D */
 	{ NULL, 0 },  /* %I */
 	{ NULL, 0 },  /* %J */
 	{ NULL, 0 },  /* %N */
 	{ NULL, 0 },  /* %O */
 	{ NULL, 0 },  /* %P */
 	{ NULL, 0 },  /* %R */
 	{ NULL, 0 },  /* %T */
 	{ NULL, 0 },  /* %V */
 	{ NULL, 0 },  /* Ac */
 	{ NULL, 0 },  /* Ao */
 	{ NULL, 0 },  /* Aq */
 	{ NULL, TYPE_At },  /* At */
 	{ NULL, 0 },  /* Bc */
 	{ NULL, 0 },  /* Bf */
 	{ NULL, 0 },  /* Bo */
 	{ NULL, 0 },  /* Bq */
 	{ NULL, TYPE_Bsx },  /* Bsx */
 	{ NULL, TYPE_Bx },  /* Bx */
 	{ NULL, 0 },  /* Db */
 	{ NULL, 0 },  /* Dc */
 	{ NULL, 0 },  /* Do */
 	{ NULL, 0 },  /* Dq */
 	{ NULL, 0 },  /* Ec */
 	{ NULL, 0 },  /* Ef */
 	{ NULL, TYPE_Em },  /* Em */
 	{ NULL, 0 },  /* Eo */
 	{ NULL, TYPE_Fx },  /* Fx */
 	{ NULL, TYPE_Ms },  /* Ms */
 	{ NULL, 0 },  /* No */
 	{ NULL, 0 },  /* Ns */
 	{ NULL, TYPE_Nx },  /* Nx */
 	{ NULL, TYPE_Ox },  /* Ox */
 	{ NULL, 0 },  /* Pc */
 	{ NULL, 0 },  /* Pf */
 	{ NULL, 0 },  /* Po */
 	{ NULL, 0 },  /* Pq */
 	{ NULL, 0 },  /* Qc */
 	{ NULL, 0 },  /* Ql */
 	{ NULL, 0 },  /* Qo */
 	{ NULL, 0 },  /* Qq */
 	{ NULL, 0 },  /* Re */
 	{ NULL, 0 },  /* Rs */
 	{ NULL, 0 },  /* Sc */
 	{ NULL, 0 },  /* So */
 	{ NULL, 0 },  /* Sq */
 	{ NULL, 0 },  /* Sm */
 	{ NULL, 0 },  /* Sx */
 	{ NULL, TYPE_Sy },  /* Sy */
 	{ NULL, TYPE_Tn },  /* Tn */
 	{ NULL, 0 },  /* Ux */
 	{ NULL, 0 },  /* Xc */
 	{ NULL, 0 },  /* Xo */
 	{ parse_mdoc_head, 0 },  /* Fo */
 	{ NULL, 0 },  /* Fc */
 	{ NULL, 0 },  /* Oo */
 	{ NULL, 0 },  /* Oc */
 	{ NULL, 0 },  /* Bk */
 	{ NULL, 0 },  /* Ek */
 	{ NULL, 0 },  /* Bt */
 	{ NULL, 0 },  /* Hf */
 	{ NULL, 0 },  /* Fr */
 	{ NULL, 0 },  /* Ud */
 	{ NULL, TYPE_Lb },  /* Lb */
 	{ NULL, 0 },  /* Lp */
 	{ NULL, TYPE_Lk },  /* Lk */
 	{ NULL, TYPE_Mt },  /* Mt */
 	{ NULL, 0 },  /* Brq */
 	{ NULL, 0 },  /* Bro */
 	{ NULL, 0 },  /* Brc */
 	{ NULL, 0 },  /* %C */
 	{ NULL, 0 },  /* Es */
 	{ NULL, 0 },  /* En */
 	{ NULL, TYPE_Dx },  /* Dx */
 	{ NULL, 0 },  /* %Q */
 	{ NULL, 0 },  /* br */
 	{ NULL, 0 },  /* sp */
 	{ NULL, 0 },  /* %U */
 	{ NULL, 0 },  /* Ta */
+	{ NULL, 0 },  /* ll */
 };
 
 
 int
 main(int argc, char *argv[])
 {
 	int		  ch, i;
 	size_t		  j, sz;
 	const char	 *path_arg;
 	struct mchars	 *mc;
 	struct manpaths	  dirs;
 	struct mparse	 *mp;
 	struct ohash_info mpages_info, mlinks_info;
 
 	memset(stmts, 0, STMT__MAX * sizeof(sqlite3_stmt *));
 	memset(&dirs, 0, sizeof(struct manpaths));
 
 	mpages_info.alloc  = mlinks_info.alloc  = hash_alloc;
 	mpages_info.calloc = mlinks_info.calloc = hash_calloc;
 	mpages_info.free  = mlinks_info.free  = hash_free;
 
 	mpages_info.key_offset = offsetof(struct mpage, inodev);
 	mlinks_info.key_offset = offsetof(struct mlink, file);
 
 	progname = strrchr(argv[0], '/');
 	if (progname == NULL)
 		progname = argv[0];
 	else
 		++progname;
 
 	/*
 	 * We accept a few different invocations.
 	 * The CHECKOP macro makes sure that invocation styles don't
 	 * clobber each other.
 	 */
 #define	CHECKOP(_op, _ch) do \
 	if (OP_DEFAULT != (_op)) { \
 		fprintf(stderr, "%s: -%c: Conflicting option\n", \
 		    progname, (_ch)); \
 		goto usage; \
 	} while (/*CONSTCOND*/0)
 
 	path_arg = NULL;
 	op = OP_DEFAULT;
 
 	while (-1 != (ch = getopt(argc, argv, "aC:Dd:npQT:tu:v")))
 		switch (ch) {
 		case 'a':
 			use_all = 1;
 			break;
 		case 'C':
 			CHECKOP(op, ch);
 			path_arg = optarg;
 			op = OP_CONFFILE;
 			break;
 		case 'D':
 			debug++;
 			break;
 		case 'd':
 			CHECKOP(op, ch);
 			path_arg = optarg;
 			op = OP_UPDATE;
 			break;
 		case 'n':
 			nodb = 1;
 			break;
 		case 'p':
 			warnings = 1;
 			break;
 		case 'Q':
 			mparse_options |= MPARSE_QUICK;
 			break;
 		case 'T':
 			if (strcmp(optarg, "utf8")) {
 				fprintf(stderr, "%s: -T%s: "
 				    "Unsupported output format\n",
 				    progname, optarg);
 				goto usage;
 			}
 			write_utf8 = 1;
 			break;
 		case 't':
 			CHECKOP(op, ch);
 			dup2(STDOUT_FILENO, STDERR_FILENO);
 			op = OP_TEST;
 			nodb = warnings = 1;
 			break;
 		case 'u':
 			CHECKOP(op, ch);
 			path_arg = optarg;
 			op = OP_DELETE;
 			break;
 		case 'v':
 			/* Compatibility with espie@'s makewhatis. */
 			break;
 		default:
 			goto usage;
 		}
 
 	argc -= optind;
 	argv += optind;
 
 	if (OP_CONFFILE == op && argc > 0) {
 		fprintf(stderr, "%s: -C: Too many arguments\n",
 		    progname);
 		goto usage;
 	}
 
 	exitcode = (int)MANDOCLEVEL_OK;
-	mp = mparse_alloc(mparse_options, MANDOCLEVEL_FATAL, NULL, NULL);
 	mc = mchars_alloc();
-
+	mp = mparse_alloc(mparse_options, MANDOCLEVEL_FATAL, NULL,
+	    mc, NULL);
 	ohash_init(&mpages, 6, &mpages_info);
 	ohash_init(&mlinks, 6, &mlinks_info);
 
 	if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) {
 
 		/*
 		 * Most of these deal with a specific directory.
 		 * Jump into that directory first.
 		 */
-		if (OP_TEST != op && 0 == set_basedir(path_arg))
+		if (OP_TEST != op && 0 == set_basedir(path_arg, 1))
 			goto out;
 
 		if (dbopen(1)) {
 			/*
 			 * The existing database is usable.  Process
 			 * all files specified on the command-line.
 			 */
 			use_all = 1;
 			for (i = 0; i < argc; i++)
 				filescan(argv[i]);
 			if (OP_TEST != op)
 				dbprune();
 		} else {
 			/*
 			 * Database missing or corrupt.
 			 * Recreate from scratch.
 			 */
 			exitcode = (int)MANDOCLEVEL_OK;
 			op = OP_DEFAULT;
 			if (0 == treescan())
 				goto out;
 			if (0 == dbopen(0))
 				goto out;
 		}
 		if (OP_DELETE != op)
 			mpages_merge(mc, mp);
 		dbclose(OP_DEFAULT == op ? 0 : 1);
 	} else {
 		/*
 		 * If we have arguments, use them as our manpaths.
 		 * If we don't, grok from manpath(1) or however else
 		 * manpath_parse() wants to do it.
 		 */
 		if (argc > 0) {
 			dirs.paths = mandoc_reallocarray(NULL,
 			    argc, sizeof(char *));
 			dirs.sz = (size_t)argc;
 			for (i = 0; i < argc; i++)
 				dirs.paths[i] = mandoc_strdup(argv[i]);
 		} else
 			manpath_parse(&dirs, path_arg, NULL, NULL);
 
 		if (0 == dirs.sz) {
 			exitcode = (int)MANDOCLEVEL_BADARG;
 			say("", "Empty manpath");
 		}
 
 		/*
 		 * First scan the tree rooted at a base directory, then
 		 * build a new database and finally move it into place.
 		 * Ignore zero-length directories and strip trailing
 		 * slashes.
 		 */
 		for (j = 0; j < dirs.sz; j++) {
 			sz = strlen(dirs.paths[j]);
 			if (sz && '/' == dirs.paths[j][sz - 1])
 				dirs.paths[j][--sz] = '\0';
 			if (0 == sz)
 				continue;
 
 			if (j) {
 				ohash_init(&mpages, 6, &mpages_info);
 				ohash_init(&mlinks, 6, &mlinks_info);
 			}
 
-			if (0 == set_basedir(dirs.paths[j]))
-				goto out;
+			if (0 == set_basedir(dirs.paths[j], argc > 0))
+				continue;
 			if (0 == treescan())
-				goto out;
+				continue;
 			if (0 == dbopen(0))
-				goto out;
+				continue;
 
 			mpages_merge(mc, mp);
 			if (warnings && !nodb &&
 			    ! (MPARSE_QUICK & mparse_options))
 				names_check();
 			dbclose(0);
 
 			if (j + 1 < dirs.sz) {
 				mpages_free();
 				ohash_delete(&mpages);
 				ohash_delete(&mlinks);
 			}
 		}
 	}
 out:
 	manpath_free(&dirs);
-	mchars_free(mc);
 	mparse_free(mp);
+	mchars_free(mc);
 	mpages_free();
 	ohash_delete(&mpages);
 	ohash_delete(&mlinks);
 	return(exitcode);
 usage:
 	fprintf(stderr, "usage: %s [-aDnpQ] [-C file] [-Tutf8]\n"
 			"       %s [-aDnpQ] [-Tutf8] dir ...\n"
 			"       %s [-DnpQ] [-Tutf8] -d dir [file ...]\n"
 			"       %s [-Dnp] -u dir [file ...]\n"
 			"       %s [-Q] -t file ...\n",
 		       progname, progname, progname,
 		       progname, progname);
 
 	return((int)MANDOCLEVEL_BADARG);
 }
 
 /*
  * Scan a directory tree rooted at "basedir" for manpages.
  * We use fts(), scanning directory parts along the way for clues to our
  * section and architecture.
  *
  * If use_all has been specified, grok all files.
  * If not, sanitise paths to the following:
  *
  *   [./]man*[/]/.
* or * [./]cat
[/]/.0 * * TODO: accomodate for multi-language directories. */ static int treescan(void) { char buf[PATH_MAX]; FTS *f; FTSENT *ff; struct mlink *mlink; int dform, gzip; char *dsec, *arch, *fsec, *cp; const char *path; const char *argv[2]; argv[0] = "."; argv[1] = (char *)NULL; f = fts_open((char * const *)argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL); if (NULL == f) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&fts_open"); return(0); } dsec = arch = NULL; dform = FORM_NONE; while (NULL != (ff = fts_read(f))) { path = ff->fts_path + 2; switch (ff->fts_info) { /* * Symbolic links require various sanity checks, * then get handled just like regular files. */ case FTS_SL: if (NULL == realpath(path, buf)) { if (warnings) say(path, "&realpath"); continue; } if (strstr(buf, basedir) != buf) { if (warnings) say("", "%s: outside base directory", buf); continue; } /* Use logical inode to avoid mpages dupe. */ if (-1 == stat(path, ff->fts_statp)) { if (warnings) say(path, "&stat"); continue; } /* FALLTHROUGH */ /* * If we're a regular file, add an mlink by using the * stored directory data and handling the filename. */ case FTS_F: if (0 == strcmp(path, MANDOC_DB)) continue; if ( ! use_all && ff->fts_level < 2) { if (warnings) say(path, "Extraneous file"); continue; } gzip = 0; fsec = NULL; while (NULL == fsec) { fsec = strrchr(ff->fts_name, '.'); if (NULL == fsec || strcmp(fsec+1, "gz")) break; gzip = 1; *fsec = '\0'; fsec = NULL; } if (NULL == fsec) { if ( ! use_all) { if (warnings) say(path, "No filename suffix"); continue; } } else if (0 == strcmp(++fsec, "html")) { if (warnings) say(path, "Skip html"); continue; } else if (0 == strcmp(fsec, "ps")) { if (warnings) say(path, "Skip ps"); continue; } else if (0 == strcmp(fsec, "pdf")) { if (warnings) say(path, "Skip pdf"); continue; } else if ( ! use_all && ((FORM_SRC == dform && strcmp(fsec, dsec)) || (FORM_CAT == dform && strcmp(fsec, "0")))) { if (warnings) say(path, "Wrong filename suffix"); continue; } else fsec[-1] = '\0'; mlink = mandoc_calloc(1, sizeof(struct mlink)); if (strlcpy(mlink->file, path, sizeof(mlink->file)) >= sizeof(mlink->file)) { say(path, "Filename too long"); free(mlink); continue; } mlink->dform = dform; mlink->dsec = dsec; mlink->arch = arch; mlink->name = ff->fts_name; mlink->fsec = fsec; mlink->gzip = gzip; mlink_add(mlink, ff->fts_statp); continue; case FTS_D: /* FALLTHROUGH */ case FTS_DP: break; default: if (warnings) say(path, "Not a regular file"); continue; } switch (ff->fts_level) { case 0: /* Ignore the root directory. */ break; case 1: /* * This might contain manX/ or catX/. * Try to infer this from the name. * If we're not in use_all, enforce it. */ cp = ff->fts_name; if (FTS_DP == ff->fts_info) break; if (0 == strncmp(cp, "man", 3)) { dform = FORM_SRC; dsec = cp + 3; } else if (0 == strncmp(cp, "cat", 3)) { dform = FORM_CAT; dsec = cp + 3; } else { dform = FORM_NONE; dsec = NULL; } if (NULL != dsec || use_all) break; if (warnings) say(path, "Unknown directory part"); fts_set(f, ff, FTS_SKIP); break; case 2: /* * Possibly our architecture. * If we're descending, keep tabs on it. */ if (FTS_DP != ff->fts_info && NULL != dsec) arch = ff->fts_name; else arch = NULL; break; default: if (FTS_DP == ff->fts_info || use_all) break; if (warnings) say(path, "Extraneous directory part"); fts_set(f, ff, FTS_SKIP); break; } } fts_close(f); return(1); } /* * Add a file to the mlinks table. * Do not verify that it's a "valid" looking manpage (we'll do that * later). * * Try to infer the manual section, architecture, and page name from the * path, assuming it looks like * * [./]man*[/]/.
* or * [./]cat
[/]/.0 * * See treescan() for the fts(3) version of this. */ static void filescan(const char *file) { char buf[PATH_MAX]; struct stat st; struct mlink *mlink; char *p, *start; assert(use_all); if (0 == strncmp(file, "./", 2)) file += 2; /* * We have to do lstat(2) before realpath(3) loses * the information whether this is a symbolic link. * We need to know that because for symbolic links, * we want to use the orginal file name, while for * regular files, we want to use the real path. */ if (-1 == lstat(file, &st)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "&lstat"); return; } else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "Not a regular file"); return; } /* * We have to resolve the file name to the real path * in any case for the base directory check. */ if (NULL == realpath(file, buf)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "&realpath"); return; } if (OP_TEST == op) start = buf; else if (strstr(buf, basedir) == buf) start = buf + strlen(basedir); else { exitcode = (int)MANDOCLEVEL_BADARG; say("", "%s: outside base directory", buf); return; } /* * Now we are sure the file is inside our tree. * If it is a symbolic link, ignore the real path * and use the original name. * This implies passing stuff like "cat1/../man1/foo.1" * on the command line won't work. So don't do that. * Note the stat(2) can still fail if the link target * doesn't exist. */ if (S_IFLNK & st.st_mode) { if (-1 == stat(buf, &st)) { exitcode = (int)MANDOCLEVEL_BADARG; say(file, "&stat"); return; } if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { say(file, "Filename too long"); return; } start = buf; if (OP_TEST != op && strstr(buf, basedir) == buf) start += strlen(basedir); } mlink = mandoc_calloc(1, sizeof(struct mlink)); + mlink->dform = FORM_NONE; if (strlcpy(mlink->file, start, sizeof(mlink->file)) >= sizeof(mlink->file)) { say(start, "Filename too long"); return; } /* * First try to guess our directory structure. * If we find a separator, try to look for man* or cat*. * If we find one of these and what's underneath is a directory, * assume it's an architecture. */ if (NULL != (p = strchr(start, '/'))) { *p++ = '\0'; if (0 == strncmp(start, "man", 3)) { mlink->dform = FORM_SRC; mlink->dsec = start + 3; } else if (0 == strncmp(start, "cat", 3)) { mlink->dform = FORM_CAT; mlink->dsec = start + 3; } start = p; if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) { *p++ = '\0'; mlink->arch = start; start = p; } } /* * Now check the file suffix. * Suffix of `.0' indicates a catpage, `.1-9' is a manpage. */ p = strrchr(start, '\0'); while (p-- > start && '/' != *p && '.' != *p) /* Loop. */ ; if ('.' == *p) { *p++ = '\0'; mlink->fsec = p; } /* * Now try to parse the name. * Use the filename portion of the path. */ mlink->name = start; if (NULL != (p = strrchr(start, '/'))) { mlink->name = p + 1; *p = '\0'; } mlink_add(mlink, &st); } static void mlink_add(struct mlink *mlink, const struct stat *st) { struct inodev inodev; struct mpage *mpage; unsigned int slot; assert(NULL != mlink->file); mlink->dsec = mandoc_strdup(mlink->dsec ? mlink->dsec : ""); mlink->arch = mandoc_strdup(mlink->arch ? mlink->arch : ""); mlink->name = mandoc_strdup(mlink->name ? mlink->name : ""); mlink->fsec = mandoc_strdup(mlink->fsec ? mlink->fsec : ""); if ('0' == *mlink->fsec) { free(mlink->fsec); mlink->fsec = mandoc_strdup(mlink->dsec); mlink->fform = FORM_CAT; } else if ('1' <= *mlink->fsec && '9' >= *mlink->fsec) mlink->fform = FORM_SRC; else mlink->fform = FORM_NONE; slot = ohash_qlookup(&mlinks, mlink->file); assert(NULL == ohash_find(&mlinks, slot)); ohash_insert(&mlinks, slot, mlink); inodev.st_ino = st->st_ino; inodev.st_dev = st->st_dev; slot = ohash_lookup_memory(&mpages, (char *)&inodev, sizeof(struct inodev), inodev.st_ino); mpage = ohash_find(&mpages, slot); if (NULL == mpage) { mpage = mandoc_calloc(1, sizeof(struct mpage)); mpage->inodev.st_ino = inodev.st_ino; mpage->inodev.st_dev = inodev.st_dev; ohash_insert(&mpages, slot, mpage); } else mlink->next = mpage->mlinks; mpage->mlinks = mlink; mlink->mpage = mpage; } static void mlink_free(struct mlink *mlink) { free(mlink->dsec); free(mlink->arch); free(mlink->name); free(mlink->fsec); free(mlink); } static void mpages_free(void) { struct mpage *mpage; struct mlink *mlink; unsigned int slot; mpage = ohash_first(&mpages, &slot); while (NULL != mpage) { while (NULL != (mlink = mpage->mlinks)) { mpage->mlinks = mlink->next; mlink_free(mlink); } free(mpage->sec); free(mpage->arch); free(mpage->title); free(mpage->desc); free(mpage); mpage = ohash_next(&mpages, &slot); } } /* * For each mlink to the mpage, check whether the path looks like * it is formatted, and if it does, check whether a source manual * exists by the same name, ignoring the suffix. * If both conditions hold, drop the mlink. */ static void mlinks_undupe(struct mpage *mpage) { char buf[PATH_MAX]; struct mlink **prev; struct mlink *mlink; char *bufp; mpage->form = FORM_CAT; prev = &mpage->mlinks; while (NULL != (mlink = *prev)) { if (FORM_CAT != mlink->dform) { mpage->form = FORM_NONE; goto nextlink; } (void)strlcpy(buf, mlink->file, sizeof(buf)); bufp = strstr(buf, "cat"); assert(NULL != bufp); memcpy(bufp, "man", 3); if (NULL != (bufp = strrchr(buf, '.'))) *++bufp = '\0'; (void)strlcat(buf, mlink->dsec, sizeof(buf)); if (NULL == ohash_find(&mlinks, ohash_qlookup(&mlinks, buf))) goto nextlink; if (warnings) say(mlink->file, "Man source exists: %s", buf); if (use_all) goto nextlink; *prev = mlink->next; mlink_free(mlink); continue; nextlink: prev = &(*prev)->next; } } static void mlink_check(struct mpage *mpage, struct mlink *mlink) { struct str *str; unsigned int slot; /* * Check whether the manual section given in a file * agrees with the directory where the file is located. * Some manuals have suffixes like (3p) on their * section number either inside the file or in the * directory name, some are linked into more than one * section, like encrypt(1) = makekey(8). */ if (FORM_SRC == mpage->form && strcasecmp(mpage->sec, mlink->dsec)) say(mlink->file, "Section \"%s\" manual in %s directory", mpage->sec, mlink->dsec); /* * Manual page directories exist for each kernel * architecture as returned by machine(1). * However, many manuals only depend on the * application architecture as returned by arch(1). * For example, some (2/ARM) manuals are shared * across the "armish" and "zaurus" kernel * architectures. * A few manuals are even shared across completely * different architectures, for example fdformat(1) * on amd64, i386, sparc, and sparc64. */ if (strcasecmp(mpage->arch, mlink->arch)) say(mlink->file, "Architecture \"%s\" manual in " "\"%s\" directory", mpage->arch, mlink->arch); /* * XXX * parse_cat() doesn't set NAME_TITLE yet. */ if (FORM_CAT == mpage->form) return; /* * Check whether this mlink * appears as a name in the NAME section. */ slot = ohash_qlookup(&names, mlink->name); str = ohash_find(&names, slot); assert(NULL != str); if ( ! (NAME_TITLE & str->mask)) say(mlink->file, "Name missing in NAME section"); } /* * Run through the files in the global vector "mpages" * and add them to the database specified in "basedir". * * This handles the parsing scheme itself, using the cues of directory * and filename to determine whether the file is parsable or not. */ static void mpages_merge(struct mchars *mc, struct mparse *mp) { char any[] = "any"; struct ohash_info str_info; - int fd[2]; struct mpage *mpage, *mpage_dest; struct mlink *mlink, *mlink_dest; struct mdoc *mdoc; struct man *man; char *sodest; char *cp; - pid_t child_pid; - int status; + int fd; unsigned int pslot; enum mandoclevel lvl; str_info.alloc = hash_alloc; str_info.calloc = hash_calloc; str_info.free = hash_free; str_info.key_offset = offsetof(struct str, key); - if (0 == nodb) + if ( ! nodb) SQL_EXEC("BEGIN TRANSACTION"); mpage = ohash_first(&mpages, &pslot); - while (NULL != mpage) { + while (mpage != NULL) { mlinks_undupe(mpage); - if (NULL == mpage->mlinks) { + if (mpage->mlinks == NULL) { mpage = ohash_next(&mpages, &pslot); continue; } name_mask = NAME_MASK; ohash_init(&names, 4, &str_info); ohash_init(&strings, 6, &str_info); mparse_reset(mp); mdoc = NULL; man = NULL; sodest = NULL; - child_pid = 0; - fd[0] = -1; - fd[1] = -1; - if (mpage->mlinks->gzip) { - if (-1 == pipe(fd)) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say(mpage->mlinks->file, "&pipe gunzip"); - goto nextpage; - } - switch (child_pid = fork()) { - case -1: - exitcode = (int)MANDOCLEVEL_SYSERR; - say(mpage->mlinks->file, "&fork gunzip"); - child_pid = 0; - close(fd[1]); - close(fd[0]); - goto nextpage; - case 0: - close(fd[0]); - if (-1 == dup2(fd[1], STDOUT_FILENO)) { - say(mpage->mlinks->file, - "&dup gunzip"); - exit(1); - } - execlp("gunzip", "gunzip", "-c", - mpage->mlinks->file, NULL); - say(mpage->mlinks->file, "&exec gunzip"); - exit(1); - default: - close(fd[1]); - break; - } + mparse_open(mp, &fd, mpage->mlinks->file); + if (fd == -1) { + say(mpage->mlinks->file, "&open"); + goto nextpage; } /* * Try interpreting the file as mdoc(7) or man(7) * source code, unless it is already known to be * formatted. Fall back to formatted mode. */ - if (FORM_CAT != mpage->mlinks->dform || - FORM_CAT != mpage->mlinks->fform) { - lvl = mparse_readfd(mp, fd[0], mpage->mlinks->file); + if (mpage->mlinks->dform != FORM_CAT || + mpage->mlinks->fform != FORM_CAT) { + lvl = mparse_readfd(mp, fd, mpage->mlinks->file); if (lvl < MANDOCLEVEL_FATAL) mparse_result(mp, &mdoc, &man, &sodest); } - if (NULL != sodest) { + if (sodest != NULL) { mlink_dest = ohash_find(&mlinks, ohash_qlookup(&mlinks, sodest)); - if (NULL != mlink_dest) { + if (mlink_dest == NULL) { + mandoc_asprintf(&cp, "%s.gz", sodest); + mlink_dest = ohash_find(&mlinks, + ohash_qlookup(&mlinks, cp)); + free(cp); + } + if (mlink_dest != NULL) { /* The .so target exists. */ mpage_dest = mlink_dest->mpage; mlink = mpage->mlinks; while (1) { mlink->mpage = mpage_dest; /* * If the target was already * processed, add the links * to the database now. * Otherwise, this will * happen when we come * to the target. */ if (mpage_dest->pageid) - dbadd_mlink(mlink); + dbadd_mlink_name(mlink); - if (NULL == mlink->next) + if (mlink->next == NULL) break; mlink = mlink->next; } /* Move all links to the target. */ mlink->next = mlink_dest->next; mlink_dest->next = mpage->mlinks; mpage->mlinks = NULL; } goto nextpage; - } else if (NULL != mdoc) { + } else if (mdoc != NULL) { mpage->form = FORM_SRC; mpage->sec = mdoc_meta(mdoc)->msec; mpage->sec = mandoc_strdup( - NULL == mpage->sec ? "" : mpage->sec); + mpage->sec == NULL ? "" : mpage->sec); mpage->arch = mdoc_meta(mdoc)->arch; mpage->arch = mandoc_strdup( - NULL == mpage->arch ? "" : mpage->arch); + mpage->arch == NULL ? "" : mpage->arch); mpage->title = mandoc_strdup(mdoc_meta(mdoc)->title); - } else if (NULL != man) { + } else if (man != NULL) { mpage->form = FORM_SRC; mpage->sec = mandoc_strdup(man_meta(man)->msec); mpage->arch = mandoc_strdup(mpage->mlinks->arch); mpage->title = mandoc_strdup(man_meta(man)->title); } else { mpage->form = FORM_CAT; mpage->sec = mandoc_strdup(mpage->mlinks->dsec); mpage->arch = mandoc_strdup(mpage->mlinks->arch); mpage->title = mandoc_strdup(mpage->mlinks->name); } putkey(mpage, mpage->sec, TYPE_sec); - putkey(mpage, '\0' == *mpage->arch ? - any : mpage->arch, TYPE_arch); + if (*mpage->arch != '\0') + putkey(mpage, mpage->arch, TYPE_arch); for (mlink = mpage->mlinks; mlink; mlink = mlink->next) { if ('\0' != *mlink->dsec) putkey(mpage, mlink->dsec, TYPE_sec); if ('\0' != *mlink->fsec) putkey(mpage, mlink->fsec, TYPE_sec); putkey(mpage, '\0' == *mlink->arch ? any : mlink->arch, TYPE_arch); putkey(mpage, mlink->name, NAME_FILE); } assert(NULL == mpage->desc); if (NULL != mdoc) { if (NULL != (cp = mdoc_meta(mdoc)->name)) putkey(mpage, cp, NAME_HEAD); parse_mdoc(mpage, mdoc_node(mdoc)); } else if (NULL != man) parse_man(mpage, man_node(man)); else - parse_cat(mpage, fd[0]); + parse_cat(mpage, fd); if (NULL == mpage->desc) mpage->desc = mandoc_strdup(mpage->mlinks->name); if (warnings && !use_all) for (mlink = mpage->mlinks; mlink; mlink = mlink->next) mlink_check(mpage, mlink); dbadd(mpage, mc); nextpage: - if (child_pid) { - if (-1 == waitpid(child_pid, &status, 0)) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say(mpage->mlinks->file, "&wait gunzip"); - } else if (WIFSIGNALED(status)) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say(mpage->mlinks->file, - "gunzip died from signal %d", - WTERMSIG(status)); - } else if (WEXITSTATUS(status)) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say(mpage->mlinks->file, - "gunzip failed with code %d", - WEXITSTATUS(status)); - } + if (mparse_wait(mp) != MANDOCLEVEL_OK) { + exitcode = (int)MANDOCLEVEL_SYSERR; + say(mpage->mlinks->file, "&wait gunzip"); } ohash_delete(&strings); ohash_delete(&names); mpage = ohash_next(&mpages, &pslot); } if (0 == nodb) SQL_EXEC("END TRANSACTION"); } static void names_check(void) { sqlite3_stmt *stmt; const char *name, *sec, *arch, *key; int irc; sqlite3_prepare_v2(db, "SELECT name, sec, arch, key FROM (" "SELECT name AS key, pageid FROM names " "WHERE bits & ? AND NOT EXISTS (" "SELECT pageid FROM mlinks " "WHERE mlinks.pageid == names.pageid " "AND mlinks.name == names.name" ")" ") JOIN (" "SELECT sec, arch, name, pageid FROM mlinks " "GROUP BY pageid" ") USING (pageid);", -1, &stmt, NULL); if (SQLITE_OK != sqlite3_bind_int64(stmt, 1, NAME_TITLE)) say("", "%s", sqlite3_errmsg(db)); while (SQLITE_ROW == (irc = sqlite3_step(stmt))) { name = (const char *)sqlite3_column_text(stmt, 0); sec = (const char *)sqlite3_column_text(stmt, 1); arch = (const char *)sqlite3_column_text(stmt, 2); key = (const char *)sqlite3_column_text(stmt, 3); say("", "%s(%s%s%s) lacks mlink \"%s\"", name, sec, '\0' == *arch ? "" : "/", '\0' == *arch ? "" : arch, key); } sqlite3_finalize(stmt); } static void parse_cat(struct mpage *mpage, int fd) { FILE *stream; char *line, *p, *title; size_t len, plen, titlesz; stream = (-1 == fd) ? fopen(mpage->mlinks->file, "r") : fdopen(fd, "r"); if (NULL == stream) { + if (-1 != fd) + close(fd); if (warnings) say(mpage->mlinks->file, "&fopen"); return; } /* Skip to first blank line. */ while (NULL != (line = fgetln(stream, &len))) if ('\n' == *line) break; /* * Assume the first line that is not indented * is the first section header. Skip to it. */ while (NULL != (line = fgetln(stream, &len))) if ('\n' != *line && ' ' != *line) break; /* * Read up until the next section into a buffer. * Strip the leading and trailing newline from each read line, * appending a trailing space. * Ignore empty (whitespace-only) lines. */ titlesz = 0; title = NULL; while (NULL != (line = fgetln(stream, &len))) { if (' ' != *line || '\n' != line[len - 1]) break; while (len > 0 && isspace((unsigned char)*line)) { line++; len--; } if (1 == len) continue; title = mandoc_realloc(title, titlesz + len); memcpy(title + titlesz, line, len); titlesz += len; title[titlesz - 1] = ' '; } /* * If no page content can be found, or the input line * is already the next section header, or there is no * trailing newline, reuse the page title as the page * description. */ if (NULL == title || '\0' == *title) { if (warnings) say(mpage->mlinks->file, "Cannot find NAME section"); fclose(stream); free(title); return; } title = mandoc_realloc(title, titlesz + 1); title[titlesz] = '\0'; /* * Skip to the first dash. * Use the remaining line as the description (no more than 70 * bytes). */ if (NULL != (p = strstr(title, "- "))) { for (p += 2; ' ' == *p || '\b' == *p; p++) /* Skip to next word. */ ; } else { if (warnings) say(mpage->mlinks->file, "No dash in title line"); p = title; } plen = strlen(p); /* Strip backspace-encoding from line. */ while (NULL != (line = memchr(p, '\b', plen))) { len = line - p; if (0 == len) { memmove(line, line + 1, plen--); continue; } memmove(line - 1, line + 1, plen - len); plen -= 2; } mpage->desc = mandoc_strdup(p); fclose(stream); free(title); } /* * Put a type/word pair into the word database for this particular file. */ static void putkey(const struct mpage *mpage, char *value, uint64_t type) { char *cp; assert(NULL != value); if (TYPE_arch == type) for (cp = value; *cp; cp++) if (isupper((unsigned char)*cp)) *cp = _tolower((unsigned char)*cp); putkeys(mpage, value, strlen(value), type); } /* * Grok all nodes at or below a certain mdoc node into putkey(). */ static void putmdockey(const struct mpage *mpage, const struct mdoc_node *n, uint64_t m) { for ( ; NULL != n; n = n->next) { if (NULL != n->child) putmdockey(mpage, n->child, m); if (MDOC_TEXT == n->type) putkey(mpage, n->string, m); } } static void parse_man(struct mpage *mpage, const struct man_node *n) { const struct man_node *head, *body; char *start, *title; char byte; size_t sz; if (NULL == n) return; /* * We're only searching for one thing: the first text child in * the BODY of a NAME section. Since we don't keep track of * sections in -man, run some hoops to find out whether we're in * the correct section or not. */ if (MAN_BODY == n->type && MAN_SH == n->tok) { body = n; assert(body->parent); if (NULL != (head = body->parent->head) && 1 == head->nchild && NULL != (head = (head->child)) && MAN_TEXT == head->type && 0 == strcmp(head->string, "NAME") && NULL != body->child) { /* * Suck the entire NAME section into memory. * Yes, we might run away. * But too many manuals have big, spread-out * NAME sections over many lines. */ title = NULL; man_deroff(&title, body); if (NULL == title) return; /* * Go through a special heuristic dance here. * Conventionally, one or more manual names are * comma-specified prior to a whitespace, then a * dash, then a description. Try to puzzle out * the name parts here. */ start = title; for ( ;; ) { sz = strcspn(start, " ,"); if ('\0' == start[sz]) break; byte = start[sz]; start[sz] = '\0'; /* * Assume a stray trailing comma in the * name list if a name begins with a dash. */ if ('-' == start[0] || ('\\' == start[0] && '-' == start[1])) break; putkey(mpage, start, NAME_TITLE); if (' ' == byte) { start += sz + 1; break; } assert(',' == byte); start += sz + 1; while (' ' == *start) start++; } if (start == title) { putkey(mpage, start, NAME_TITLE); free(title); return; } while (isspace((unsigned char)*start)) start++; if (0 == strncmp(start, "-", 1)) start += 1; else if (0 == strncmp(start, "\\-\\-", 4)) start += 4; else if (0 == strncmp(start, "\\-", 2)) start += 2; else if (0 == strncmp(start, "\\(en", 4)) start += 4; else if (0 == strncmp(start, "\\(em", 4)) start += 4; while (' ' == *start) start++; mpage->desc = mandoc_strdup(start); free(title); return; } } for (n = n->child; n; n = n->next) { if (NULL != mpage->desc) break; parse_man(mpage, n); } } static void parse_mdoc(struct mpage *mpage, const struct mdoc_node *n) { assert(NULL != n); for (n = n->child; NULL != n; n = n->next) { switch (n->type) { case MDOC_ELEM: /* FALLTHROUGH */ case MDOC_BLOCK: /* FALLTHROUGH */ case MDOC_HEAD: /* FALLTHROUGH */ case MDOC_BODY: /* FALLTHROUGH */ case MDOC_TAIL: if (NULL != mdocs[n->tok].fp) if (0 == (*mdocs[n->tok].fp)(mpage, n)) break; if (mdocs[n->tok].mask) putmdockey(mpage, n->child, mdocs[n->tok].mask); break; default: assert(MDOC_ROOT != n->type); continue; } if (NULL != n->child) parse_mdoc(mpage, n); } } static int parse_mdoc_Fd(struct mpage *mpage, const struct mdoc_node *n) { const char *start, *end; size_t sz; if (SEC_SYNOPSIS != n->sec || NULL == (n = n->child) || MDOC_TEXT != n->type) return(0); /* * Only consider those `Fd' macro fields that begin with an * "inclusion" token (versus, e.g., #define). */ if (strcmp("#include", n->string)) return(0); if (NULL == (n = n->next) || MDOC_TEXT != n->type) return(0); /* * Strip away the enclosing angle brackets and make sure we're * not zero-length. */ start = n->string; if ('<' == *start || '"' == *start) start++; if (0 == (sz = strlen(start))) return(0); end = &start[(int)sz - 1]; if ('>' == *end || '"' == *end) end--; if (end > start) putkeys(mpage, start, end - start + 1, TYPE_In); return(0); } static int parse_mdoc_Fn(struct mpage *mpage, const struct mdoc_node *n) { char *cp; if (NULL == (n = n->child) || MDOC_TEXT != n->type) return(0); /* * Parse: .Fn "struct type *name" "char *arg". * First strip away pointer symbol. * Then store the function name, then type. * Finally, store the arguments. */ if (NULL == (cp = strrchr(n->string, ' '))) cp = n->string; while ('*' == *cp) cp++; putkey(mpage, cp, TYPE_Fn); if (n->string < cp) putkeys(mpage, n->string, cp - n->string, TYPE_Ft); for (n = n->next; NULL != n; n = n->next) if (MDOC_TEXT == n->type) putkey(mpage, n->string, TYPE_Fa); return(0); } static int parse_mdoc_Xr(struct mpage *mpage, const struct mdoc_node *n) { char *cp; if (NULL == (n = n->child)) return(0); if (NULL == n->next) { putkey(mpage, n->string, TYPE_Xr); return(0); } mandoc_asprintf(&cp, "%s(%s)", n->string, n->next->string); putkey(mpage, cp, TYPE_Xr); free(cp); return(0); } static int parse_mdoc_Nd(struct mpage *mpage, const struct mdoc_node *n) { if (MDOC_BODY == n->type) mdoc_deroff(&mpage->desc, n); return(0); } static int parse_mdoc_Nm(struct mpage *mpage, const struct mdoc_node *n) { if (SEC_NAME == n->sec) putmdockey(mpage, n->child, NAME_TITLE); else if (SEC_SYNOPSIS == n->sec && MDOC_HEAD == n->type) putmdockey(mpage, n->child, NAME_SYN); return(0); } static int parse_mdoc_Sh(struct mpage *mpage, const struct mdoc_node *n) { return(SEC_CUSTOM == n->sec && MDOC_HEAD == n->type); } static int parse_mdoc_head(struct mpage *mpage, const struct mdoc_node *n) { return(MDOC_HEAD == n->type); } static int parse_mdoc_body(struct mpage *mpage, const struct mdoc_node *n) { return(MDOC_BODY == n->type); } /* * Add a string to the hash table for the current manual. * Each string has a bitmask telling which macros it belongs to. * When we finish the manual, we'll dump the table. */ static void putkeys(const struct mpage *mpage, const char *cp, size_t sz, uint64_t v) { struct ohash *htab; struct str *s; const char *end; unsigned int slot; int i; if (0 == sz) return; if (TYPE_Nm & v) { htab = &names; v &= name_mask; - name_mask &= ~NAME_FIRST; + if (v & NAME_FIRST) + name_mask &= ~NAME_FIRST; if (debug > 1) say(mpage->mlinks->file, "Adding name %*s", sz, cp); } else { htab = &strings; if (debug > 1) for (i = 0; i < mansearch_keymax; i++) - if (1 << i & v) + if ((uint64_t)1 << i & v) say(mpage->mlinks->file, "Adding key %s=%*s", mansearch_keynames[i], sz, cp); } end = cp + sz; slot = ohash_qlookupi(htab, cp, &end); s = ohash_find(htab, slot); if (NULL != s && mpage == s->mpage) { s->mask |= v; return; } else if (NULL == s) { s = mandoc_calloc(1, sizeof(struct str) + sz + 1); memcpy(s->key, cp, sz); ohash_insert(htab, slot, s); } s->mpage = mpage; s->mask = v; } /* * Take a Unicode codepoint and produce its UTF-8 encoding. * This isn't the best way to do this, but it works. * The magic numbers are from the UTF-8 packaging. * They're not as scary as they seem: read the UTF-8 spec for details. */ static size_t utf8(unsigned int cp, char out[7]) { size_t rc; rc = 0; if (cp <= 0x0000007F) { rc = 1; out[0] = (char)cp; } else if (cp <= 0x000007FF) { rc = 2; out[0] = (cp >> 6 & 31) | 192; out[1] = (cp & 63) | 128; } else if (cp <= 0x0000FFFF) { rc = 3; out[0] = (cp >> 12 & 15) | 224; out[1] = (cp >> 6 & 63) | 128; out[2] = (cp & 63) | 128; } else if (cp <= 0x001FFFFF) { rc = 4; out[0] = (cp >> 18 & 7) | 240; out[1] = (cp >> 12 & 63) | 128; out[2] = (cp >> 6 & 63) | 128; out[3] = (cp & 63) | 128; } else if (cp <= 0x03FFFFFF) { rc = 5; out[0] = (cp >> 24 & 3) | 248; out[1] = (cp >> 18 & 63) | 128; out[2] = (cp >> 12 & 63) | 128; out[3] = (cp >> 6 & 63) | 128; out[4] = (cp & 63) | 128; } else if (cp <= 0x7FFFFFFF) { rc = 6; out[0] = (cp >> 30 & 1) | 252; out[1] = (cp >> 24 & 63) | 128; out[2] = (cp >> 18 & 63) | 128; out[3] = (cp >> 12 & 63) | 128; out[4] = (cp >> 6 & 63) | 128; out[5] = (cp & 63) | 128; } else return(0); out[rc] = '\0'; return(rc); } /* * Store the rendered version of a key, or alias the pointer * if the key contains no escape sequences. */ static void render_key(struct mchars *mc, struct str *key) { size_t sz, bsz, pos; char utfbuf[7], res[6]; char *buf; const char *seq, *cpp, *val; int len, u; enum mandoc_esc esc; assert(NULL == key->rendered); res[0] = '\\'; res[1] = '\t'; res[2] = ASCII_NBRSP; res[3] = ASCII_HYPH; res[4] = ASCII_BREAK; res[5] = '\0'; val = key->key; bsz = strlen(val); /* * Pre-check: if we have no stop-characters, then set the * pointer as ourselvse and get out of here. */ if (strcspn(val, res) == bsz) { key->rendered = key->key; return; } /* Pre-allocate by the length of the input */ buf = mandoc_malloc(++bsz); pos = 0; while ('\0' != *val) { /* * Halt on the first escape sequence. * This also halts on the end of string, in which case * we just copy, fallthrough, and exit the loop. */ if ((sz = strcspn(val, res)) > 0) { memcpy(&buf[pos], val, sz); pos += sz; val += sz; } switch (*val) { case ASCII_HYPH: buf[pos++] = '-'; val++; continue; case '\t': /* FALLTHROUGH */ case ASCII_NBRSP: buf[pos++] = ' '; val++; /* FALLTHROUGH */ case ASCII_BREAK: continue; default: break; } if ('\\' != *val) break; /* Read past the slash. */ val++; /* * Parse the escape sequence and see if it's a * predefined character or special character. */ esc = mandoc_escape((const char **)&val, &seq, &len); if (ESCAPE_ERROR == esc) break; if (ESCAPE_SPECIAL != esc) continue; /* * Render the special character * as either UTF-8 or ASCII. */ if (write_utf8) { - if (0 == (u = mchars_spec2cp(mc, seq, len))) + if ((u = mchars_spec2cp(mc, seq, len)) <= 0) continue; cpp = utfbuf; if (0 == (sz = utf8(u, utfbuf))) continue; sz = strlen(cpp); } else { cpp = mchars_spec2str(mc, seq, len, &sz); if (NULL == cpp) continue; if (ASCII_NBRSP == *cpp) { cpp = " "; sz = 1; } } /* Copy the rendered glyph into the stream. */ bsz += sz; buf = mandoc_realloc(buf, bsz); memcpy(&buf[pos], cpp, sz); pos += sz; } buf[pos] = '\0'; key->rendered = buf; } static void dbadd_mlink(const struct mlink *mlink) { size_t i; i = 1; SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->dsec); SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->arch); SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->name); SQL_BIND_INT64(stmts[STMT_INSERT_LINK], i, mlink->mpage->pageid); SQL_STEP(stmts[STMT_INSERT_LINK]); sqlite3_reset(stmts[STMT_INSERT_LINK]); } +static void +dbadd_mlink_name(const struct mlink *mlink) +{ + size_t i; + + dbadd_mlink(mlink); + + i = 1; + SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, NAME_FILE & NAME_MASK); + SQL_BIND_TEXT(stmts[STMT_INSERT_NAME], i, mlink->name); + SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, mlink->mpage->pageid); + SQL_STEP(stmts[STMT_INSERT_NAME]); + sqlite3_reset(stmts[STMT_INSERT_NAME]); +} + /* * Flush the current page's terms (and their bits) into the database. * Wrap the entire set of additions in a transaction to make sqlite be a * little faster. * Also, handle escape sequences at the last possible moment. */ static void dbadd(struct mpage *mpage, struct mchars *mc) { struct mlink *mlink; struct str *key; size_t i; unsigned int slot; mlink = mpage->mlinks; if (nodb) { for (key = ohash_first(&names, &slot); NULL != key; key = ohash_next(&names, &slot)) { if (key->rendered != key->key) free(key->rendered); free(key); } for (key = ohash_first(&strings, &slot); NULL != key; key = ohash_next(&strings, &slot)) { if (key->rendered != key->key) free(key->rendered); free(key); } if (0 == debug) return; while (NULL != mlink) { fputs(mlink->name, stdout); if (NULL == mlink->next || strcmp(mlink->dsec, mlink->next->dsec) || strcmp(mlink->fsec, mlink->next->fsec) || strcmp(mlink->arch, mlink->next->arch)) { putchar('('); if ('\0' == *mlink->dsec) fputs(mlink->fsec, stdout); else fputs(mlink->dsec, stdout); if ('\0' != *mlink->arch) printf("/%s", mlink->arch); putchar(')'); } mlink = mlink->next; if (NULL != mlink) fputs(", ", stdout); } printf(" - %s\n", mpage->desc); return; } if (debug) say(mlink->file, "Adding to database"); i = strlen(mpage->desc) + 1; key = mandoc_calloc(1, sizeof(struct str) + i); memcpy(key->key, mpage->desc, i); render_key(mc, key); i = 1; SQL_BIND_TEXT(stmts[STMT_INSERT_PAGE], i, key->rendered); - SQL_BIND_INT(stmts[STMT_INSERT_PAGE], i, FORM_SRC == mpage->form); + SQL_BIND_INT(stmts[STMT_INSERT_PAGE], i, mpage->form); SQL_STEP(stmts[STMT_INSERT_PAGE]); mpage->pageid = sqlite3_last_insert_rowid(db); sqlite3_reset(stmts[STMT_INSERT_PAGE]); if (key->rendered != key->key) free(key->rendered); free(key); while (NULL != mlink) { dbadd_mlink(mlink); mlink = mlink->next; } mlink = mpage->mlinks; for (key = ohash_first(&names, &slot); NULL != key; key = ohash_next(&names, &slot)) { assert(key->mpage == mpage); if (NULL == key->rendered) render_key(mc, key); i = 1; SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, key->mask); SQL_BIND_TEXT(stmts[STMT_INSERT_NAME], i, key->rendered); SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, mpage->pageid); SQL_STEP(stmts[STMT_INSERT_NAME]); sqlite3_reset(stmts[STMT_INSERT_NAME]); if (key->rendered != key->key) free(key->rendered); free(key); } for (key = ohash_first(&strings, &slot); NULL != key; key = ohash_next(&strings, &slot)) { assert(key->mpage == mpage); if (NULL == key->rendered) render_key(mc, key); i = 1; SQL_BIND_INT64(stmts[STMT_INSERT_KEY], i, key->mask); SQL_BIND_TEXT(stmts[STMT_INSERT_KEY], i, key->rendered); SQL_BIND_INT64(stmts[STMT_INSERT_KEY], i, mpage->pageid); SQL_STEP(stmts[STMT_INSERT_KEY]); sqlite3_reset(stmts[STMT_INSERT_KEY]); if (key->rendered != key->key) free(key->rendered); free(key); } } static void dbprune(void) { struct mpage *mpage; struct mlink *mlink; size_t i; unsigned int slot; if (0 == nodb) SQL_EXEC("BEGIN TRANSACTION"); for (mpage = ohash_first(&mpages, &slot); NULL != mpage; mpage = ohash_next(&mpages, &slot)) { mlink = mpage->mlinks; if (debug) say(mlink->file, "Deleting from database"); if (nodb) continue; for ( ; NULL != mlink; mlink = mlink->next) { i = 1; SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE], i, mlink->dsec); SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE], i, mlink->arch); SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE], i, mlink->name); SQL_STEP(stmts[STMT_DELETE_PAGE]); sqlite3_reset(stmts[STMT_DELETE_PAGE]); } } if (0 == nodb) SQL_EXEC("END TRANSACTION"); } /* * Close an existing database and its prepared statements. * If "real" is not set, rename the temporary file into the real one. */ static void dbclose(int real) { size_t i; int status; pid_t child; if (nodb) return; for (i = 0; i < STMT__MAX; i++) { sqlite3_finalize(stmts[i]); stmts[i] = NULL; } sqlite3_close(db); db = NULL; if (real) return; if ('\0' == *tempfilename) { if (-1 == rename(MANDOC_DB "~", MANDOC_DB)) { exitcode = (int)MANDOCLEVEL_SYSERR; say(MANDOC_DB, "&rename"); } return; } switch (child = fork()) { case -1: exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&fork cmp"); return; case 0: execlp("cmp", "cmp", "-s", tempfilename, MANDOC_DB, NULL); say("", "&exec cmp"); exit(0); default: break; } if (-1 == waitpid(child, &status, 0)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&wait cmp"); } else if (WIFSIGNALED(status)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "cmp died from signal %d", WTERMSIG(status)); } else if (WEXITSTATUS(status)) { exitcode = (int)MANDOCLEVEL_SYSERR; say(MANDOC_DB, "Data changed, but cannot replace database"); } *strrchr(tempfilename, '/') = '\0'; switch (child = fork()) { case -1: exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&fork rm"); return; case 0: execlp("rm", "rm", "-rf", tempfilename, NULL); say("", "&exec rm"); exit((int)MANDOCLEVEL_SYSERR); default: break; } if (-1 == waitpid(child, &status, 0)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&wait rm"); } else if (WIFSIGNALED(status) || WEXITSTATUS(status)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "%s: Cannot remove temporary directory", tempfilename); } } /* * This is straightforward stuff. * Open a database connection to a "temporary" database, then open a set * of prepared statements we'll use over and over again. * If "real" is set, we use the existing database; if not, we truncate a * temporary one. * Must be matched by dbclose(). */ static int dbopen(int real) { const char *sql; int rc, ofl; if (nodb) return(1); *tempfilename = '\0'; ofl = SQLITE_OPEN_READWRITE; if (real) { rc = sqlite3_open_v2(MANDOC_DB, &db, ofl, NULL); if (SQLITE_OK != rc) { exitcode = (int)MANDOCLEVEL_SYSERR; if (SQLITE_CANTOPEN != rc) say(MANDOC_DB, "%s", sqlite3_errstr(rc)); return(0); } goto prepare_statements; } ofl |= SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE; remove(MANDOC_DB "~"); rc = sqlite3_open_v2(MANDOC_DB "~", &db, ofl, NULL); if (SQLITE_OK == rc) goto create_tables; if (MPARSE_QUICK & mparse_options) { exitcode = (int)MANDOCLEVEL_SYSERR; say(MANDOC_DB "~", "%s", sqlite3_errstr(rc)); return(0); } (void)strlcpy(tempfilename, "/tmp/mandocdb.XXXXXX", sizeof(tempfilename)); if (NULL == mkdtemp(tempfilename)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&%s", tempfilename); return(0); } (void)strlcat(tempfilename, "/" MANDOC_DB, sizeof(tempfilename)); rc = sqlite3_open_v2(tempfilename, &db, ofl, NULL); if (SQLITE_OK != rc) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "%s: %s", tempfilename, sqlite3_errstr(rc)); return(0); } create_tables: sql = "CREATE TABLE \"mpages\" (\n" " \"desc\" TEXT NOT NULL,\n" " \"form\" INTEGER NOT NULL,\n" " \"pageid\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL\n" ");\n" "\n" "CREATE TABLE \"mlinks\" (\n" " \"sec\" TEXT NOT NULL,\n" " \"arch\" TEXT NOT NULL,\n" " \"name\" TEXT NOT NULL,\n" " \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) " "ON DELETE CASCADE\n" ");\n" "CREATE INDEX mlinks_pageid_idx ON mlinks (pageid);\n" "\n" "CREATE TABLE \"names\" (\n" " \"bits\" INTEGER NOT NULL,\n" " \"name\" TEXT NOT NULL,\n" " \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) " "ON DELETE CASCADE\n" ");\n" "\n" "CREATE TABLE \"keys\" (\n" " \"bits\" INTEGER NOT NULL,\n" " \"key\" TEXT NOT NULL,\n" " \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) " "ON DELETE CASCADE\n" ");\n" "CREATE INDEX keys_pageid_idx ON keys (pageid);\n"; if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, NULL)) { exitcode = (int)MANDOCLEVEL_SYSERR; say(MANDOC_DB, "%s", sqlite3_errmsg(db)); sqlite3_close(db); return(0); } prepare_statements: if (SQLITE_OK != sqlite3_exec(db, "PRAGMA foreign_keys = ON", NULL, NULL, NULL)) { exitcode = (int)MANDOCLEVEL_SYSERR; say(MANDOC_DB, "PRAGMA foreign_keys: %s", sqlite3_errmsg(db)); sqlite3_close(db); return(0); } sql = "DELETE FROM mpages WHERE pageid IN " "(SELECT pageid FROM mlinks WHERE " "sec=? AND arch=? AND name=?)"; sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_DELETE_PAGE], NULL); sql = "INSERT INTO mpages " "(desc,form) VALUES (?,?)"; sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_PAGE], NULL); sql = "INSERT INTO mlinks " "(sec,arch,name,pageid) VALUES (?,?,?,?)"; sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_LINK], NULL); sql = "INSERT INTO names " "(bits,name,pageid) VALUES (?,?,?)"; sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_NAME], NULL); sql = "INSERT INTO keys " "(bits,key,pageid) VALUES (?,?,?)"; sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_KEY], NULL); #ifndef __APPLE__ /* * When opening a new database, we can turn off * synchronous mode for much better performance. */ if (real && SQLITE_OK != sqlite3_exec(db, "PRAGMA synchronous = OFF", NULL, NULL, NULL)) { exitcode = (int)MANDOCLEVEL_SYSERR; say(MANDOC_DB, "PRAGMA synchronous: %s", - sqlite3_errmsg(db)); + sqlite3_errmsg(db)); sqlite3_close(db); return(0); } #endif return(1); } static void * hash_calloc(size_t n, size_t sz, void *arg) { return(mandoc_calloc(n, sz)); } static void * hash_alloc(size_t sz, void *arg) { return(mandoc_malloc(sz)); } static void hash_free(void *p, void *arg) { free(p); } static int -set_basedir(const char *targetdir) +set_basedir(const char *targetdir, int report_baddir) { static char startdir[PATH_MAX]; static int getcwd_status; /* 1 = ok, 2 = failure */ static int chdir_status; /* 1 = changed directory */ char *cp; /* * Remember the original working directory, if possible. * This will be needed if the second or a later directory * on the command line is given as a relative path. * Do not error out if the current directory is not * searchable: Maybe it won't be needed after all. */ if (0 == getcwd_status) { if (NULL == getcwd(startdir, sizeof(startdir))) { getcwd_status = 2; (void)strlcpy(startdir, strerror(errno), sizeof(startdir)); } else getcwd_status = 1; } /* * We are leaving the old base directory. * Do not use it any longer, not even for messages. */ *basedir = '\0'; /* * If and only if the directory was changed earlier and * the next directory to process is given as a relative path, * first go back, or bail out if that is impossible. */ if (chdir_status && '/' != *targetdir) { if (2 == getcwd_status) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "getcwd: %s", startdir); return(0); } if (-1 == chdir(startdir)) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "&chdir %s", startdir); return(0); } } /* * Always resolve basedir to the canonicalized absolute * pathname and append a trailing slash, such that * we can reliably check whether files are inside. */ if (NULL == realpath(targetdir, basedir)) { - exitcode = (int)MANDOCLEVEL_BADARG; - say("", "&%s: realpath", targetdir); + if (report_baddir || errno != ENOENT) { + exitcode = (int)MANDOCLEVEL_BADARG; + say("", "&%s: realpath", targetdir); + } return(0); } else if (-1 == chdir(basedir)) { - exitcode = (int)MANDOCLEVEL_BADARG; - say("", "&chdir"); + if (report_baddir || errno != ENOENT) { + exitcode = (int)MANDOCLEVEL_BADARG; + say("", "&chdir"); + } return(0); } chdir_status = 1; cp = strchr(basedir, '\0'); if ('/' != cp[-1]) { if (cp - basedir >= PATH_MAX - 1) { exitcode = (int)MANDOCLEVEL_SYSERR; say("", "Filename too long"); return(0); } *cp++ = '/'; *cp = '\0'; } return(1); } static void say(const char *file, const char *format, ...) { va_list ap; int use_errno; if ('\0' != *basedir) fprintf(stderr, "%s", basedir); if ('\0' != *basedir && '\0' != *file) fputc('/', stderr); if ('\0' != *file) fprintf(stderr, "%s", file); use_errno = 1; if (NULL != format) { switch (*format) { case '&': format++; break; case '\0': format = NULL; break; default: use_errno = 0; break; } } if (NULL != format) { if ('\0' != *basedir || '\0' != *file) fputs(": ", stderr); va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); } if (use_errno) { if ('\0' != *basedir || '\0' != *file || NULL != format) fputs(": ", stderr); perror(NULL); } else fputc('\n', stderr); } Index: vendor/mdocml/dist/manpage.c =================================================================== --- vendor/mdocml/dist/manpage.c (revision 275396) +++ vendor/mdocml/dist/manpage.c (revision 275397) @@ -1,190 +1,191 @@ -/* $Id: manpage.c,v 1.7 2014/01/06 03:02:46 schwarze Exp $ */ +/* $Id: manpage.c,v 1.9 2014/08/17 03:24:47 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons * Copyright (c) 2013 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include + #include #include #include #include #include #include #include #include #include "manpath.h" #include "mansearch.h" static void show(const char *, const char *); int main(int argc, char *argv[]) { int ch, term; size_t i, sz, len; struct mansearch search; struct manpage *res; char *conf_file, *defpaths, *auxpaths, *cp; char buf[PATH_MAX]; const char *cmd; struct manpaths paths; char *progname; extern char *optarg; extern int optind; term = isatty(STDIN_FILENO) && isatty(STDOUT_FILENO); progname = strrchr(argv[0], '/'); if (progname == NULL) progname = argv[0]; else ++progname; auxpaths = defpaths = conf_file = NULL; memset(&paths, 0, sizeof(struct manpaths)); memset(&search, 0, sizeof(struct mansearch)); while (-1 != (ch = getopt(argc, argv, "C:M:m:S:s:"))) switch (ch) { case ('C'): conf_file = optarg; break; case ('M'): defpaths = optarg; break; case ('m'): auxpaths = optarg; break; case ('S'): search.arch = optarg; break; case ('s'): search.sec = optarg; break; default: goto usage; } argc -= optind; argv += optind; if (0 == argc) goto usage; - search.deftype = TYPE_Nm | TYPE_Nd; + search.outkey = "Nd"; + search.argmode = ARG_EXPR; manpath_parse(&paths, conf_file, defpaths, auxpaths); - ch = mansearch(&search, &paths, argc, argv, "Nd", &res, &sz); + ch = mansearch(&search, &paths, argc, argv, &res, &sz); manpath_free(&paths); if (0 == ch) goto usage; if (0 == sz) { free(res); return(EXIT_FAILURE); } else if (1 == sz && term) { i = 1; goto show; } else if (NULL == res) return(EXIT_FAILURE); for (i = 0; i < sz; i++) { printf("%6zu %s: %s\n", i + 1, res[i].names, res[i].output); free(res[i].names); free(res[i].output); } if (0 == term) { for (i = 0; i < sz; i++) free(res[i].file); free(res); return(EXIT_SUCCESS); } i = 1; printf("Enter a choice [1]: "); fflush(stdout); if (NULL != (cp = fgetln(stdin, &len))) if ('\n' == cp[--len] && len > 0) { cp[len] = '\0'; if ((i = atoi(cp)) < 1 || i > sz) i = 0; } if (0 == i) { for (i = 0; i < sz; i++) free(res[i].file); free(res); return(EXIT_SUCCESS); } show: cmd = res[i - 1].form ? "mandoc" : "cat"; strlcpy(buf, res[i - 1].file, PATH_MAX); for (i = 0; i < sz; i++) free(res[i].file); free(res); show(cmd, buf); /* NOTREACHED */ usage: fprintf(stderr, "usage: %s [-C conf] " "[-M paths] " "[-m paths] " "[-S arch] " "[-s section] " "expr ...\n", progname); return(EXIT_FAILURE); } static void show(const char *cmd, const char *file) { int fds[2]; pid_t pid; if (-1 == pipe(fds)) { perror(NULL); exit(EXIT_FAILURE); } if (-1 == (pid = fork())) { perror(NULL); exit(EXIT_FAILURE); } else if (pid > 0) { dup2(fds[0], STDIN_FILENO); close(fds[1]); cmd = NULL != getenv("MANPAGER") ? getenv("MANPAGER") : (NULL != getenv("PAGER") ? getenv("PAGER") : "more"); execlp(cmd, cmd, (char *)NULL); perror(cmd); exit(EXIT_FAILURE); } dup2(fds[1], STDOUT_FILENO); close(fds[0]); execlp(cmd, cmd, file, (char *)NULL); perror(cmd); exit(EXIT_FAILURE); } Index: vendor/mdocml/dist/manpath.c =================================================================== --- vendor/mdocml/dist/manpath.c (revision 275396) +++ vendor/mdocml/dist/manpath.c (revision 275397) @@ -1,222 +1,237 @@ -/* $Id: manpath.c,v 1.15 2014/04/23 21:06:41 schwarze Exp $ */ +/* $Id: manpath.c,v 1.19 2014/11/27 00:30:40 schwarze Exp $ */ /* - * Copyright (c) 2011 Ingo Schwarze + * Copyright (c) 2011, 2014 Ingo Schwarze * Copyright (c) 2011 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif +#include +#include + #include #include #include #include #include #include #include "mandoc_aux.h" #include "manpath.h" #define MAN_CONF_FILE "/etc/man.conf" #define MAN_CONF_KEY "_whatdb" -static void manpath_add(struct manpaths *, const char *); -static void manpath_parseline(struct manpaths *, char *); +static void manpath_add(struct manpaths *, const char *, int); +static void manpath_parseline(struct manpaths *, char *, int); void manpath_parse(struct manpaths *dirs, const char *file, char *defp, char *auxp) { -#ifdef USE_MANPATH +#if HAVE_MANPATH char cmd[(PATH_MAX * 3) + 20]; FILE *stream; char *buf; size_t sz, bsz; strlcpy(cmd, "manpath", sizeof(cmd)); if (file) { strlcat(cmd, " -C ", sizeof(cmd)); strlcat(cmd, file, sizeof(cmd)); } if (auxp) { strlcat(cmd, " -m ", sizeof(cmd)); strlcat(cmd, auxp, sizeof(cmd)); } if (defp) { strlcat(cmd, " -M ", sizeof(cmd)); strlcat(cmd, defp, sizeof(cmd)); } /* Open manpath(1). Ignore errors. */ stream = popen(cmd, "r"); if (NULL == stream) return; buf = NULL; bsz = 0; /* Read in as much output as we can. */ do { buf = mandoc_realloc(buf, bsz + 1024); sz = fread(buf + bsz, 1, 1024, stream); bsz += sz; } while (sz > 0); if ( ! ferror(stream) && feof(stream) && bsz && '\n' == buf[bsz - 1]) { buf[bsz - 1] = '\0'; - manpath_parseline(dirs, buf); + manpath_parseline(dirs, buf, 1); } free(buf); pclose(stream); #else char *insert; /* Always prepend -m. */ - manpath_parseline(dirs, auxp); + manpath_parseline(dirs, auxp, 1); /* If -M is given, it overrides everything else. */ if (NULL != defp) { - manpath_parseline(dirs, defp); + manpath_parseline(dirs, defp, 1); return; } /* MANPATH and man.conf(5) cooperate. */ defp = getenv("MANPATH"); if (NULL == file) file = MAN_CONF_FILE; /* No MANPATH; use man.conf(5) only. */ if (NULL == defp || '\0' == defp[0]) { manpath_manconf(dirs, file); return; } /* Prepend man.conf(5) to MANPATH. */ if (':' == defp[0]) { manpath_manconf(dirs, file); - manpath_parseline(dirs, defp); + manpath_parseline(dirs, defp, 0); return; } /* Append man.conf(5) to MANPATH. */ if (':' == defp[strlen(defp) - 1]) { - manpath_parseline(dirs, defp); + manpath_parseline(dirs, defp, 0); manpath_manconf(dirs, file); return; } /* Insert man.conf(5) into MANPATH. */ insert = strstr(defp, "::"); if (NULL != insert) { *insert++ = '\0'; - manpath_parseline(dirs, defp); + manpath_parseline(dirs, defp, 0); manpath_manconf(dirs, file); - manpath_parseline(dirs, insert + 1); + manpath_parseline(dirs, insert + 1, 0); return; } /* MANPATH overrides man.conf(5) completely. */ - manpath_parseline(dirs, defp); + manpath_parseline(dirs, defp, 0); #endif } /* * Parse a FULL pathname from a colon-separated list of arrays. */ static void -manpath_parseline(struct manpaths *dirs, char *path) +manpath_parseline(struct manpaths *dirs, char *path, int complain) { char *dir; if (NULL == path) return; for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) - manpath_add(dirs, dir); + manpath_add(dirs, dir, complain); } /* * Add a directory to the array, ignoring bad directories. * Grow the array one-by-one for simplicity's sake. */ static void -manpath_add(struct manpaths *dirs, const char *dir) +manpath_add(struct manpaths *dirs, const char *dir, int complain) { char buf[PATH_MAX]; + struct stat sb; char *cp; size_t i; - if (NULL == (cp = realpath(dir, buf))) + if (NULL == (cp = realpath(dir, buf))) { + if (complain) { + fputs("manpath: ", stderr); + perror(dir); + } return; + } for (i = 0; i < dirs->sz; i++) if (0 == strcmp(dirs->paths[i], dir)) return; + if (stat(cp, &sb) == -1) { + if (complain) { + fputs("manpath: ", stderr); + perror(dir); + } + return; + } + dirs->paths = mandoc_reallocarray(dirs->paths, dirs->sz + 1, sizeof(char *)); dirs->paths[dirs->sz++] = mandoc_strdup(cp); } void manpath_free(struct manpaths *p) { size_t i; for (i = 0; i < p->sz; i++) free(p->paths[i]); free(p->paths); } void manpath_manconf(struct manpaths *dirs, const char *file) { FILE *stream; char *p, *q; size_t len, keysz; keysz = strlen(MAN_CONF_KEY); assert(keysz > 0); if (NULL == (stream = fopen(file, "r"))) return; while (NULL != (p = fgetln(stream, &len))) { if (0 == len || '\n' != p[--len]) break; p[len] = '\0'; while (isspace((unsigned char)*p)) p++; if (strncmp(MAN_CONF_KEY, p, keysz)) continue; p += keysz; while (isspace((unsigned char)*p)) p++; if ('\0' == *p) continue; if (NULL == (q = strrchr(p, '/'))) continue; *q = '\0'; - manpath_add(dirs, p); + manpath_add(dirs, p, 0); } fclose(stream); } Index: vendor/mdocml/dist/mansearch.c =================================================================== --- vendor/mdocml/dist/mansearch.c (revision 275396) +++ vendor/mdocml/dist/mansearch.c (revision 275397) @@ -1,861 +1,877 @@ -/* $Id: mansearch.c,v 1.42 2014/08/09 14:24:53 schwarze Exp $ */ +/* $Id: mansearch.c,v 1.51 2014/11/27 01:58:21 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons * Copyright (c) 2013, 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include +#include + #include #include #include #include #include #include #include #include #include #include #include -#ifdef HAVE_OHASH +#if HAVE_OHASH #include #else #include "compat_ohash.h" #endif #include #ifndef SQLITE_DETERMINISTIC #define SQLITE_DETERMINISTIC 0 #endif #include "mandoc.h" #include "mandoc_aux.h" #include "manpath.h" #include "mansearch.h" extern int mansearch_keymax; extern const char *const mansearch_keynames[]; #define SQL_BIND_TEXT(_db, _s, _i, _v) \ do { if (SQLITE_OK != sqlite3_bind_text \ ((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \ fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ } while (0) #define SQL_BIND_INT64(_db, _s, _i, _v) \ do { if (SQLITE_OK != sqlite3_bind_int64 \ ((_s), (_i)++, (_v))) \ fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ } while (0) #define SQL_BIND_BLOB(_db, _s, _i, _v) \ do { if (SQLITE_OK != sqlite3_bind_blob \ ((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \ fprintf(stderr, "%s\n", sqlite3_errmsg((_db))); \ } while (0) struct expr { regex_t regexp; /* compiled regexp, if applicable */ const char *substr; /* to search for, if applicable */ struct expr *next; /* next in sequence */ uint64_t bits; /* type-mask */ int equal; /* equality, not subsring match */ int open; /* opening parentheses before */ int and; /* logical AND before */ int close; /* closing parentheses after */ }; struct match { uint64_t pageid; /* identifier in database */ + uint64_t bits; /* name type mask */ char *desc; /* manual page description */ - int form; /* 0 == catpage */ + int form; /* bit field: formatted, zipped? */ }; static void buildnames(struct manpage *, sqlite3 *, sqlite3_stmt *, uint64_t, const char *, int form); static char *buildoutput(sqlite3 *, sqlite3_stmt *, uint64_t, uint64_t); static void *hash_alloc(size_t, void *); static void hash_free(void *, void *); static void *hash_calloc(size_t, size_t, void *); static struct expr *exprcomp(const struct mansearch *, int, char *[]); static void exprfree(struct expr *); static struct expr *exprspec(struct expr *, uint64_t, const char *, const char *); static struct expr *exprterm(const struct mansearch *, char *, int); static int manpage_compare(const void *, const void *); static void sql_append(char **sql, size_t *sz, const char *newstr, int count); static void sql_match(sqlite3_context *context, int argc, sqlite3_value **argv); static void sql_regexp(sqlite3_context *context, int argc, sqlite3_value **argv); static char *sql_statement(const struct expr *); int mansearch_setup(int start) { static void *pagecache; int c; #define PC_PAGESIZE 1280 #define PC_NUMPAGES 256 if (start) { if (NULL != pagecache) { fprintf(stderr, "pagecache already enabled\n"); return((int)MANDOCLEVEL_BADARG); } pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); if (MAP_FAILED == pagecache) { perror("mmap"); pagecache = NULL; return((int)MANDOCLEVEL_SYSERR); } c = sqlite3_config(SQLITE_CONFIG_PAGECACHE, pagecache, PC_PAGESIZE, PC_NUMPAGES); if (SQLITE_OK == c) return((int)MANDOCLEVEL_OK); fprintf(stderr, "pagecache: %s\n", sqlite3_errstr(c)); } else if (NULL == pagecache) { fprintf(stderr, "pagecache missing\n"); return((int)MANDOCLEVEL_BADARG); } if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) { perror("munmap"); pagecache = NULL; return((int)MANDOCLEVEL_SYSERR); } pagecache = NULL; return((int)MANDOCLEVEL_OK); } int mansearch(const struct mansearch *search, const struct manpaths *paths, int argc, char *argv[], - const char *outkey, struct manpage **res, size_t *sz) { int fd, rc, c, indexbit; int64_t pageid; uint64_t outbit, iterbit; char buf[PATH_MAX]; char *sql; struct manpage *mpage; struct expr *e, *ep; sqlite3 *db; sqlite3_stmt *s, *s2; struct match *mp; struct ohash_info info; struct ohash htab; unsigned int idx; size_t i, j, cur, maxres; info.calloc = hash_calloc; info.alloc = hash_alloc; info.free = hash_free; info.key_offset = offsetof(struct match, pageid); *sz = cur = maxres = 0; sql = NULL; *res = NULL; fd = -1; e = NULL; rc = 0; if (0 == argc) goto out; if (NULL == (e = exprcomp(search, argc, argv))) goto out; outbit = 0; - if (NULL != outkey) { + if (NULL != search->outkey) { for (indexbit = 0, iterbit = 1; indexbit < mansearch_keymax; indexbit++, iterbit <<= 1) { - if (0 == strcasecmp(outkey, + if (0 == strcasecmp(search->outkey, mansearch_keynames[indexbit])) { outbit = iterbit; break; } } } /* * Save a descriptor to the current working directory. * Since pathnames in the "paths" variable might be relative, * and we'll be chdir()ing into them, we need to keep a handle * on our current directory from which to start the chdir(). */ if (NULL == getcwd(buf, PATH_MAX)) { perror("getcwd"); goto out; } else if (-1 == (fd = open(buf, O_RDONLY, 0))) { perror(buf); goto out; } sql = sql_statement(e); /* * Loop over the directories (containing databases) for us to * search. * Don't let missing/bad databases/directories phase us. * In each, try to open the resident database and, if it opens, * scan it for our match expression. */ for (i = 0; i < paths->sz; i++) { if (-1 == fchdir(fd)) { perror(buf); free(*res); break; } else if (-1 == chdir(paths->paths[i])) { perror(paths->paths[i]); continue; } c = sqlite3_open_v2(MANDOC_DB, &db, SQLITE_OPEN_READONLY, NULL); if (SQLITE_OK != c) { perror(MANDOC_DB); sqlite3_close(db); continue; } /* * Define the SQL functions for substring * and regular expression matching. */ c = sqlite3_create_function(db, "match", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_match, NULL, NULL); assert(SQLITE_OK == c); c = sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8 | SQLITE_DETERMINISTIC, NULL, sql_regexp, NULL, NULL); assert(SQLITE_OK == c); j = 1; c = sqlite3_prepare_v2(db, sql, -1, &s, NULL); if (SQLITE_OK != c) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); for (ep = e; NULL != ep; ep = ep->next) { if (NULL == ep->substr) { SQL_BIND_BLOB(db, s, j, ep->regexp); } else SQL_BIND_TEXT(db, s, j, ep->substr); if (0 == ((TYPE_Nd | TYPE_Nm) & ep->bits)) SQL_BIND_INT64(db, s, j, ep->bits); } memset(&htab, 0, sizeof(struct ohash)); ohash_init(&htab, 4, &info); /* * Hash each entry on its [unique] document identifier. * This is a uint64_t. * Instead of using a hash function, simply convert the * uint64_t to a uint32_t, the hash value's type. * This gives good performance and preserves the * distribution of buckets in the table. */ while (SQLITE_ROW == (c = sqlite3_step(s))) { pageid = sqlite3_column_int64(s, 2); idx = ohash_lookup_memory(&htab, (char *)&pageid, sizeof(uint64_t), (uint32_t)pageid); if (NULL != ohash_find(&htab, idx)) continue; mp = mandoc_calloc(1, sizeof(struct match)); mp->pageid = pageid; mp->form = sqlite3_column_int(s, 1); + mp->bits = sqlite3_column_int64(s, 3); if (TYPE_Nd == outbit) mp->desc = mandoc_strdup((const char *) sqlite3_column_text(s, 0)); ohash_insert(&htab, idx, mp); } if (SQLITE_DONE != c) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); sqlite3_finalize(s); c = sqlite3_prepare_v2(db, "SELECT sec, arch, name, pageid FROM mlinks " "WHERE pageid=? ORDER BY sec, arch, name", -1, &s, NULL); if (SQLITE_OK != c) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); c = sqlite3_prepare_v2(db, "SELECT bits, key, pageid FROM keys " "WHERE pageid=? AND bits & ?", -1, &s2, NULL); if (SQLITE_OK != c) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); for (mp = ohash_first(&htab, &idx); NULL != mp; mp = ohash_next(&htab, &idx)) { if (cur + 1 > maxres) { maxres += 1024; *res = mandoc_reallocarray(*res, maxres, sizeof(struct manpage)); } mpage = *res + cur; + mpage->ipath = i; + mpage->bits = mp->bits; mpage->sec = 10; mpage->form = mp->form; buildnames(mpage, db, s, mp->pageid, paths->paths[i], mp->form); mpage->output = TYPE_Nd & outbit ? mp->desc : outbit ? buildoutput(db, s2, mp->pageid, outbit) : NULL; free(mp); cur++; } sqlite3_finalize(s); sqlite3_finalize(s2); sqlite3_close(db); ohash_delete(&htab); + + /* + * In man(1) mode, prefer matches in earlier trees + * over matches in later trees. + */ + + if (cur && search->firstmatch) + break; } qsort(*res, cur, sizeof(struct manpage), manpage_compare); rc = 1; out: if (-1 != fd) { if (-1 == fchdir(fd)) perror(buf); close(fd); } exprfree(e); free(sql); *sz = cur; return(rc); } +void +mansearch_free(struct manpage *res, size_t sz) +{ + size_t i; + + for (i = 0; i < sz; i++) { + free(res[i].file); + free(res[i].names); + free(res[i].output); + } + free(res); +} + static int manpage_compare(const void *vp1, const void *vp2) { const struct manpage *mp1, *mp2; int diff; mp1 = vp1; mp2 = vp2; - diff = mp1->sec - mp2->sec; - return(diff ? diff : strcasecmp(mp1->names, mp2->names)); + return( (diff = mp2->bits - mp1->bits) ? diff : + (diff = mp1->sec - mp2->sec) ? diff : + strcasecmp(mp1->names, mp2->names)); } static void buildnames(struct manpage *mpage, sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, const char *path, int form) { char *newnames, *prevsec, *prevarch; const char *oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec; size_t i; int c; mpage->file = NULL; mpage->names = NULL; prevsec = prevarch = NULL; i = 1; SQL_BIND_INT64(db, s, i, pageid); while (SQLITE_ROW == (c = sqlite3_step(s))) { /* Decide whether we already have some names. */ if (NULL == mpage->names) { oldnames = ""; sep1 = ""; } else { oldnames = mpage->names; sep1 = ", "; } /* Fetch the next name. */ sec = (const char *)sqlite3_column_text(s, 0); arch = (const char *)sqlite3_column_text(s, 1); name = (const char *)sqlite3_column_text(s, 2); /* Remember the first section found. */ if (9 < mpage->sec && '1' <= *sec && '9' >= *sec) mpage->sec = (*sec - '1') + 1; /* If the section changed, append the old one. */ if (NULL != prevsec && (strcmp(sec, prevsec) || strcmp(arch, prevarch))) { sep2 = '\0' == *prevarch ? "" : "/"; mandoc_asprintf(&newnames, "%s(%s%s%s)", oldnames, prevsec, sep2, prevarch); free(mpage->names); oldnames = mpage->names = newnames; free(prevsec); free(prevarch); prevsec = prevarch = NULL; } /* Save the new section, to append it later. */ if (NULL == prevsec) { prevsec = mandoc_strdup(sec); prevarch = mandoc_strdup(arch); } /* Append the new name. */ mandoc_asprintf(&newnames, "%s%s%s", oldnames, sep1, name); free(mpage->names); mpage->names = newnames; /* Also save the first file name encountered. */ - if (NULL != mpage->file) + if (mpage->file != NULL) continue; - if (form) { + if (form & FORM_SRC) { sep1 = "man"; fsec = sec; } else { sep1 = "cat"; fsec = "0"; } - sep2 = '\0' == *arch ? "" : "/"; + sep2 = *arch == '\0' ? "" : "/"; mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s", path, sep1, sec, sep2, arch, name, fsec); } - if (SQLITE_DONE != c) + if (c != SQLITE_DONE) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); sqlite3_reset(s); /* Append one final section to the names. */ - if (NULL != prevsec) { - sep2 = '\0' == *prevarch ? "" : "/"; + if (prevsec != NULL) { + sep2 = *prevarch == '\0' ? "" : "/"; mandoc_asprintf(&newnames, "%s(%s%s%s)", mpage->names, prevsec, sep2, prevarch); free(mpage->names); mpage->names = newnames; free(prevsec); free(prevarch); } } static char * buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, uint64_t outbit) { char *output, *newoutput; const char *oldoutput, *sep1, *data; size_t i; int c; output = NULL; i = 1; SQL_BIND_INT64(db, s, i, pageid); SQL_BIND_INT64(db, s, i, outbit); while (SQLITE_ROW == (c = sqlite3_step(s))) { if (NULL == output) { oldoutput = ""; sep1 = ""; } else { oldoutput = output; sep1 = " # "; } data = (const char *)sqlite3_column_text(s, 1); mandoc_asprintf(&newoutput, "%s%s%s", oldoutput, sep1, data); free(output); output = newoutput; } if (SQLITE_DONE != c) fprintf(stderr, "%s\n", sqlite3_errmsg(db)); sqlite3_reset(s); return(output); } /* * Implement substring match as an application-defined SQL function. * Using the SQL LIKE or GLOB operators instead would be a bad idea * because that would require escaping metacharacters in the string * being searched for. */ static void sql_match(sqlite3_context *context, int argc, sqlite3_value **argv) { assert(2 == argc); sqlite3_result_int(context, NULL != strcasestr( (const char *)sqlite3_value_text(argv[1]), (const char *)sqlite3_value_text(argv[0]))); } /* * Implement regular expression match * as an application-defined SQL function. */ static void sql_regexp(sqlite3_context *context, int argc, sqlite3_value **argv) { assert(2 == argc); sqlite3_result_int(context, !regexec( (regex_t *)sqlite3_value_blob(argv[0]), (const char *)sqlite3_value_text(argv[1]), 0, NULL, 0)); } static void sql_append(char **sql, size_t *sz, const char *newstr, int count) { size_t newsz; newsz = 1 < count ? (size_t)count : strlen(newstr); *sql = mandoc_realloc(*sql, *sz + newsz + 1); if (1 < count) memset(*sql + *sz, *newstr, (size_t)count); else memcpy(*sql + *sz, newstr, newsz); *sz += newsz; (*sql)[*sz] = '\0'; } /* * Prepare the search SQL statement. */ static char * sql_statement(const struct expr *e) { char *sql; size_t sz; int needop; - sql = mandoc_strdup( - "SELECT desc, form, pageid FROM mpages WHERE "); + sql = mandoc_strdup(e->equal ? + "SELECT desc, form, pageid, bits " + "FROM mpages NATURAL JOIN names WHERE " : + "SELECT desc, form, pageid, 0 FROM mpages WHERE "); sz = strlen(sql); for (needop = 0; NULL != e; e = e->next) { if (e->and) sql_append(&sql, &sz, " AND ", 1); else if (needop) sql_append(&sql, &sz, " OR ", 1); if (e->open) sql_append(&sql, &sz, "(", e->open); sql_append(&sql, &sz, TYPE_Nd & e->bits ? (NULL == e->substr ? "desc REGEXP ?" : "desc MATCH ?") : TYPE_Nm == e->bits ? (NULL == e->substr ? "pageid IN (SELECT pageid FROM names " "WHERE name REGEXP ?)" : e->equal - ? "pageid IN (SELECT pageid FROM names " - "WHERE name = ?)" + ? "name = ? " : "pageid IN (SELECT pageid FROM names " "WHERE name MATCH ?)") : (NULL == e->substr ? "pageid IN (SELECT pageid FROM keys " "WHERE key REGEXP ? AND bits & ?)" : "pageid IN (SELECT pageid FROM keys " "WHERE key MATCH ? AND bits & ?)"), 1); if (e->close) sql_append(&sql, &sz, ")", e->close); needop = 1; } return(sql); } /* * Compile a set of string tokens into an expression. * Tokens in "argv" are assumed to be individual expression atoms (e.g., * "(", "foo=bar", etc.). */ static struct expr * exprcomp(const struct mansearch *search, int argc, char *argv[]) { uint64_t mask; int i, toopen, logic, igncase, toclose; struct expr *first, *prev, *cur, *next; first = cur = NULL; logic = igncase = toclose = 0; toopen = NULL != search->sec || NULL != search->arch; for (i = 0; i < argc; i++) { if (0 == strcmp("(", argv[i])) { if (igncase) goto fail; toopen++; toclose++; continue; } else if (0 == strcmp(")", argv[i])) { if (toopen || logic || igncase || NULL == cur) goto fail; cur->close++; if (0 > --toclose) goto fail; continue; } else if (0 == strcmp("-a", argv[i])) { if (toopen || logic || igncase || NULL == cur) goto fail; logic = 1; continue; } else if (0 == strcmp("-o", argv[i])) { if (toopen || logic || igncase || NULL == cur) goto fail; logic = 2; continue; } else if (0 == strcmp("-i", argv[i])) { if (igncase) goto fail; igncase = 1; continue; } next = exprterm(search, argv[i], !igncase); if (NULL == next) goto fail; if (NULL == first) first = next; else cur->next = next; prev = cur = next; /* * Searching for descriptions must be split out * because they are stored in the mpages table, * not in the keys table. */ for (mask = TYPE_Nm; mask <= TYPE_Nd; mask <<= 1) { if (mask & cur->bits && ~mask & cur->bits) { next = mandoc_calloc(1, sizeof(struct expr)); memcpy(next, cur, sizeof(struct expr)); prev->open = 1; cur->bits = mask; cur->next = next; cur = next; cur->bits &= ~mask; } } prev->and = (1 == logic); prev->open += toopen; if (cur != prev) cur->close = 1; toopen = logic = igncase = 0; } if (toopen || logic || igncase || toclose) goto fail; if (NULL != search->sec || NULL != search->arch) cur->close++; if (NULL != search->arch) cur = exprspec(cur, TYPE_arch, search->arch, "^(%s|any)$"); if (NULL != search->sec) exprspec(cur, TYPE_sec, search->sec, "^%s$"); return(first); fail: if (NULL != first) exprfree(first); return(NULL); } static struct expr * exprspec(struct expr *cur, uint64_t key, const char *value, const char *format) { char errbuf[BUFSIZ]; char *cp; int irc; mandoc_asprintf(&cp, format, value); cur->next = mandoc_calloc(1, sizeof(struct expr)); cur = cur->next; cur->and = 1; cur->bits = key; if (0 != (irc = regcomp(&cur->regexp, cp, REG_EXTENDED | REG_NOSUB | REG_ICASE))) { regerror(irc, &cur->regexp, errbuf, sizeof(errbuf)); fprintf(stderr, "regcomp: %s\n", errbuf); cur->substr = value; } free(cp); return(cur); } static struct expr * exprterm(const struct mansearch *search, char *buf, int cs) { char errbuf[BUFSIZ]; struct expr *e; char *key, *val; uint64_t iterbit; int i, irc; if ('\0' == *buf) return(NULL); e = mandoc_calloc(1, sizeof(struct expr)); - if (MANSEARCH_MAN & search->flags) { - e->bits = search->deftype; + if (search->argmode == ARG_NAME) { + e->bits = TYPE_Nm; e->substr = buf; e->equal = 1; return(e); } /* - * Look for an '=' or '~' operator, - * unless forced to some fixed macro keys. + * Separate macro keys from search string. + * If needed, request regular expression handling + * by setting e->substr to NULL. */ - if (MANSEARCH_WHATIS & search->flags) - val = NULL; - else - val = strpbrk(buf, "=~"); - - if (NULL == val) { - e->bits = search->deftype; + if (search->argmode == ARG_WORD) { + e->bits = TYPE_Nm; + e->substr = NULL; + mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf); + cs = 0; + } else if ((val = strpbrk(buf, "=~")) == NULL) { + e->bits = TYPE_Nm | TYPE_Nd; e->substr = buf; - - /* - * Found an operator. - * Regexp search is requested by !e->substr. - */ - } else { if (val == buf) - e->bits = search->deftype; + e->bits = TYPE_Nm | TYPE_Nd; if ('=' == *val) e->substr = val + 1; *val++ = '\0'; if (NULL != strstr(buf, "arch")) cs = 0; } /* Compile regular expressions. */ - if (MANSEARCH_WHATIS & search->flags) { - e->substr = NULL; - mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf); - } - if (NULL == e->substr) { irc = regcomp(&e->regexp, val, REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE)); - if (MANSEARCH_WHATIS & search->flags) + if (search->argmode == ARG_WORD) free(val); if (irc) { regerror(irc, &e->regexp, errbuf, sizeof(errbuf)); fprintf(stderr, "regcomp: %s\n", errbuf); free(e); return(NULL); } } if (e->bits) return(e); /* * Parse out all possible fields. * If the field doesn't resolve, bail. */ while (NULL != (key = strsep(&buf, ","))) { if ('\0' == *key) continue; for (i = 0, iterbit = 1; i < mansearch_keymax; i++, iterbit <<= 1) { if (0 == strcasecmp(key, mansearch_keynames[i])) { e->bits |= iterbit; break; } } if (i == mansearch_keymax) { if (strcasecmp(key, "any")) { free(e); return(NULL); } e->bits |= ~0ULL; } } return(e); } static void exprfree(struct expr *p) { struct expr *pp; while (NULL != p) { pp = p->next; free(p); p = pp; } } static void * hash_calloc(size_t nmemb, size_t sz, void *arg) { return(mandoc_calloc(nmemb, sz)); } static void * hash_alloc(size_t sz, void *arg) { return(mandoc_malloc(sz)); } static void hash_free(void *p, void *arg) { free(p); } Index: vendor/mdocml/dist/mansearch.h =================================================================== --- vendor/mdocml/dist/mansearch.h (revision 275396) +++ vendor/mdocml/dist/mansearch.h (revision 275397) @@ -1,101 +1,113 @@ -/* $Id: mansearch.h,v 1.15 2014/07/24 20:30:45 schwarze Exp $ */ +/* $Id: mansearch.h,v 1.21 2014/11/27 01:58:21 schwarze Exp $ */ /* * Copyright (c) 2012 Kristaps Dzonsons * Copyright (c) 2013, 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef MANSEARCH_H #define MANSEARCH_H #define MANDOC_DB "mandoc.db" #define TYPE_arch 0x0000000000000001ULL #define TYPE_sec 0x0000000000000002ULL #define TYPE_Xr 0x0000000000000004ULL #define TYPE_Ar 0x0000000000000008ULL #define TYPE_Fa 0x0000000000000010ULL #define TYPE_Fl 0x0000000000000020ULL #define TYPE_Dv 0x0000000000000040ULL #define TYPE_Fn 0x0000000000000080ULL #define TYPE_Ic 0x0000000000000100ULL #define TYPE_Pa 0x0000000000000200ULL #define TYPE_Cm 0x0000000000000400ULL #define TYPE_Li 0x0000000000000800ULL #define TYPE_Em 0x0000000000001000ULL #define TYPE_Cd 0x0000000000002000ULL #define TYPE_Va 0x0000000000004000ULL #define TYPE_Ft 0x0000000000008000ULL #define TYPE_Tn 0x0000000000010000ULL #define TYPE_Er 0x0000000000020000ULL #define TYPE_Ev 0x0000000000040000ULL #define TYPE_Sy 0x0000000000080000ULL #define TYPE_Sh 0x0000000000100000ULL #define TYPE_In 0x0000000000200000ULL #define TYPE_Ss 0x0000000000400000ULL #define TYPE_Ox 0x0000000000800000ULL #define TYPE_An 0x0000000001000000ULL #define TYPE_Mt 0x0000000002000000ULL #define TYPE_St 0x0000000004000000ULL #define TYPE_Bx 0x0000000008000000ULL #define TYPE_At 0x0000000010000000ULL #define TYPE_Nx 0x0000000020000000ULL #define TYPE_Fx 0x0000000040000000ULL #define TYPE_Lk 0x0000000080000000ULL #define TYPE_Ms 0x0000000100000000ULL #define TYPE_Bsx 0x0000000200000000ULL #define TYPE_Dx 0x0000000400000000ULL #define TYPE_Rs 0x0000000800000000ULL #define TYPE_Vt 0x0000001000000000ULL #define TYPE_Lb 0x0000002000000000ULL #define TYPE_Nm 0x0000004000000000ULL #define TYPE_Nd 0x0000008000000000ULL #define NAME_SYN 0x0000004000000001ULL -#define NAME_FILE 0x0000004000000002ULL -#define NAME_TITLE 0x000000400000000cULL -#define NAME_FIRST 0x0000004000000008ULL -#define NAME_HEAD 0x0000004000000010ULL +#define NAME_FIRST 0x0000004000000004ULL +#define NAME_TITLE 0x0000004000000006ULL +#define NAME_HEAD 0x0000004000000008ULL +#define NAME_FILE 0x0000004000000010ULL #define NAME_MASK 0x000000000000001fULL -__BEGIN_DECLS +#define FORM_CAT 0 /* manual page is preformatted */ +#define FORM_SRC 1 /* format is mdoc(7) or man(7) */ +#define FORM_NONE 4 /* format is unknown */ +enum argmode { + ARG_FILE = 0, + ARG_NAME, + ARG_WORD, + ARG_EXPR +}; + struct manpage { char *file; /* to be prefixed by manpath */ char *names; /* a list of names with sections */ char *output; /* user-defined additional output */ + size_t ipath; /* number of the manpath */ + uint64_t bits; /* name type mask */ int sec; /* section number, 10 means invalid */ int form; /* 0 == catpage */ }; struct mansearch { const char *arch; /* architecture/NULL */ const char *sec; /* mansection/NULL */ - uint64_t deftype; /* type if no key */ - int flags; -#define MANSEARCH_WHATIS 0x01 /* whatis(1) mode: whole words, no keys */ -#define MANSEARCH_MAN 0x02 /* man(1) mode: string equality, no keys */ + const char *outkey; /* show content of this macro */ + enum argmode argmode; /* interpretation of arguments */ + int firstmatch; /* first matching database only */ }; +__BEGIN_DECLS + int mansearch_setup(int); int mansearch(const struct mansearch *cfg, /* options */ const struct manpaths *paths, /* manpaths */ int argc, /* size of argv */ char *argv[], /* search terms */ - const char *outkey, /* name of additional output key */ struct manpage **res, /* results */ size_t *ressz); /* results returned */ +void mansearch_free(struct manpage *, size_t); __END_DECLS -#endif /*!MANSEARCH_H*/ +#endif /* MANSEARCH_H */ Index: vendor/mdocml/dist/mansearch_const.c =================================================================== --- vendor/mdocml/dist/mansearch_const.c (revision 275396) +++ vendor/mdocml/dist/mansearch_const.c (revision 275397) @@ -1,35 +1,34 @@ -/* $Id: mansearch_const.c,v 1.5 2014/08/09 14:05:21 schwarze Exp $ */ +/* $Id: mansearch_const.c,v 1.6 2014/08/10 23:54:41 schwarze Exp $ */ /* * Copyright (c) 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include + #include #include "manpath.h" #include "mansearch.h" const int mansearch_keymax = 40; const char *const mansearch_keynames[40] = { "arch", "sec", "Xr", "Ar", "Fa", "Fl", "Dv", "Fn", "Ic", "Pa", "Cm", "Li", "Em", "Cd", "Va", "Ft", "Tn", "Er", "Ev", "Sy", "Sh", "In", "Ss", "Ox", "An", "Mt", "St", "Bx", "At", "Nx", "Fx", "Lk", "Ms", "Bsx", "Dx", "Rs", "Vt", "Lb", "Nm", "Nd" }; Index: vendor/mdocml/dist/mchars_alloc.3 =================================================================== --- vendor/mdocml/dist/mchars_alloc.3 (revision 275396) +++ vendor/mdocml/dist/mchars_alloc.3 (revision 275397) @@ -1,224 +1,235 @@ -.\" $Id: mchars_alloc.3,v 1.1 2014/08/05 05:48:56 schwarze Exp $ +.\" $Id: mchars_alloc.3,v 1.2 2014/10/26 18:07:28 schwarze Exp $ .\" .\" Copyright (c) 2014 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: August 5 2014 $ +.Dd $Mdocdate: October 26 2014 $ .Dt MCHARS_ALLOC 3 .Os .Sh NAME .Nm mchars_alloc , .Nm mchars_free , .Nm mchars_num2char , .Nm mchars_num2uc , .Nm mchars_spec2cp , .Nm mchars_spec2str .Nd character table for mandoc .Sh LIBRARY .Lb libmandoc .Sh SYNOPSIS .In sys/types.h .In mandoc.h .Ft "struct mchars *" .Fn mchars_alloc "void" .Ft void .Fo mchars_free .Fa "struct mchars *table" .Fc .Ft char .Fo mchars_num2char .Fa "const char *decimal" .Fa "size_t sz" .Fc .Ft int .Fo mchars_num2uc .Fa "const char *hexadecimal" .Fa "size_t sz" .Fc .Ft int .Fo mchars_spec2cp .Fa "const struct mchars *table" .Fa "const char *name" .Fa "size_t sz" .Fc .Ft "const char *" .Fo mchars_spec2str .Fa "const struct mchars *table" .Fa "const char *name" .Fa "size_t sz" .Fa "size_t *rsz" .Fc +.Ft "const char *" +.Fn mchars_uc2str "int codepoint" .Sh DESCRIPTION These functions translate Unicode character numbers and .Xr roff 7 character names into glyphs. See .Xr mandoc_char 7 for a list of .Xr roff 7 special characters. These functions are intended for external use by programs formatting .Xr mdoc 7 and .Xr man 7 pages for output, for example the .Xr mandoc 1 output formatter modules and .Xr makewhatis 8 . The .Fa decimal , .Fa hexadecimal , .Fa name , and .Fa size input arguments are usually obtained from the .Xr mandoc_escape 3 parser function. .Pp The function .Fn mchars_num2char converts a .Fa decimal string representation of a character number consisting of .Fa sz digits into a printable ASCII character. If the input string is non-numeric or does not represent a printable ASCII character, the NUL character .Pq Sq \e0 is returned. For example, the .Xr mandoc 1 .Fl Tascii , .Fl Tutf8 , and .Fl Thtml output modules use this function to render .Xr roff 7 .Ic \eN escape sequences. .Pp The function .Fn mchars_num2uc converts a .Fa hexadecimal string representation of a Unicode codepoint consisting of .Fa sz digits into an integer representation. If the input string is non-numeric or represents an ASCII character, the NUL character .Pq Sq \e0 is returned. For example, the .Xr mandoc 1 .Fl Tutf8 and .Fl Thtml output modules use this function to render .Xr roff 7 .Ic \e[u Ns Ar XXXX Ns Ic \&] and .Ic \eC\(aqu Ns Ar XXXX Ns Ic \(aq escape sequences. .Pp The function .Fn mchars_alloc allocates an opaque .Vt "struct mchars *" table object for subsequent use by the following two lookup functions. When no longer needed, this object can be destroyed with .Fn mchars_free . .Pp The function .Fn mchars_spec2cp looks up a .Xr roff 7 special character .Fa name consisting of .Fa sz characters in the .Fa table and returns the corresponding Unicode codepoint. If the .Ar name is not recognized, \-1 is returned. For example, the .Xr mandoc 1 .Fl Tutf8 and .Fl Thtml output modules use this function to render .Xr roff 7 .Ic \e[ Ns Ar name Ns Ic \&] and .Ic \eC\(aq Ns Ar name Ns Ic \(aq escape sequences. .Pp The function .Fn mchars_spec2str looks up a .Xr roff 7 special character .Fa name consisting of .Fa sz characters in the .Fa table and returns an ASCII string representation. The length of the representation is returned in .Fa rsz . In many cases, the meaning of such ASCII representations is not quite obvious, so using .Xr roff 7 special characters in documents intended for ASCII rendering is usually a bad idea. If the .Ar name is not recognized, .Dv NULL is returned. For example, .Xr makewhatis 8 and the .Xr mandoc 1 .Fl Tascii output module use this function to render .Xr roff 7 .Ic \e[ Ns Ar name Ns Ic \&] and .Ic \eC\(aq Ns Ar name Ns Ic \(aq escape sequences. +.Pp +The function +.Fn mchars_uc2str +performs a reverse lookup of the Unicode +.Fa codepoint +and returns an ASCII string representation, or the string +.Qq +if none is available. .Sh FILES These funtions are implemented in the file .Pa chars.c . .Sh SEE ALSO .Xr mandoc 1 , .Xr mandoc_escape 3 , .Xr mandoc_char 7 , .Xr roff 7 .Sh HISTORY These functions and their predecessors have been available since the following mandoc versions: .Bl -column "mchars_num2char()" "1.11.3" "chars_num2char()" "1.10.10" .It Sy function Ta since Ta Sy predecessor Ta since .It Fn mchars_alloc Ta 1.11.3 Ta Fn ascii2htab Ta 1.5.3 .It Fn mchars_free Ta 1.11.2 Ta Fn asciifree Ta 1.6.0 .It Fn mchars_num2char Ta 1.11.2 Ta Fn chars_num2char Ta 1.10.10 .It Fn mchars_num2uc Ta 1.11.3 Ta \(em Ta \(em .It Fn mchars_spec2cp Ta 1.11.2 Ta Fn chars_spec2cp Ta 1.10.5 .It Fn mchars_spec2str Ta 1.11.2 Ta Fn a2ascii Ta 1.5.3 +.It Fn mchars_uc2str Ta 1.13.2 Ta \(em Ta \(em .El .Sh AUTHORS .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .An Ingo Schwarze Aq Mt schwarze@openbsd.org Index: vendor/mdocml/dist/mdoc.7 =================================================================== --- vendor/mdocml/dist/mdoc.7 (revision 275396) +++ vendor/mdocml/dist/mdoc.7 (revision 275397) @@ -1,3321 +1,3312 @@ -.\" $Id: mdoc.7,v 1.234 2014/08/08 16:38:06 schwarze Exp $ +.\" $Id: mdoc.7,v 1.244 2014/11/28 18:36:35 schwarze Exp $ .\" .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons .\" Copyright (c) 2010, 2011, 2013 Ingo Schwarze .\" .\" Permission to use, copy, modify, and distribute this software for any .\" purpose with or without fee is hereby granted, provided that the above .\" copyright notice and this permission notice appear in all copies. .\" .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: August 8 2014 $ +.Dd $Mdocdate: November 28 2014 $ .Dt MDOC 7 .Os .Sh NAME .Nm mdoc .Nd semantic markup language for formatting manual pages .Sh DESCRIPTION The .Nm mdoc language supports authoring of manual pages for the .Xr man 1 utility by allowing semantic annotations of words, phrases, page sections and complete manual pages. Such annotations are used by formatting tools to achieve a uniform presentation across all manuals written in .Nm , and to support hyperlinking if supported by the output medium. .Pp This reference document describes the structure of manual pages and the syntax and usage of the .Nm language. The reference implementation of a parsing and formatting tool is .Xr mandoc 1 ; the .Sx COMPATIBILITY section describes compatibility with other implementations. .Pp In an .Nm document, lines beginning with the control character .Sq \&. are called .Dq macro lines . The first word is the macro name. It consists of two or three letters. Most macro names begin with a capital letter. For a list of available macros, see .Sx MACRO OVERVIEW . The words following the macro name are arguments to the macro, optionally including the names of other, callable macros; see .Sx MACRO SYNTAX for details. .Pp Lines not beginning with the control character are called .Dq text lines . They provide free-form text to be printed; the formatting of the text depends on the respective processing context: .Bd -literal -offset indent \&.Sh Macro lines change control state. Text lines are interpreted within the current state. .Ed .Pp Many aspects of the basic syntax of the .Nm language are based on the .Xr roff 7 language; see the .Em LANGUAGE SYNTAX and .Em MACRO SYNTAX sections in the .Xr roff 7 manual for details, in particular regarding comments, escape sequences, whitespace, and quoting. However, using .Xr roff 7 requests in .Nm documents is discouraged; .Xr mandoc 1 supports some of them merely for backward compatibility. .Sh MANUAL STRUCTURE A well-formed .Nm document consists of a document prologue followed by one or more sections. .Pp The prologue, which consists of the .Sx \&Dd , .Sx \&Dt , and .Sx \&Os macros in that order, is required for every document. .Pp The first section (sections are denoted by .Sx \&Sh ) must be the NAME section, consisting of at least one .Sx \&Nm followed by .Sx \&Nd . .Pp Following that, convention dictates specifying at least the .Em SYNOPSIS and .Em DESCRIPTION sections, although this varies between manual sections. .Pp The following is a well-formed skeleton .Nm file for a utility .Qq progname : .Bd -literal -offset indent \&.Dd $\&Mdocdate$ \&.Dt PROGNAME section \&.Os \&.Sh NAME \&.Nm progname \&.Nd one line about what it does \&.\e\(dq .Sh LIBRARY \&.\e\(dq For sections 2, 3, and 9 only. \&.\e\(dq Not used in OpenBSD. \&.Sh SYNOPSIS \&.Nm progname \&.Op Fl options \&.Ar \&.Sh DESCRIPTION The \&.Nm utility processes files ... \&.\e\(dq .Sh CONTEXT \&.\e\(dq For section 9 functions only. \&.\e\(dq .Sh IMPLEMENTATION NOTES \&.\e\(dq Not used in OpenBSD. \&.\e\(dq .Sh RETURN VALUES \&.\e\(dq For sections 2, 3, and 9 function return values only. \&.\e\(dq .Sh ENVIRONMENT \&.\e\(dq For sections 1, 6, 7, and 8 only. \&.\e\(dq .Sh FILES \&.\e\(dq .Sh EXIT STATUS \&.\e\(dq For sections 1, 6, and 8 only. \&.\e\(dq .Sh EXAMPLES \&.\e\(dq .Sh DIAGNOSTICS \&.\e\(dq For sections 1, 4, 6, 7, 8, and 9 printf/stderr messages only. \&.\e\(dq .Sh ERRORS \&.\e\(dq For sections 2, 3, 4, and 9 errno settings only. \&.\e\(dq .Sh SEE ALSO \&.\e\(dq .Xr foobar 1 \&.\e\(dq .Sh STANDARDS \&.\e\(dq .Sh HISTORY \&.\e\(dq .Sh AUTHORS \&.\e\(dq .Sh CAVEATS \&.\e\(dq .Sh BUGS \&.\e\(dq .Sh SECURITY CONSIDERATIONS \&.\e\(dq Not used in OpenBSD. .Ed .Pp The sections in an .Nm document are conventionally ordered as they appear above. Sections should be composed as follows: .Bl -ohang -offset Ds .It Em NAME The name(s) and a one line description of the documented material. The syntax for this as follows: .Bd -literal -offset indent \&.Nm name0 , \&.Nm name1 , \&.Nm name2 \&.Nd a one line description .Ed .Pp Multiple .Sq \&Nm names should be separated by commas. .Pp The .Sx \&Nm macro(s) must precede the .Sx \&Nd macro. .Pp See .Sx \&Nm and .Sx \&Nd . .It Em LIBRARY The name of the library containing the documented material, which is assumed to be a function in a section 2, 3, or 9 manual. The syntax for this is as follows: .Bd -literal -offset indent \&.Lb libarm .Ed .Pp See .Sx \&Lb . .It Em SYNOPSIS Documents the utility invocation syntax, function call syntax, or device configuration. .Pp For the first, utilities (sections 1, 6, and 8), this is generally structured as follows: .Bd -literal -offset indent \&.Nm bar \&.Op Fl v \&.Op Fl o Ar file \&.Op Ar \&.Nm foo \&.Op Fl v \&.Op Fl o Ar file \&.Op Ar .Ed .Pp Commands should be ordered alphabetically. .Pp For the second, function calls (sections 2, 3, 9): .Bd -literal -offset indent \&.In header.h \&.Vt extern const char *global; \&.Ft "char *" \&.Fn foo "const char *src" \&.Ft "char *" \&.Fn bar "const char *src" .Ed .Pp Ordering of .Sx \&In , .Sx \&Vt , .Sx \&Fn , and .Sx \&Fo macros should follow C header-file conventions. .Pp And for the third, configurations (section 4): .Bd -literal -offset indent \&.Cd \(dqit* at isa? port 0x2e\(dq \&.Cd \(dqit* at isa? port 0x4e\(dq .Ed .Pp Manuals not in these sections generally don't need a .Em SYNOPSIS . .Pp Some macros are displayed differently in the .Em SYNOPSIS section, particularly .Sx \&Nm , .Sx \&Cd , .Sx \&Fd , .Sx \&Fn , .Sx \&Fo , .Sx \&In , .Sx \&Vt , and .Sx \&Ft . All of these macros are output on their own line. If two such dissimilar macros are pairwise invoked (except for .Sx \&Ft before .Sx \&Fo or .Sx \&Fn ) , they are separated by a vertical space, unless in the case of .Sx \&Fo , .Sx \&Fn , and .Sx \&Ft , which are always separated by vertical space. .Pp When text and macros following an .Sx \&Nm macro starting an input line span multiple output lines, all output lines but the first will be indented to align with the text immediately following the .Sx \&Nm macro, up to the next .Sx \&Nm , .Sx \&Sh , or .Sx \&Ss macro or the end of an enclosing block, whichever comes first. .It Em DESCRIPTION This begins with an expansion of the brief, one line description in .Em NAME : .Bd -literal -offset indent The \&.Nm utility does this, that, and the other. .Ed .Pp It usually follows with a breakdown of the options (if documenting a command), such as: .Bd -literal -offset indent The arguments are as follows: \&.Bl \-tag \-width Ds \&.It Fl v Print verbose information. \&.El .Ed .Pp Manuals not documenting a command won't include the above fragment. .Pp Since the .Em DESCRIPTION section usually contains most of the text of a manual, longer manuals often use the .Sx \&Ss macro to form subsections. In very long manuals, the .Em DESCRIPTION may be split into multiple sections, each started by an .Sx \&Sh macro followed by a non-standard section name, and each having several subsections, like in the present .Nm manual. .It Em CONTEXT This section lists the contexts in which functions can be called in section 9. The contexts are autoconf, process, or interrupt. .It Em IMPLEMENTATION NOTES Implementation-specific notes should be kept here. This is useful when implementing standard functions that may have side effects or notable algorithmic implications. .It Em RETURN VALUES This section documents the return values of functions in sections 2, 3, and 9. .Pp See .Sx \&Rv . .It Em ENVIRONMENT Lists the environment variables used by the utility, and explains the syntax and semantics of their values. The .Xr environ 7 manual provides examples of typical content and formatting. .Pp See .Sx \&Ev . .It Em FILES Documents files used. It's helpful to document both the file name and a short description of how the file is used (created, modified, etc.). .Pp See .Sx \&Pa . .It Em EXIT STATUS This section documents the command exit status for section 1, 6, and 8 utilities. Historically, this information was described in .Em DIAGNOSTICS , a practise that is now discouraged. .Pp See .Sx \&Ex . .It Em EXAMPLES Example usages. This often contains snippets of well-formed, well-tested invocations. Make sure that examples work properly! .It Em DIAGNOSTICS Documents error messages. In section 4 and 9 manuals, these are usually messages printed by the kernel to the console and to the kernel log. In section 1, 6, 7, and 8, these are usually messages printed by userland programs to the standard error output. .Pp Historically, this section was used in place of .Em EXIT STATUS for manuals in sections 1, 6, and 8; however, this practise is discouraged. .Pp See .Sx \&Bl .Fl diag . .It Em ERRORS Documents .Xr errno 2 settings in sections 2, 3, 4, and 9. .Pp See .Sx \&Er . .It Em SEE ALSO References other manuals with related topics. This section should exist for most manuals. Cross-references should conventionally be ordered first by section, then -alphabetically. +alphabetically (ignoring case). .Pp References to other documentation concerning the topic of the manual page, for example authoritative books or journal articles, may also be provided in this section. .Pp See .Sx \&Rs and .Sx \&Xr . .It Em STANDARDS References any standards implemented or used. If not adhering to any standards, the .Em HISTORY section should be used instead. .Pp See .Sx \&St . .It Em HISTORY A brief history of the subject, including where it was first implemented, and when it was ported to or reimplemented for the operating system at hand. .It Em AUTHORS Credits to the person or persons who wrote the code and/or documentation. Authors should generally be noted by both name and email address. .Pp See .Sx \&An . .It Em CAVEATS Common misuses and misunderstandings should be explained in this section. .It Em BUGS Known bugs, limitations, and work-arounds should be described in this section. .It Em SECURITY CONSIDERATIONS Documents any security precautions that operators should consider. .El .Sh MACRO OVERVIEW This overview is sorted such that macros of similar purpose are listed together, to help find the best macro for any given purpose. Deprecated macros are not included in the overview, but can be found below in the alphabetical .Sx MACRO REFERENCE . .Ss Document preamble and NAME section macros .Bl -column "Brq, Bro, Brc" description .It Sx \&Dd Ta document date: Cm $\&Mdocdate$ | Ar month day , year -.It Sx \&Dt Ta document title: Ar TITLE section Op Ar volume | arch +.It Sx \&Dt Ta document title: Ar TITLE section Op Ar arch .It Sx \&Os Ta operating system version: Op Ar system Op Ar version .It Sx \&Nm Ta document name (one argument) .It Sx \&Nd Ta document description (one line) .El .Ss Sections and cross references .Bl -column "Brq, Bro, Brc" description .It Sx \&Sh Ta section header (one line) .It Sx \&Ss Ta subsection header (one line) .It Sx \&Sx Ta internal cross reference to a section or subsection .It Sx \&Xr Ta cross reference to another manual page: Ar name section .It Sx \&Pp , \&Lp Ta start a text paragraph (no arguments) .El .Ss Displays and lists .Bl -column "Brq, Bro, Brc" description .It Sx \&Bd , \&Ed Ta display block: .Fl Ar type .Op Fl offset Ar width .Op Fl compact .It Sx \&D1 Ta indented display (one line) .It Sx \&Dl Ta indented literal display (one line) .It Sx \&Bl , \&El Ta list block: .Fl Ar type .Op Fl width Ar val .Op Fl offset Ar val .Op Fl compact .It Sx \&It Ta list item (syntax depends on Fl Ar type ) .It Sx \&Ta Ta table cell separator in Sx \&Bl Fl column No lists .It Sx \&Rs , \&%* , \&Re Ta bibliographic block (references) .El .Ss Spacing control .Bl -column "Brq, Bro, Brc" description .It Sx \&Pf Ta prefix, no following horizontal space (one argument) .It Sx \&Ns Ta roman font, no preceding horizontal space (no arguments) .It Sx \&Ap Ta apostrophe without surrounding whitespace (no arguments) .It Sx \&Sm Ta switch horizontal spacing mode: Op Cm on | off .It Sx \&Bk , \&Ek Ta keep block: Fl words .It Sx \&br Ta force output line break in text mode (no arguments) .It Sx \&sp Ta force vertical space: Op Ar height .El .Ss Semantic markup for command line utilities: .Bl -column "Brq, Bro, Brc" description .It Sx \&Nm Ta start a SYNOPSIS block with the name of a utility .It Sx \&Fl Ta command line options (flags) (>=0 arguments) .It Sx \&Cm Ta command modifier (>0 arguments) .It Sx \&Ar Ta command arguments (>=0 arguments) .It Sx \&Op , \&Oo , \&Oc Ta optional syntax elements (enclosure) .It Sx \&Ic Ta internal or interactive command (>0 arguments) .It Sx \&Ev Ta environmental variable (>0 arguments) .It Sx \&Pa Ta file system path (>=0 arguments) .El .Ss Semantic markup for function libraries: .Bl -column "Brq, Bro, Brc" description .It Sx \&Lb Ta function library (one argument) .It Sx \&In Ta include file (one argument) .It Sx \&Fd Ta other preprocessor directive (>0 arguments) .It Sx \&Ft Ta function type (>0 arguments) .It Sx \&Fo , \&Fc Ta function block: Ar funcname .It Sx \&Fn Ta function name: .Op Ar functype .Ar funcname .Oo .Op Ar argtype .Ar argname .Oc .It Sx \&Fa Ta function argument (>0 arguments) .It Sx \&Vt Ta variable type (>0 arguments) .It Sx \&Va Ta variable name (>0 arguments) .It Sx \&Dv Ta defined variable or preprocessor constant (>0 arguments) .It Sx \&Er Ta error constant (>0 arguments) .It Sx \&Ev Ta environmental variable (>0 arguments) .El .Ss Various semantic markup: .Bl -column "Brq, Bro, Brc" description .It Sx \&An Ta author name (>0 arguments) .It Sx \&Lk Ta hyperlink: Ar uri Op Ar name .It Sx \&Mt Ta Do mailto Dc hyperlink: Ar address .It Sx \&Cd Ta kernel configuration declaration (>0 arguments) .It Sx \&Ad Ta memory address (>0 arguments) .It Sx \&Ms Ta mathematical symbol (>0 arguments) .El .Ss Physical markup .Bl -column "Brq, Bro, Brc" description .It Sx \&Em Ta italic font or underline (emphasis) (>0 arguments) .It Sx \&Sy Ta boldface font (symbolic) (>0 arguments) .It Sx \&Li Ta typewriter font (literal) (>0 arguments) .It Sx \&No Ta return to roman font (normal) (no arguments) .It Sx \&Bf , \&Ef Ta font block: .Op Fl Ar type | Cm \&Em | \&Li | \&Sy .El .Ss Physical enclosures .Bl -column "Brq, Bro, Brc" description .It Sx \&Dq , \&Do , \&Dc Ta enclose in typographic double quotes: Dq text .It Sx \&Qq , \&Qo , \&Qc Ta enclose in typewriter double quotes: Qq text .It Sx \&Sq , \&So , \&Sc Ta enclose in single quotes: Sq text .It Sx \&Ql Ta single-quoted literal text: Ql text .It Sx \&Pq , \&Po , \&Pc Ta enclose in parentheses: Pq text .It Sx \&Bq , \&Bo , \&Bc Ta enclose in square brackets: Bq text .It Sx \&Brq , \&Bro , \&Brc Ta enclose in curly braces: Brq text .It Sx \&Aq , \&Ao , \&Ac Ta enclose in angle brackets: Aq text .It Sx \&Eo , \&Ec Ta generic enclosure .El .Ss Text production .Bl -column "Brq, Bro, Brc" description .It Sx \&Ex Fl std Ta standard command exit values: Op Ar utility ... .It Sx \&Rv Fl std Ta standard function return values: Op Ar function ... .It Sx \&St Ta reference to a standards document (one argument) .It Sx \&At Ta At .It Sx \&Bx Ta Bx .It Sx \&Bsx Ta Bsx .It Sx \&Nx Ta Nx .It Sx \&Fx Ta Fx .It Sx \&Ox Ta Ox .It Sx \&Dx Ta Dx .El .Sh MACRO REFERENCE This section is a canonical reference of all macros, arranged alphabetically. For the scoping of individual macros, see .Sx MACRO SYNTAX . .Ss \&%A Author name of an .Sx \&Rs block. Multiple authors should each be accorded their own .Sx \%%A line. Author names should be ordered with full or abbreviated forename(s) first, then full surname. .Ss \&%B Book title of an .Sx \&Rs block. This macro may also be used in a non-bibliographic context when referring to book titles. .Ss \&%C Publication city or location of an .Sx \&Rs block. .Ss \&%D Publication date of an .Sx \&Rs block. Recommended formats of arguments are .Ar month day , year or just .Ar year . .Ss \&%I Publisher or issuer name of an .Sx \&Rs block. .Ss \&%J Journal name of an .Sx \&Rs block. .Ss \&%N Issue number (usually for journals) of an .Sx \&Rs block. .Ss \&%O Optional information of an .Sx \&Rs block. .Ss \&%P Book or journal page number of an .Sx \&Rs block. .Ss \&%Q Institutional author (school, government, etc.) of an .Sx \&Rs block. Multiple institutional authors should each be accorded their own .Sx \&%Q line. .Ss \&%R Technical report name of an .Sx \&Rs block. .Ss \&%T Article title of an .Sx \&Rs block. This macro may also be used in a non-bibliographical context when referring to article titles. .Ss \&%U URI of reference document. .Ss \&%V Volume number of an .Sx \&Rs block. .Ss \&Ac Close an .Sx \&Ao block. Does not have any tail arguments. .Ss \&Ad Memory address. Do not use this for postal addresses. .Pp Examples: .Dl \&.Ad [0,$] .Dl \&.Ad 0x00000000 .Ss \&An Author name. Can be used both for the authors of the program, function, or driver documented in the manual, or for the authors of the manual itself. Requires either the name of an author or one of the following arguments: .Pp .Bl -tag -width "-nosplitX" -offset indent -compact .It Fl split Start a new output line before each subsequent invocation of .Sx \&An . .It Fl nosplit The opposite of .Fl split . .El .Pp The default is .Fl nosplit . The effect of selecting either of the .Fl split modes ends at the beginning of the .Em AUTHORS section. In the .Em AUTHORS section, the default is .Fl nosplit for the first author listing and .Fl split for all other author listings. .Pp Examples: .Dl \&.An -nosplit .Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv .Ss \&Ao Begin a block enclosed by angle brackets. Does not have any head arguments. .Pp Examples: .Dl \&.Fl -key= \&Ns \&Ao \&Ar val \&Ac .Pp See also .Sx \&Aq . .Ss \&Ap Inserts an apostrophe without any surrounding whitespace. This is generally used as a grammatical device when referring to the verb form of a function. .Pp Examples: .Dl \&.Fn execve \&Ap d .Ss \&Aq Encloses its arguments in angle brackets. .Pp Examples: .Dl \&.Fl -key= \&Ns \&Aq \&Ar val .Pp .Em Remarks : this macro is often abused for rendering URIs, which should instead use .Sx \&Lk or .Sx \&Mt , or to note pre-processor .Dq Li #include statements, which should use .Sx \&In . .Pp See also .Sx \&Ao . .Ss \&Ar Command arguments. If an argument is not provided, the string .Dq file ...\& is used as a default. .Pp Examples: .Dl ".Fl o Ar file" .Dl ".Ar" .Dl ".Ar arg1 , arg2 ." .Pp The arguments to the .Sx \&Ar macro are names and placeholders for command arguments; for fixed strings to be passed verbatim as arguments, use .Sx \&Fl or .Sx \&Cm . .Ss \&At Formats an .At version. Accepts one optional argument: .Pp .Bl -tag -width "v[1-7] | 32vX" -offset indent -compact .It Cm v[1-7] | 32v A version of .At . .It Cm III .At III . .It Cm V[.[1-4]]? A version of .At V . .El .Pp Note that these arguments do not begin with a hyphen. .Pp Examples: .Dl \&.At .Dl \&.At III .Dl \&.At V.1 .Pp See also .Sx \&Bsx , .Sx \&Bx , .Sx \&Dx , .Sx \&Fx , .Sx \&Nx , and .Sx \&Ox . .Ss \&Bc Close a .Sx \&Bo block. Does not have any tail arguments. .Ss \&Bd Begin a display block. Its syntax is as follows: .Bd -ragged -offset indent .Pf \. Sx \&Bd .Fl Ns Ar type .Op Fl offset Ar width .Op Fl compact .Ed .Pp Display blocks are used to select a different indentation and justification than the one used by the surrounding text. They may contain both macro lines and text lines. By default, a display block is preceded by a vertical space. .Pp The .Ar type must be one of the following: .Bl -tag -width 13n -offset indent .It Fl centered Produce one output line from each input line, and centre-justify each line. Using this display type is not recommended; many .Nm implementations render it poorly. .It Fl filled Change the positions of line breaks to fill each line, and left- and right-justify the resulting block. .It Fl literal Produce one output line from each input line, and do not justify the block at all. Preserve white space as it appears in the input. Always use a constant-width font. Use this for displaying source code. .It Fl ragged Change the positions of line breaks to fill each line, and left-justify the resulting block. .It Fl unfilled The same as .Fl literal , but using the same font as for normal text, which is a variable width font if supported by the output device. .El .Pp The .Ar type must be provided first. Additional arguments may follow: .Bl -tag -width 13n -offset indent .It Fl offset Ar width Indent the display by the .Ar width , which may be one of the following: .Bl -item .It One of the pre-defined strings .Cm indent , the width of a standard indentation (six constant width characters); .Cm indent-two , twice .Cm indent ; .Cm left , which has no effect; .Cm right , which justifies to the right margin; or .Cm center , which aligns around an imagined centre axis. .It A macro invocation, which selects a predefined width associated with that macro. The most popular is the imaginary macro .Ar \&Ds , which resolves to .Sy 6n . .It A scaling width as described in .Xr roff 7 . .It An arbitrary string, which indents by the length of this string. .El .Pp When the argument is missing, .Fl offset is ignored. .It Fl compact Do not assert vertical space before the display. .El .Pp Examples: .Bd -literal -offset indent \&.Bd \-literal \-offset indent \-compact Hello world. \&.Ed .Ed .Pp See also .Sx \&D1 and .Sx \&Dl . .Ss \&Bf Change the font mode for a scoped block of text. Its syntax is as follows: .Bd -ragged -offset indent .Pf \. Sx \&Bf .Oo .Fl emphasis | literal | symbolic | .Cm \&Em | \&Li | \&Sy .Oc .Ed .Pp The .Fl emphasis and .Cm \&Em argument are equivalent, as are .Fl symbolic and .Cm \&Sy , and .Fl literal and .Cm \&Li . Without an argument, this macro does nothing. The font mode continues until broken by a new font mode in a nested scope or .Sx \&Ef is encountered. .Pp See also .Sx \&Li , .Sx \&Ef , .Sx \&Em , and .Sx \&Sy . .Ss \&Bk For each macro, keep its output together on the same output line, until the end of the macro or the end of the input line is reached, whichever comes first. Line breaks in text lines are unaffected. The syntax is as follows: .Pp .D1 Pf \. Sx \&Bk Fl words .Pp The .Fl words argument is required; additional arguments are ignored. .Pp The following example will not break within each .Sx \&Op macro line: .Bd -literal -offset indent \&.Bk \-words \&.Op Fl f Ar flags \&.Op Fl o Ar output \&.Ek .Ed .Pp Be careful in using over-long lines within a keep block! Doing so will clobber the right margin. .Ss \&Bl Begin a list. Lists consist of items specified using the .Sx \&It macro, containing a head or a body or both. The list syntax is as follows: .Bd -ragged -offset indent .Pf \. Sx \&Bl .Fl Ns Ar type .Op Fl width Ar val .Op Fl offset Ar val .Op Fl compact .Op HEAD ... .Ed .Pp The list .Ar type is mandatory and must be specified first. The .Fl width and .Fl offset -arguments accept scaling widths as described in -.Xr roff 7 +arguments accept macro names as described for +.Sx \&Bd +.Fl offset , +scaling widths as described in +.Xr roff 7 , or use the length of the given string. The .Fl offset is a global indentation for the whole list, affecting both item heads and bodies. For those list types supporting it, the .Fl width argument requests an additional indentation of item bodies, to be added to the .Fl offset . Unless the .Fl compact argument is specified, list entries are separated by vertical space. .Pp A list must specify one of the following list types: .Bl -tag -width 12n -offset indent .It Fl bullet No item heads can be specified, but a bullet will be printed at the head of each item. Item bodies start on the same output line as the bullet and are indented according to the .Fl width argument. .It Fl column A columnated list. The .Fl width argument has no effect; instead, each argument specifies the width of one column, using either the scaling width syntax described in .Xr roff 7 or the string length of the argument. If the first line of the body of a .Fl column list is not an .Sx \&It macro line, .Sx \&It contexts spanning one input line each are implied until an .Sx \&It macro line is encountered, at which point items start being interpreted as described in the .Sx \&It documentation. .It Fl dash Like .Fl bullet , except that dashes are used in place of bullets. .It Fl diag Like .Fl inset , except that item heads are not parsed for macro invocations. Most often used in the .Em DIAGNOSTICS section with error constants in the item heads. .It Fl enum A numbered list. No item heads can be specified. Formatted like .Fl bullet , except that cardinal numbers are used in place of bullets, starting at 1. .It Fl hang Like .Fl tag , except that the first lines of item bodies are not indented, but follow the item heads like in .Fl inset lists. .It Fl hyphen Synonym for .Fl dash . .It Fl inset Item bodies follow items heads on the same line, using normal inter-word spacing. Bodies are not indented, and the .Fl width argument is ignored. .It Fl item No item heads can be specified, and none are printed. Bodies are not indented, and the .Fl width argument is ignored. .It Fl ohang Item bodies start on the line following item heads and are not indented. The .Fl width argument is ignored. .It Fl tag Item bodies are indented according to the .Fl width argument. When an item head fits inside the indentation, the item body follows this head on the same output line. Otherwise, the body starts on the output line following the head. .El .Pp Lists may be nested within lists and displays. Nesting of .Fl column and .Fl enum lists may not be portable. .Pp See also .Sx \&El and .Sx \&It . .Ss \&Bo Begin a block enclosed by square brackets. Does not have any head arguments. .Pp Examples: .Bd -literal -offset indent -compact \&.Bo 1 , \&.Dv BUFSIZ \&Bc .Ed .Pp See also .Sx \&Bq . .Ss \&Bq Encloses its arguments in square brackets. .Pp Examples: .Dl \&.Bq 1 , \&Dv BUFSIZ .Pp .Em Remarks : this macro is sometimes abused to emulate optional arguments for commands; the correct macros to use for this purpose are .Sx \&Op , .Sx \&Oo , and .Sx \&Oc . .Pp See also .Sx \&Bo . .Ss \&Brc Close a .Sx \&Bro block. Does not have any tail arguments. .Ss \&Bro Begin a block enclosed by curly braces. Does not have any head arguments. .Pp Examples: .Bd -literal -offset indent -compact \&.Bro 1 , ... , \&.Va n \&Brc .Ed .Pp See also .Sx \&Brq . .Ss \&Brq Encloses its arguments in curly braces. .Pp Examples: .Dl \&.Brq 1 , ... , \&Va n .Pp See also .Sx \&Bro . .Ss \&Bsx Format the .Bsx version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Bsx 1.0 .Dl \&.Bsx .Pp See also .Sx \&At , .Sx \&Bx , .Sx \&Dx , .Sx \&Fx , .Sx \&Nx , and .Sx \&Ox . .Ss \&Bt Supported only for compatibility, do not use this in new manuals. Prints .Dq is currently in beta test. .Ss \&Bx Format the .Bx version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Bx 4.3 Tahoe .Dl \&.Bx 4.4 .Dl \&.Bx .Pp See also .Sx \&At , .Sx \&Bsx , .Sx \&Dx , .Sx \&Fx , .Sx \&Nx , and .Sx \&Ox . .Ss \&Cd Kernel configuration declaration. This denotes strings accepted by .Xr config 8 . It is most often used in section 4 manual pages. .Pp Examples: .Dl \&.Cd device le0 at scode? .Pp .Em Remarks : this macro is commonly abused by using quoted literals to retain whitespace and align consecutive .Sx \&Cd declarations. This practise is discouraged. .Ss \&Cm Command modifiers. Typically used for fixed strings passed as arguments, unless .Sx \&Fl is more appropriate. Also useful when specifying configuration options or keys. .Pp Examples: .Dl ".Nm mt Fl f Ar device Cm rewind" .Dl ".Nm ps Fl o Cm pid , Ns Cm command" .Dl ".Nm dd Cm if= Ns Ar file1 Cm of= Ns Ar file2" .Dl ".Cm IdentityFile Pa ~/.ssh/id_rsa" .Dl ".Cm LogLevel Dv DEBUG" .Ss \&D1 One-line indented display. This is formatted by the default rules and is useful for simple indented statements. It is followed by a newline. .Pp Examples: .Dl \&.D1 \&Fl abcdefgh .Pp See also .Sx \&Bd and .Sx \&Dl . .Ss \&Db -Switch debugging mode. -Its syntax is as follows: -.Pp -.D1 Pf \. Sx \&Db Cm on | off -.Pp -This macro is ignored by -.Xr mandoc 1 . +This macro is obsolete. +No replacement is needed. +It is ignored by +.Xr mandoc 1 +and groff including its arguments. +It was formerly used to toggle a debugging mode. .Ss \&Dc Close a .Sx \&Do block. Does not have any tail arguments. .Ss \&Dd Document date for display in the page footer. This is the mandatory first macro of any .Nm manual. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Dd Ar month day , year .Pp The .Ar month is the full English month name, the .Ar day is an optionally zero-padded numeral, and the .Ar year is the full four-digit year. .Pp Other arguments are not portable; the .Xr mandoc 1 utility handles them as follows: .Bl -dash -offset 3n -compact .It To have the date automatically filled in by the .Ox version of .Xr cvs 1 , the special string .Dq $\&Mdocdate$ can be given as an argument. .It The traditional, purely numeric .Xr man 7 format .Ar year Ns \(en Ns Ar month Ns \(en Ns Ar day is accepted, too. .It If a date string cannot be parsed, it is used verbatim. .It If no date string is given, the current date is used. .El .Pp Examples: .Dl \&.Dd $\&Mdocdate$ .Dl \&.Dd $\&Mdocdate: July 21 2007$ .Dl \&.Dd July 21, 2007 .Pp See also .Sx \&Dt and .Sx \&Os . .Ss \&Dl -One-line intended display. +One-line indented display. This is formatted as literal text and is useful for commands and invocations. It is followed by a newline. .Pp Examples: .Dl \&.Dl % mandoc mdoc.7 \e(ba less .Pp See also .Sx \&Bd and .Sx \&D1 . .Ss \&Do Begin a block enclosed by double quotes. Does not have any head arguments. .Pp Examples: .Bd -literal -offset indent -compact \&.Do April is the cruellest month \&.Dc \e(em T.S. Eliot .Ed .Pp See also .Sx \&Dq . .Ss \&Dq Encloses its arguments in .Dq typographic double-quotes. .Pp Examples: .Bd -literal -offset indent -compact \&.Dq April is the cruellest month \e(em T.S. Eliot .Ed .Pp See also .Sx \&Qq , .Sx \&Sq , and .Sx \&Do . .Ss \&Dt Document title for display in the page header. This is the mandatory second macro of any .Nm file. Its syntax is as follows: .Bd -ragged -offset indent .Pf \. Sx \&Dt .Ar TITLE .Ar section -.Op Ar volume | arch +.Op Ar arch .Ed .Pp Its arguments are as follows: .Bl -tag -width section -offset 2n .It Ar TITLE The document's title (name), defaulting to .Dq UNTITLED if unspecified. To achieve a uniform appearance of page header lines, it should by convention be all caps. .It Ar section The manual section. This may be one of .Cm 1 .Pq utilities , .Cm 2 .Pq system calls , .Cm 3 .Pq libraries , .Cm 3p .Pq Perl libraries , .Cm 4 .Pq devices , .Cm 5 .Pq file formats , .Cm 6 .Pq games , .Cm 7 .Pq miscellaneous , .Cm 8 .Pq system utilities , .Cm 9 .Pq kernel functions , .Cm X11 .Pq X Window System , .Cm X11R6 .Pq X Window System , .Cm unass .Pq unassociated , .Cm local .Pq local system , .Cm draft .Pq draft manual , or .Cm paper .Pq paper . It should correspond to the manual's filename suffix and defaults to the empty string if unspecified. -.It Ar volume -This overrides the volume inferred from -.Ar section . -This field is optional, and if specified, must be one of -.Cm USD -.Pq users' supplementary documents , -.Cm PS1 -.Pq programmers' supplementary documents , -.Cm AMD -.Pq administrators' supplementary documents , -.Cm SMM -.Pq system managers' manuals , -.Cm URM -.Pq users' reference manuals , -.Cm PRM -.Pq programmers' reference manuals , -.Cm KM -.Pq kernel manuals , -.Cm IND -.Pq master index , -.Cm MMI -.Pq master index , -.Cm LOCAL -.Pq local manuals , -.Cm LOC -.Pq local manuals , -or -.Cm CON -.Pq contributed manuals . .It Ar arch This specifies the machine architecture a manual page applies to, where relevant, for example .Cm alpha , .Cm amd64 , .Cm i386 , or .Cm sparc64 . -The list of supported architectures varies by operating system. -For the full list of all architectures recognized by -.Xr mandoc 1 , -see the file -.Pa arch.in -in the source distribution. +The list of valid architectures varies by operating system. .El .Pp Examples: .Dl \&.Dt FOO 1 -.Dl \&.Dt FOO 4 KM .Dl \&.Dt FOO 9 i386 .Pp See also .Sx \&Dd and .Sx \&Os . .Ss \&Dv Defined variables such as preprocessor constants, constant symbols, enumeration values, and so on. .Pp Examples: .Dl \&.Dv NULL .Dl \&.Dv BUFSIZ .Dl \&.Dv STDOUT_FILENO .Pp See also .Sx \&Er and .Sx \&Ev for special-purpose constants, .Sx \&Va for variable symbols, and .Sx \&Fd for listing preprocessor variable definitions in the .Em SYNOPSIS . .Ss \&Dx Format the .Dx version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Dx 2.4.1 .Dl \&.Dx .Pp See also .Sx \&At , .Sx \&Bsx , .Sx \&Bx , .Sx \&Fx , .Sx \&Nx , and .Sx \&Ox . .Ss \&Ec Close a scope started by .Sx \&Eo . Its syntax is as follows: .Pp .D1 Pf \. Sx \&Ec Op Ar TERM .Pp The .Ar TERM argument is used as the enclosure tail, for example, specifying \e(rq will emulate .Sx \&Dc . .Ss \&Ed End a display context started by .Sx \&Bd . .Ss \&Ef End a font mode context started by .Sx \&Bf . .Ss \&Ek End a keep context started by .Sx \&Bk . .Ss \&El End a list context started by .Sx \&Bl . .Pp See also .Sx \&Bl and .Sx \&It . .Ss \&Em -Denotes text that should be -.Em emphasised . -Note that this is a presentation term and should not be used for -stylistically decorating technical terms. -Depending on the output device, this is usually represented -using an italic font or underlined characters. +Request an italic font. +If the output device does not provide that, underline. .Pp +This is most often used for stress emphasis (not to be confused with +importance, see +.Sx \&Sy ) . +In the rare cases where none of the semantic markup macros fit, +it can also be used for technical terms and placeholders, except +that for syntax elements, +.Sx \&Sy +and +.Sx \&Ar +are preferred, respectively. +.Pp Examples: -.Dl \&.Em Warnings! -.Dl \&.Em Remarks : +.Bd -literal -compact -offset indent +Selected lines are those +\&.Em not +matching any of the specified patterns. +Some of the functions use a +\&.Em hold space +to save the pattern space for subsequent retrieval. +.Ed .Pp See also .Sx \&Bf , .Sx \&Li , .Sx \&No , and .Sx \&Sy . .Ss \&En This macro is obsolete. Use .Sx \&Eo or any of the other enclosure macros. .Pp It encloses its argument in the delimiters specified by the last .Sx \&Es macro. .Ss \&Eo An arbitrary enclosure. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Eo Op Ar TERM .Pp The .Ar TERM argument is used as the enclosure head, for example, specifying \e(lq will emulate .Sx \&Do . .Ss \&Er Error constants for definitions of the .Va errno libc global variable. This is most often used in section 2 and 3 manual pages. .Pp Examples: .Dl \&.Er EPERM .Dl \&.Er ENOENT .Pp See also .Sx \&Dv for general constants. .Ss \&Es This macro is obsolete. Use .Sx \&Eo or any of the other enclosure macros. .Pp It takes two arguments, defining the delimiters to be used by subsequent .Sx \&En macros. .Ss \&Ev Environmental variables such as those specified in .Xr environ 7 . .Pp Examples: .Dl \&.Ev DISPLAY .Dl \&.Ev PATH .Pp See also .Sx \&Dv for general constants. .Ss \&Ex Insert a standard sentence regarding command exit values of 0 on success and >0 on failure. This is most often used in section 1, 6, and 8 manual pages. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Ex Fl std Op Ar utility ... .Pp If .Ar utility is not specified, the document's name set by .Sx \&Nm is used. Multiple .Ar utility arguments are treated as separate utilities. .Pp See also .Sx \&Rv . .Ss \&Fa -Function argument. +Function argument or parameter. Its syntax is as follows: .Bd -ragged -offset indent .Pf \. Sx \&Fa .Qo .Op Ar argtype .Op Ar argname .Qc Ar \&... .Ed .Pp Each argument may be a name and a type (recommended for the .Em SYNOPSIS section), a name alone (for function invocations), or a type alone (for function prototypes). If both a type and a name are given or if the type consists of multiple words, all words belonging to the same function argument have to be given in a single argument to the .Sx \&Fa macro. .Pp This macro is also used to specify the field name of a structure. .Pp Most often, the .Sx \&Fa macro is used in the .Em SYNOPSIS within .Sx \&Fo blocks when documenting multi-line function prototypes. If invoked with multiple arguments, the arguments are separated by a comma. Furthermore, if the following macro is another .Sx \&Fa , the last argument will also have a trailing comma. .Pp Examples: .Dl \&.Fa \(dqconst char *p\(dq .Dl \&.Fa \(dqint a\(dq \(dqint b\(dq \(dqint c\(dq .Dl \&.Fa \(dqchar *\(dq size_t .Pp See also .Sx \&Fo . .Ss \&Fc End a function context started by .Sx \&Fo . .Ss \&Fd Preprocessor directive, in particular for listing it in the .Em SYNOPSIS . Historically, it was also used to document include files. The latter usage has been deprecated in favour of .Sx \&In . .Pp Its syntax is as follows: .Bd -ragged -offset indent .Pf \. Sx \&Fd .Li # Ns Ar directive .Op Ar argument ... .Ed .Pp Examples: .Dl \&.Fd #define sa_handler __sigaction_u.__sa_handler .Dl \&.Fd #define SIO_MAXNFDS .Dl \&.Fd #ifdef FS_DEBUG .Dl \&.Ft void .Dl \&.Fn dbg_open \(dqconst char *\(dq .Dl \&.Fd #endif .Pp See also .Sx MANUAL STRUCTURE , .Sx \&In , and .Sx \&Dv . .Ss \&Fl Command-line flag or option. Used when listing arguments to command-line utilities. Prints a fixed-width hyphen .Sq \- directly followed by each argument. If no arguments are provided, a hyphen is printed followed by a space. If the argument is a macro, a hyphen is prefixed to the subsequent macro output. .Pp Examples: .Dl ".Fl R Op Fl H | L | P" .Dl ".Op Fl 1AaCcdFfgHhikLlmnopqRrSsTtux" .Dl ".Fl type Cm d Fl name Pa CVS" .Dl ".Fl Ar signal_number" .Dl ".Fl o Fl" .Pp See also .Sx \&Cm . .Ss \&Fn A function name. Its syntax is as follows: .Bd -ragged -offset indent .Pf \. Ns Sx \&Fn .Op Ar functype .Ar funcname .Op Oo Ar argtype Oc Ar argname .Ed .Pp Function arguments are surrounded in parenthesis and are delimited by commas. If no arguments are specified, blank parenthesis are output. In the .Em SYNOPSIS section, this macro starts a new output line, and a blank line is automatically inserted between function definitions. .Pp Examples: .Dl \&.Fn \(dqint funcname\(dq \(dqint arg0\(dq \(dqint arg1\(dq .Dl \&.Fn funcname \(dqint arg0\(dq .Dl \&.Fn funcname arg0 .Pp .Bd -literal -offset indent -compact \&.Ft functype \&.Fn funcname .Ed .Pp When referring to a function documented in another manual page, use .Sx \&Xr instead. See also .Sx MANUAL STRUCTURE , .Sx \&Fo , and .Sx \&Ft . .Ss \&Fo Begin a function block. This is a multi-line version of .Sx \&Fn . Its syntax is as follows: .Pp .D1 Pf \. Sx \&Fo Ar funcname .Pp Invocations usually occur in the following context: .Bd -ragged -offset indent .Pf \. Sx \&Ft Ar functype .br .Pf \. Sx \&Fo Ar funcname .br .Pf \. Sx \&Fa Qq Ar argtype Ar argname .br \&.\.\. .br .Pf \. Sx \&Fc .Ed .Pp A .Sx \&Fo scope is closed by .Sx \&Fc . .Pp See also .Sx MANUAL STRUCTURE , .Sx \&Fa , .Sx \&Fc , and .Sx \&Ft . .Ss \&Fr This macro is obsolete. No replacement markup is needed. .Pp It was used to show numerical function return values in an italic font. .Ss \&Ft A function type. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Ft Ar functype .Pp In the .Em SYNOPSIS section, a new output line is started after this macro. .Pp Examples: .Dl \&.Ft int .Bd -literal -offset indent -compact \&.Ft functype \&.Fn funcname .Ed .Pp See also .Sx MANUAL STRUCTURE , .Sx \&Fn , and .Sx \&Fo . .Ss \&Fx Format the .Fx version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Fx 7.1 .Dl \&.Fx .Pp See also .Sx \&At , .Sx \&Bsx , .Sx \&Bx , .Sx \&Dx , .Sx \&Nx , and .Sx \&Ox . .Ss \&Hf This macro is not implemented in .Xr mandoc 1 . .Pp It was used to include the contents of a (header) file literally. The syntax was: .Pp .Dl Pf . Sx \&Hf Ar filename .Ss \&Ic Designate an internal or interactive command. This is similar to .Sx \&Cm but used for instructions rather than values. .Pp Examples: .Dl \&.Ic :wq .Dl \&.Ic hash .Dl \&.Ic alias .Pp Note that using .Sx \&Bd Fl literal or .Sx \&D1 is preferred for displaying code; the .Sx \&Ic macro is used when referring to specific instructions. .Ss \&In An .Dq include file. When invoked as the first macro on an input line in the .Em SYNOPSIS section, the argument is displayed in angle brackets and preceded by .Dq #include , and a blank line is inserted in front if there is a preceding function declaration. This is most often used in section 2, 3, and 9 manual pages. .Pp Examples: .Dl \&.In sys/types.h .Pp See also .Sx MANUAL STRUCTURE . .Ss \&It A list item. The syntax of this macro depends on the list type. .Pp Lists of type .Fl hang , .Fl ohang , .Fl inset , and .Fl diag have the following syntax: .Pp .D1 Pf \. Sx \&It Ar args .Pp Lists of type .Fl bullet , .Fl dash , .Fl enum , .Fl hyphen and .Fl item have the following syntax: .Pp .D1 Pf \. Sx \&It .Pp with subsequent lines interpreted within the scope of the .Sx \&It until either a closing .Sx \&El or another .Sx \&It . .Pp The .Fl tag list has the following syntax: .Pp .D1 Pf \. Sx \&It Op Cm args .Pp Subsequent lines are interpreted as with .Fl bullet and family. The line arguments correspond to the list's left-hand side; body arguments correspond to the list's contents. .Pp The .Fl column list is the most complicated. Its syntax is as follows: .Pp .D1 Pf \. Sx \&It Ar cell Op Ar cell ... .D1 Pf \. Sx \&It Ar cell Op Sx \&Ta Ar cell ... .Pp The arguments consist of one or more lines of text and macros representing a complete table line. Cells within the line are delimited by tabs or by the special .Sx \&Ta block macro. The tab cell delimiter may only be used within the .Sx \&It line itself; on following lines, only the .Sx \&Ta macro can be used to delimit cells, and .Sx \&Ta is only recognised as a macro when called by other macros, not as the first macro on a line. .Pp Note that quoted strings may span tab-delimited cells on an .Sx \&It line. For example, .Pp .Dl .It \(dqcol1 ; col2 ;\(dq \&; .Pp will preserve the semicolon whitespace except for the last. .Pp See also .Sx \&Bl . .Ss \&Lb Specify a library. The syntax is as follows: .Pp .D1 Pf \. Sx \&Lb Ar library .Pp The .Ar library parameter may be a system library, such as .Cm libz or .Cm libpam , in which case a small library description is printed next to the linker invocation; or a custom library, in which case the library name is printed in quotes. This is most commonly used in the .Em SYNOPSIS section as described in .Sx MANUAL STRUCTURE . .Pp Examples: .Dl \&.Lb libz .Dl \&.Lb libmandoc .Ss \&Li Denotes text that should be in a .Li literal font mode. Note that this is a presentation term and should not be used for stylistically decorating technical terms. .Pp On terminal output devices, this is often indistinguishable from normal text. .Pp See also .Sx \&Bf , .Sx \&Em , .Sx \&No , and .Sx \&Sy . .Ss \&Lk Format a hyperlink. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Lk Ar uri Op Ar name .Pp Examples: .Dl \&.Lk http://bsd.lv \(dqThe BSD.lv Project\(dq .Dl \&.Lk http://bsd.lv .Pp See also .Sx \&Mt . .Ss \&Lp Synonym for .Sx \&Pp . .Ss \&Ms Display a mathematical symbol. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Ms Ar symbol .Pp Examples: .Dl \&.Ms sigma .Dl \&.Ms aleph .Ss \&Mt Format a .Dq mailto: hyperlink. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Mt Ar address .Pp Examples: .Dl \&.Mt discuss@manpages.bsd.lv .Dl \&.An Kristaps Dzonsons \&Aq \&Mt kristaps@bsd.lv .Ss \&Nd A one line description of the manual's content. This may only be invoked in the .Em SYNOPSIS section subsequent the .Sx \&Nm macro. .Pp Examples: .Dl Pf . Sx \&Nd mdoc language reference .Dl Pf . Sx \&Nd format and display UNIX manuals .Pp The .Sx \&Nd macro technically accepts child macros and terminates with a subsequent .Sx \&Sh invocation. Do not assume this behaviour: some .Xr whatis 1 database generators are not smart enough to parse more than the line arguments and will display macros verbatim. .Pp See also .Sx \&Nm . .Ss \&Nm The name of the manual page, or \(em in particular in section 1, 6, and 8 pages \(em of an additional command or feature documented in the manual page. When first invoked, the .Sx \&Nm macro expects a single argument, the name of the manual page. Usually, the first invocation happens in the .Em NAME section of the page. The specified name will be remembered and used whenever the macro is called again without arguments later in the page. The .Sx \&Nm macro uses .Sx Block full-implicit semantics when invoked as the first macro on an input line in the .Em SYNOPSIS section; otherwise, it uses ordinary .Sx In-line semantics. .Pp Examples: .Bd -literal -offset indent \&.Sh SYNOPSIS \&.Nm cat \&.Op Fl benstuv \&.Op Ar .Ed .Pp In the .Em SYNOPSIS of section 2, 3 and 9 manual pages, use the .Sx \&Fn macro rather than .Sx \&Nm to mark up the name of the manual page. .Ss \&No Normal text. Closes the scope of any preceding in-line macro. When used after physical formatting macros like .Sx \&Em or .Sx \&Sy , switches back to the standard font face and weight. Can also be used to embed plain text strings in macro lines using semantic annotation macros. .Pp Examples: .Dl ".Em italic , Sy bold , No and roman" .Pp .Bd -literal -offset indent -compact \&.Sm off \&.Cm :C No / Ar pattern No / Ar replacement No / \&.Sm on .Ed .Pp See also .Sx \&Em , .Sx \&Li , and .Sx \&Sy . .Ss \&Ns Suppress a space between the output of the preceding macro and the following text or macro. Following invocation, input is interpreted as normal text just like after an .Sx \&No macro. .Pp This has no effect when invoked at the start of a macro line. .Pp Examples: .Dl ".Ar name Ns = Ns Ar value" .Dl ".Cm :M Ns Ar pattern" .Dl ".Fl o Ns Ar output" .Pp See also .Sx \&No and .Sx \&Sm . .Ss \&Nx Format the .Nx version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Nx 5.01 .Dl \&.Nx .Pp See also .Sx \&At , .Sx \&Bsx , .Sx \&Bx , .Sx \&Dx , .Sx \&Fx , and .Sx \&Ox . .Ss \&Oc Close multi-line .Sx \&Oo context. .Ss \&Oo Multi-line version of .Sx \&Op . .Pp Examples: .Bd -literal -offset indent -compact \&.Oo \&.Op Fl flag Ns Ar value \&.Oc .Ed .Ss \&Op Optional part of a command line. Prints the argument(s) in brackets. This is most often used in the .Em SYNOPSIS section of section 1 and 8 manual pages. .Pp Examples: .Dl \&.Op \&Fl a \&Ar b .Dl \&.Op \&Ar a | b .Pp See also .Sx \&Oo . .Ss \&Os Operating system version for display in the page footer. This is the mandatory third macro of any .Nm file. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Os Op Ar system Op Ar version .Pp The optional .Ar system parameter specifies the relevant operating system or environment. Left unspecified, it defaults to the local operating system version. This is the suggested form. .Pp Examples: .Dl \&.Os .Dl \&.Os KTH/CSC/TCS .Dl \&.Os BSD 4.3 .Pp See also .Sx \&Dd and .Sx \&Dt . .Ss \&Ot This macro is obsolete. Use .Sx \&Ft instead; with .Xr mandoc 1 , both have the same effect. .Pp Historical .Nm packages described it as .Dq "old function type (FORTRAN)" . .Ss \&Ox Format the .Ox version provided as an argument, or a default value if no argument is provided. .Pp Examples: .Dl \&.Ox 4.5 .Dl \&.Ox .Pp See also .Sx \&At , .Sx \&Bsx , .Sx \&Bx , .Sx \&Dx , .Sx \&Fx , and .Sx \&Nx . .Ss \&Pa An absolute or relative file system path, or a file or directory name. If an argument is not provided, the character .Sq \(ti is used as a default. .Pp Examples: .Dl \&.Pa /usr/bin/mandoc .Dl \&.Pa /usr/share/man/man7/mdoc.7 .Pp See also .Sx \&Lk . .Ss \&Pc Close parenthesised context opened by .Sx \&Po . .Ss \&Pf Removes the space between its argument .Pq Dq prefix and the following macro. Its syntax is as follows: .Pp .D1 .Pf Ar prefix macro arguments ... .Pp This is equivalent to: .Pp .D1 .No Ar prefix No \&Ns Ar macro arguments ... .Pp Examples: .Dl ".Pf $ Ar variable_name" .Dl ".Pf 0x Ar hex_digits" .Pp See also .Sx \&Ns and .Sx \&Sm . .Ss \&Po Multi-line version of .Sx \&Pq . .Ss \&Pp Break a paragraph. This will assert vertical space between prior and subsequent macros and/or text. .Pp Paragraph breaks are not needed before or after .Sx \&Sh or .Sx \&Ss macros or before displays .Pq Sx \&Bd or lists .Pq Sx \&Bl unless the .Fl compact flag is given. .Ss \&Pq Parenthesised enclosure. .Pp See also .Sx \&Po . .Ss \&Qc Close quoted context opened by .Sx \&Qo . .Ss \&Ql Format a single-quoted literal. See also .Sx \&Qq and .Sx \&Sq . .Ss \&Qo Multi-line version of .Sx \&Qq . .Ss \&Qq Encloses its arguments in .Qq typewriter double-quotes. Consider using .Sx \&Dq . .Pp See also .Sx \&Dq , .Sx \&Sq , and .Sx \&Qo . .Ss \&Re Close an .Sx \&Rs block. Does not have any tail arguments. .Ss \&Rs Begin a bibliographic .Pq Dq reference block. Does not have any head arguments. The block macro may only contain .Sx \&%A , .Sx \&%B , .Sx \&%C , .Sx \&%D , .Sx \&%I , .Sx \&%J , .Sx \&%N , .Sx \&%O , .Sx \&%P , .Sx \&%Q , .Sx \&%R , .Sx \&%T , .Sx \&%U , and .Sx \&%V child macros (at least one must be specified). .Pp Examples: .Bd -literal -offset indent -compact \&.Rs \&.%A J. E. Hopcroft \&.%A J. D. Ullman \&.%B Introduction to Automata Theory, Languages, and Computation \&.%I Addison-Wesley \&.%C Reading, Massachusettes \&.%D 1979 \&.Re .Ed .Pp If an .Sx \&Rs block is used within a SEE ALSO section, a vertical space is asserted before the rendered output, else the block continues on the current line. .Ss \&Rv Insert a standard sentence regarding a function call's return value of 0 on success and \-1 on error, with the .Va errno libc global variable set on error. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Rv Fl std Op Ar function ... .Pp If .Ar function is not specified, the document's name set by .Sx \&Nm is used. Multiple .Ar function arguments are treated as separate functions. .Pp See also .Sx \&Ex . .Ss \&Sc Close single-quoted context opened by .Sx \&So . .Ss \&Sh Begin a new section. For a list of conventional manual sections, see .Sx MANUAL STRUCTURE . These sections should be used unless it's absolutely necessary that custom sections be used. .Pp Section names should be unique so that they may be keyed by .Sx \&Sx . Although this macro is parsed, it should not consist of child node or it may not be linked with .Sx \&Sx . .Pp See also .Sx \&Pp , .Sx \&Ss , and .Sx \&Sx . .Ss \&Sm Switches the spacing mode for output generated from macros. Its syntax is as follows: .Pp .D1 Pf \. Sx \&Sm Op Cm on | off .Pp By default, spacing is .Cm on . When switched .Cm off , no white space is inserted between macro arguments and between the output generated from adjacent macros, but text lines still get normal spacing between words and sentences. .Pp When called without an argument, the .Sx \&Sm macro toggles the spacing mode. Using this is not recommended because it makes the code harder to read. .Ss \&So Multi-line version of .Sx \&Sq . .Ss \&Sq Encloses its arguments in .Sq typewriter single-quotes. .Pp See also .Sx \&Dq , .Sx \&Qq , and .Sx \&So . .Ss \&Ss Begin a new subsection. Unlike with .Sx \&Sh , there is no convention for the naming of subsections. Except .Em DESCRIPTION , the conventional sections described in .Sx MANUAL STRUCTURE rarely have subsections. .Pp Sub-section names should be unique so that they may be keyed by .Sx \&Sx . Although this macro is parsed, it should not consist of child node or it may not be linked with .Sx \&Sx . .Pp See also .Sx \&Pp , .Sx \&Sh , and .Sx \&Sx . .Ss \&St Replace an abbreviation for a standard with the full form. The following standards are recognised. Where multiple lines are given without a blank line in between, they all refer to the same standard, and using the first form is recommended. .Bl -tag -width 1n .It C language standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-ansiC .St -ansiC .It \-ansiC-89 .St -ansiC-89 .It \-isoC .St -isoC .It \-isoC-90 .St -isoC-90 .br The original C standard. .Pp .It \-isoC-amd1 .St -isoC-amd1 .Pp .It \-isoC-tcor1 .St -isoC-tcor1 .Pp .It \-isoC-tcor2 .St -isoC-tcor2 .Pp .It \-isoC-99 .St -isoC-99 .It \-ansiC-99 .St -ansiC-99 .br The second major version of the C language standard. .Pp .It \-isoC-2011 .St -isoC-2011 .br The third major version of the C language standard. .El .It POSIX.1 before the Single UNIX Specification .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-p1003.1-88 .St -p1003.1-88 .It \-p1003.1 .St -p1003.1 .br The original POSIX standard, based on ANSI C. .Pp .It \-p1003.1-90 .St -p1003.1-90 .It \-iso9945-1-90 .St -iso9945-1-90 .br The first update of POSIX.1. .Pp .It \-p1003.1b-93 .St -p1003.1b-93 .It \-p1003.1b .St -p1003.1b .br Real-time extensions. .Pp .It \-p1003.1c-95 .St -p1003.1c-95 .br POSIX thread interfaces. .Pp .It \-p1003.1i-95 .St -p1003.1i-95 .br Technical Corrigendum. .Pp .It \-p1003.1-96 .St -p1003.1-96 .It \-iso9945-1-96 .St -iso9945-1-96 .br Includes POSIX.1-1990, 1b, 1c, and 1i. .El .It X/Open Portability Guide version 4 and related standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-xpg3 .St -xpg3 .br An XPG4 precursor, published in 1989. .Pp .It \-p1003.2 .St -p1003.2 .It \-p1003.2-92 .St -p1003.2-92 .It \-iso9945-2-93 .St -iso9945-2-93 .br An XCU4 precursor. .Pp .It \-p1003.2a-92 .St -p1003.2a-92 .br Updates to POSIX.2. .Pp .It \-xpg4 .St -xpg4 .br Based on POSIX.1 and POSIX.2, published in 1992. .El .It Single UNIX Specification version 1 and related standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact +.It \-susv1 +.St -susv1 .It \-xpg4.2 .St -xpg4.2 .br -This standard was published in 1994 and is also called SUSv1. +This standard was published in 1994. It was used as the basis for UNIX 95 certification. The following three refer to parts of it. .Pp .It \-xsh4.2 .St -xsh4.2 .Pp .It \-xcurses4.2 .St -xcurses4.2 .Pp .It \-p1003.1g-2000 .St -p1003.1g-2000 .br Networking APIs, including sockets. .Pp .It \-xpg4.3 .St -xpg4.3 .Pp .It \-svid4 .St -svid4 , .br Published in 1995. .El .It Single UNIX Specification version 2 and related standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-susv2 .St -susv2 This Standard was published in 1997 and is also called X/Open Portability Guide version 5. It was used as the basis for UNIX 98 certification. The following refer to parts of it. .Pp .It \-xbd5 .St -xbd5 .Pp .It \-xsh5 .St -xsh5 .Pp .It \-xcu5 .St -xcu5 .Pp .It \-xns5 .St -xns5 -.It \-xns5.2d2.0 -.St -xns5.2d2.0 .It \-xns5.2 .St -xns5.2 -.Pp -.It \-p1387.2 -.St -p1387.2 -.It \-p1387.2-95 -.St -p1387.2-95 -.br -POSIX software administration. .El .It Single UNIX Specification version 3 and related standards .Pp .Bl -tag -width "-p1003.1g-2000X" -compact .It \-p1003.1d-99 .St -p1003.1d-99 .br Additional real-time extensions. .Pp -.It \-p1003.1j-2000 -.St -p1003.1j-2000 -.br -Advanced real-time extensions. -.Pp -.It \-p1003.1q-2000 -.St -p1003.1q-2000 -.br -Amendment 7: Tracing [C Language]. -.Pp .It \-p1003.1-2001 .St -p1003.1-2001 .It \-susv3 .St -susv3 .br This standard is based on C99, SUSv2, POSIX.1-1996, 1d, and 1j. It is also called X/Open Portability Guide version 6. It is used as the basis for UNIX 03 certification. .Pp .It \-p1003.1-2004 .St -p1003.1-2004 .br The second and last Technical Corrigendum. .El .It Single UNIX Specification version 4 .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-p1003.1-2008 .St -p1003.1-2008 +.It \-susv4 +.St -susv4 .br -This standard is also called SUSv4 and +This standard is also called X/Open Portability Guide version 7. .Pp .It \-p1003.1-2013 .St -p1003.1-2013 .br This is the first Technical Corrigendum. .El .It Other standards .Pp .Bl -tag -width "-p1003.1g-2000" -compact .It \-ieee754 .St -ieee754 .br Floating-point arithmetic. .Pp .It \-iso8601 .St -iso8601 .br Representation of dates and times, published in 1988. .Pp .It \-iso8802-3 .St -iso8802-3 .br Ethernet local area networks. .Pp .It \-ieee1275-94 .St -ieee1275-94 .El .El .Ss \&Sx Reference a section or subsection in the same manual page. The referenced section or subsection name must be identical to the enclosed argument, including whitespace. .Pp Examples: .Dl \&.Sx MANUAL STRUCTURE .Pp See also .Sx \&Sh and .Sx \&Ss . .Ss \&Sy -Format enclosed arguments in symbolic -.Pq Dq boldface . -Note that this is a presentation term and should not be used for -stylistically decorating technical terms. +Request a boldface font. .Pp +This is most often used to indicate importance or seriousness (not to be +confused with stress emphasis, see +.Sx \&Em ) . +When none of the semantic macros fit, it is also adequate for syntax +elements that have to be given or that appear verbatim. +.Pp +Examples: +.Bd -literal -compact -offset indent +\&.Sy Warning : +If +\&.Sy s +appears in the owner permissions, set-user-ID mode is set. +This utility replaces the former +\&.Sy dumpdir +program. +.Ed +.Pp See also .Sx \&Bf , .Sx \&Em , .Sx \&Li , and .Sx \&No . .Ss \&Ta Table cell separator in .Sx \&Bl Fl column lists; can only be used below .Sx \&It . .Ss \&Tn Supported only for compatibility, do not use this in new manuals. Even though the macro name .Pq Dq tradename suggests a semantic function, historic usage is inconsistent, mostly using it as a presentation-level macro to request a small caps font. .Ss \&Ud Supported only for compatibility, do not use this in new manuals. Prints out .Dq currently under development. .Ss \&Ux Supported only for compatibility, do not use this in new manuals. Prints out .Dq Ux . .Ss \&Va A variable name. .Pp Examples: .Dl \&.Va foo .Dl \&.Va const char *bar ; +.Pp +For function arguments and parameters, use +.Sx \&Fa +instead. +For declarations of global variables in the +.Em SYNOPSIS +section, use +.Sx \&Vt . .Ss \&Vt A variable type. +.Pp This is also used for indicating global variables in the .Em SYNOPSIS section, in which case a variable name is also specified. Note that it accepts .Sx Block partial-implicit syntax when invoked as the first macro on an input line in the .Em SYNOPSIS section, else it accepts ordinary .Sx In-line syntax. In the former case, this macro starts a new output line, and a blank line is inserted in front if there is a preceding function definition or include directive. .Pp -Note that this should not be confused with -.Sx \&Ft , -which is used for function return types. -.Pp Examples: .Dl \&.Vt unsigned char .Dl \&.Vt extern const char * const sys_signame[] \&; .Pp +For parameters in function prototypes, use +.Sx \&Fa +instead, for function return types +.Sx \&Ft , +and for variable names outside the +.Em SYNOPSIS +section +.Sx \&Va , +even when including a type with the name. See also -.Sx MANUAL STRUCTURE -and -.Sx \&Va . +.Sx MANUAL STRUCTURE . .Ss \&Xc Close a scope opened by .Sx \&Xo . .Ss \&Xo Extend the header of an .Sx \&It macro or the body of a partial-implicit block macro beyond the end of the input line. This macro originally existed to work around the 9-argument limit of historic .Xr roff 7 . .Ss \&Xr Link to another manual .Pq Qq cross-reference . Its syntax is as follows: .Pp .D1 Pf \. Sx \&Xr Ar name Op section .Pp Cross reference the .Ar name and .Ar section number of another man page; omitting the section number is rarely useful. .Pp Examples: .Dl \&.Xr mandoc 1 .Dl \&.Xr mandoc 1 \&; .Dl \&.Xr mandoc 1 \&Ns s behaviour .Ss \&br Emits a line-break. This macro should not be used; it is implemented for compatibility with historical manuals. .Pp Consider using .Sx \&Pp in the event of natural paragraph breaks. .Ss \&sp Emits vertical space. This macro should not be used; it is implemented for compatibility with historical manuals. Its syntax is as follows: .Pp .D1 Pf \. Sx \&sp Op Ar height .Pp The .Ar height argument is a scaling width as described in .Xr roff 7 . If unspecified, .Sx \&sp asserts a single vertical space. .Sh MACRO SYNTAX The syntax of a macro depends on its classification. In this section, .Sq \-arg refers to macro arguments, which may be followed by zero or more .Sq parm parameters; .Sq \&Yo opens the scope of a macro; and if specified, .Sq \&Yc closes it out. .Pp The .Em Callable column indicates that the macro may also be called by passing its name as an argument to another macro. For example, .Sq \&.Op \&Fl O \&Ar file produces .Sq Op Fl O Ar file . To prevent a macro call and render the macro name literally, escape it by prepending a zero-width space, .Sq \e& . For example, .Sq \&Op \e&Fl O produces .Sq Op \&Fl O . If a macro is not callable but its name appears as an argument to another macro, it is interpreted as opaque text. For example, .Sq \&.Fl \&Sh produces .Sq Fl \&Sh . .Pp The .Em Parsed column indicates whether the macro may call other macros by receiving their names as arguments. If a macro is not parsed but the name of another macro appears as an argument, it is interpreted as opaque text. .Pp The .Em Scope column, if applicable, describes closure rules. .Ss Block full-explicit Multi-line scope closed by an explicit closing macro. All macros contains bodies; only .Sx \&Bf and .Pq optionally .Sx \&Bl contain a head. .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB \(lBbody...\(rB \&.Yc .Ed .Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope .It Sx \&Bd Ta \&No Ta \&No Ta closed by Sx \&Ed .It Sx \&Bf Ta \&No Ta \&No Ta closed by Sx \&Ef .It Sx \&Bk Ta \&No Ta \&No Ta closed by Sx \&Ek .It Sx \&Bl Ta \&No Ta \&No Ta closed by Sx \&El .It Sx \&Ed Ta \&No Ta \&No Ta opened by Sx \&Bd .It Sx \&Ef Ta \&No Ta \&No Ta opened by Sx \&Bf .It Sx \&Ek Ta \&No Ta \&No Ta opened by Sx \&Bk .It Sx \&El Ta \&No Ta \&No Ta opened by Sx \&Bl .El .Ss Block full-implicit Multi-line scope closed by end-of-file or implicitly by another macro. All macros have bodies; some .Po .Sx \&It Fl bullet , .Fl hyphen , .Fl dash , .Fl enum , .Fl item .Pc don't have heads; only one .Po .Sx \&It in .Sx \&Bl Fl column .Pc has multiple heads. .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead... \(lBTa head...\(rB\(rB \(lBbody...\(rB .Ed .Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXXXXXXXXX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope .It Sx \&It Ta \&No Ta Yes Ta closed by Sx \&It , Sx \&El .It Sx \&Nd Ta \&No Ta \&No Ta closed by Sx \&Sh .It Sx \&Nm Ta \&No Ta Yes Ta closed by Sx \&Nm , Sx \&Sh , Sx \&Ss .It Sx \&Sh Ta \&No Ta Yes Ta closed by Sx \&Sh .It Sx \&Ss Ta \&No Ta Yes Ta closed by Sx \&Sh , Sx \&Ss .El .Pp Note that the .Sx \&Nm macro is a .Sx Block full-implicit macro only when invoked as the first macro in a .Em SYNOPSIS section line, else it is .Sx In-line . .Ss Block partial-explicit Like block full-explicit, but also with single-line scope. Each has at least a body and, in limited circumstances, a head .Po .Sx \&Fo , .Sx \&Eo .Pc and/or tail .Pq Sx \&Ec . .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB \(lBbody...\(rB \&.Yc \(lBtail...\(rB \&.Yo \(lB\-arg \(lBparm...\(rB\(rB \(lBhead...\(rB \ \(lBbody...\(rB \&Yc \(lBtail...\(rB .Ed .Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope .It Sx \&Ac Ta Yes Ta Yes Ta opened by Sx \&Ao .It Sx \&Ao Ta Yes Ta Yes Ta closed by Sx \&Ac .It Sx \&Bc Ta Yes Ta Yes Ta closed by Sx \&Bo .It Sx \&Bo Ta Yes Ta Yes Ta opened by Sx \&Bc .It Sx \&Brc Ta Yes Ta Yes Ta opened by Sx \&Bro .It Sx \&Bro Ta Yes Ta Yes Ta closed by Sx \&Brc .It Sx \&Dc Ta Yes Ta Yes Ta opened by Sx \&Do .It Sx \&Do Ta Yes Ta Yes Ta closed by Sx \&Dc .It Sx \&Ec Ta Yes Ta Yes Ta opened by Sx \&Eo .It Sx \&Eo Ta Yes Ta Yes Ta closed by Sx \&Ec .It Sx \&Fc Ta Yes Ta Yes Ta opened by Sx \&Fo .It Sx \&Fo Ta \&No Ta \&No Ta closed by Sx \&Fc .It Sx \&Oc Ta Yes Ta Yes Ta closed by Sx \&Oo .It Sx \&Oo Ta Yes Ta Yes Ta opened by Sx \&Oc .It Sx \&Pc Ta Yes Ta Yes Ta closed by Sx \&Po .It Sx \&Po Ta Yes Ta Yes Ta opened by Sx \&Pc .It Sx \&Qc Ta Yes Ta Yes Ta opened by Sx \&Oo .It Sx \&Qo Ta Yes Ta Yes Ta closed by Sx \&Oc .It Sx \&Re Ta \&No Ta \&No Ta opened by Sx \&Rs .It Sx \&Rs Ta \&No Ta \&No Ta closed by Sx \&Re .It Sx \&Sc Ta Yes Ta Yes Ta opened by Sx \&So .It Sx \&So Ta Yes Ta Yes Ta closed by Sx \&Sc .It Sx \&Xc Ta Yes Ta Yes Ta opened by Sx \&Xo .It Sx \&Xo Ta Yes Ta Yes Ta closed by Sx \&Xc .El .Ss Block partial-implicit Like block full-implicit, but with single-line scope closed by the end of the line. .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBbody...\(rB \(lBres...\(rB .Ed .Bl -column "MacroX" "CallableX" "ParsedX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed .It Sx \&Aq Ta Yes Ta Yes .It Sx \&Bq Ta Yes Ta Yes .It Sx \&Brq Ta Yes Ta Yes .It Sx \&D1 Ta \&No Ta \&Yes .It Sx \&Dl Ta \&No Ta Yes .It Sx \&Dq Ta Yes Ta Yes .It Sx \&En Ta Yes Ta Yes .It Sx \&Op Ta Yes Ta Yes .It Sx \&Pq Ta Yes Ta Yes .It Sx \&Ql Ta Yes Ta Yes .It Sx \&Qq Ta Yes Ta Yes .It Sx \&Sq Ta Yes Ta Yes .It Sx \&Vt Ta Yes Ta Yes .El .Pp Note that the .Sx \&Vt macro is a .Sx Block partial-implicit only when invoked as the first macro in a .Em SYNOPSIS section line, else it is .Sx In-line . .Ss Special block macro The .Sx \&Ta macro can only be used below .Sx \&It in .Sx \&Bl Fl column lists. It delimits blocks representing table cells; these blocks have bodies, but no heads. .Bl -column "MacroX" "CallableX" "ParsedX" "closed by XXXX" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Scope .It Sx \&Ta Ta Yes Ta Yes Ta closed by Sx \&Ta , Sx \&It .El .Ss In-line Closed by the end of the line, fixed argument lengths, and/or subsequent macros. In-line macros have only text children. If a number (or inequality) of arguments is .Pq n , then the macro accepts an arbitrary number of arguments. .Bd -literal -offset indent \&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBargs...\(rB \(lBres...\(rB \&.Yo \(lB\-arg \(lBval...\(rB\(rB \(lBargs...\(rB Yc... \&.Yo \(lB\-arg \(lBval...\(rB\(rB arg0 arg1 argN .Ed .Bl -column "MacroX" "CallableX" "ParsedX" "Arguments" -offset indent .It Em Macro Ta Em Callable Ta Em Parsed Ta Em Arguments .It Sx \&%A Ta \&No Ta \&No Ta >0 .It Sx \&%B Ta \&No Ta \&No Ta >0 .It Sx \&%C Ta \&No Ta \&No Ta >0 .It Sx \&%D Ta \&No Ta \&No Ta >0 .It Sx \&%I Ta \&No Ta \&No Ta >0 .It Sx \&%J Ta \&No Ta \&No Ta >0 .It Sx \&%N Ta \&No Ta \&No Ta >0 .It Sx \&%O Ta \&No Ta \&No Ta >0 .It Sx \&%P Ta \&No Ta \&No Ta >0 .It Sx \&%Q Ta \&No Ta \&No Ta >0 .It Sx \&%R Ta \&No Ta \&No Ta >0 .It Sx \&%T Ta \&No Ta \&No Ta >0 .It Sx \&%U Ta \&No Ta \&No Ta >0 .It Sx \&%V Ta \&No Ta \&No Ta >0 .It Sx \&Ad Ta Yes Ta Yes Ta >0 .It Sx \&An Ta Yes Ta Yes Ta >0 .It Sx \&Ap Ta Yes Ta Yes Ta 0 .It Sx \&Ar Ta Yes Ta Yes Ta n .It Sx \&At Ta Yes Ta Yes Ta 1 .It Sx \&Bsx Ta Yes Ta Yes Ta n .It Sx \&Bt Ta \&No Ta \&No Ta 0 .It Sx \&Bx Ta Yes Ta Yes Ta n .It Sx \&Cd Ta Yes Ta Yes Ta >0 .It Sx \&Cm Ta Yes Ta Yes Ta >0 .It Sx \&Db Ta \&No Ta \&No Ta 1 .It Sx \&Dd Ta \&No Ta \&No Ta n .It Sx \&Dt Ta \&No Ta \&No Ta n .It Sx \&Dv Ta Yes Ta Yes Ta >0 .It Sx \&Dx Ta Yes Ta Yes Ta n .It Sx \&Em Ta Yes Ta Yes Ta >0 .It Sx \&Er Ta Yes Ta Yes Ta >0 .It Sx \&Es Ta Yes Ta Yes Ta 2 .It Sx \&Ev Ta Yes Ta Yes Ta >0 .It Sx \&Ex Ta \&No Ta \&No Ta n .It Sx \&Fa Ta Yes Ta Yes Ta >0 .It Sx \&Fd Ta \&No Ta \&No Ta >0 .It Sx \&Fl Ta Yes Ta Yes Ta n .It Sx \&Fn Ta Yes Ta Yes Ta >0 .It Sx \&Fr Ta Yes Ta Yes Ta >0 .It Sx \&Ft Ta Yes Ta Yes Ta >0 .It Sx \&Fx Ta Yes Ta Yes Ta n .It Sx \&Hf Ta \&No Ta \&No Ta n .It Sx \&Ic Ta Yes Ta Yes Ta >0 .It Sx \&In Ta \&No Ta \&No Ta 1 .It Sx \&Lb Ta \&No Ta \&No Ta 1 .It Sx \&Li Ta Yes Ta Yes Ta >0 .It Sx \&Lk Ta Yes Ta Yes Ta >0 .It Sx \&Lp Ta \&No Ta \&No Ta 0 .It Sx \&Ms Ta Yes Ta Yes Ta >0 .It Sx \&Mt Ta Yes Ta Yes Ta >0 .It Sx \&Nm Ta Yes Ta Yes Ta n .It Sx \&No Ta Yes Ta Yes Ta 0 .It Sx \&Ns Ta Yes Ta Yes Ta 0 .It Sx \&Nx Ta Yes Ta Yes Ta n .It Sx \&Os Ta \&No Ta \&No Ta n .It Sx \&Ot Ta Yes Ta Yes Ta >0 .It Sx \&Ox Ta Yes Ta Yes Ta n .It Sx \&Pa Ta Yes Ta Yes Ta n .It Sx \&Pf Ta Yes Ta Yes Ta 1 .It Sx \&Pp Ta \&No Ta \&No Ta 0 .It Sx \&Rv Ta \&No Ta \&No Ta n .It Sx \&Sm Ta \&No Ta \&No Ta <2 .It Sx \&St Ta \&No Ta Yes Ta 1 .It Sx \&Sx Ta Yes Ta Yes Ta >0 .It Sx \&Sy Ta Yes Ta Yes Ta >0 .It Sx \&Tn Ta Yes Ta Yes Ta >0 .It Sx \&Ud Ta \&No Ta \&No Ta 0 .It Sx \&Ux Ta Yes Ta Yes Ta n .It Sx \&Va Ta Yes Ta Yes Ta n .It Sx \&Vt Ta Yes Ta Yes Ta >0 .It Sx \&Xr Ta Yes Ta Yes Ta >0 .It Sx \&br Ta \&No Ta \&No Ta 0 .It Sx \&sp Ta \&No Ta \&No Ta 1 .El .Ss Delimiters When a macro argument consists of one single input character considered as a delimiter, the argument gets special handling. This does not apply when delimiters appear in arguments containing more than one character. Consequently, to prevent special handling and just handle it like any other argument, a delimiter can be escaped by prepending a zero-width space .Pq Sq \e& . In text lines, delimiters never need escaping, but may be used as normal punctuation. .Pp For many macros, when the leading arguments are opening delimiters, these delimiters are put before the macro scope, and when the trailing arguments are closing delimiters, these delimiters are put after the macro scope. For example, .Pp .D1 Pf \. \&Aq "( [ word ] ) ." .Pp renders as: .Pp .D1 Aq ( [ word ] ) . .Pp Opening delimiters are: .Pp .Bl -tag -width Ds -offset indent -compact .It \&( left parenthesis .It \&[ left bracket .El .Pp Closing delimiters are: .Pp .Bl -tag -width Ds -offset indent -compact .It \&. period .It \&, comma .It \&: colon .It \&; semicolon .It \&) right parenthesis .It \&] right bracket .It \&? question mark .It \&! exclamation mark .El .Pp Note that even a period preceded by a backslash .Pq Sq \e.\& gets this special handling; use .Sq \e&. to prevent that. .Pp Many in-line macros interrupt their scope when they encounter delimiters, and resume their scope when more arguments follow that are not delimiters. For example, .Pp .D1 Pf \. \&Fl "a ( b | c \e*(Ba d ) e" .Pp renders as: .Pp .D1 Fl a ( b | c \*(Ba d ) e .Pp This applies to both opening and closing delimiters, and also to the middle delimiter: .Pp .Bl -tag -width Ds -offset indent -compact .It \&| vertical bar .El .Pp As a special case, the predefined string \e*(Ba is handled and rendered in the same way as a plain .Sq \&| character. Using this predefined string is not recommended in new manuals. .Ss Font handling In .Nm documents, usage of semantic markup is recommended in order to have proper fonts automatically selected; only when no fitting semantic markup is available, consider falling back to .Sx Physical markup macros. Whenever any .Nm macro switches the .Xr roff 7 font mode, it will automatically restore the previous font when exiting its scope. Manually switching the font using the .Xr roff 7 .Ql \ef font escape sequences is never required. .Sh COMPATIBILITY This section provides an incomplete list of compatibility issues between mandoc and other troff implementations, at this time limited to GNU troff .Pq Qq groff . The term .Qq historic groff refers to groff versions before 1.17, which featured a significant update of the .Pa doc.tmac file. .Pp Heirloom troff, the other significant troff implementation accepting \-mdoc, is similar to historic groff. .Pp The following problematic behaviour is found in groff: .ds hist (Historic groff only.) .Pp .Bl -dash -compact .It Display macros .Po .Sx \&Bd , .Sx \&Dl , and .Sx \&D1 .Pc may not be nested. \*[hist] .It .Sx \&At with unknown arguments produces no output at all. \*[hist] Newer groff and mandoc print .Qq AT&T UNIX and the arguments. .It .Sx \&Bl Fl column does not recognise trailing punctuation characters when they immediately precede tabulator characters, but treats them as normal text and outputs a space before them. .It .Sx \&Bd Fl ragged compact does not start a new line. \*[hist] .It .Sx \&Dd with non-standard arguments behaves very strangely. When there are three arguments, they are printed verbatim. Any other number of arguments is replaced by the current date, but without any arguments the string .Dq Epoch is printed. .It .Sx \&Fl does not print a dash for an empty argument. \*[hist] .It .Sx \&Fn does not start a new line unless invoked as the line macro in the .Em SYNOPSIS section. \*[hist] .It .Sx \&Fo with .Pf non- Sx \&Fa children causes inconsistent spacing between arguments. In mandoc, a single space is always inserted between arguments. .It .Sx \&Ft in the .Em SYNOPSIS causes inconsistent vertical spacing, depending on whether a prior .Sx \&Fn has been invoked. See .Sx \&Ft and .Sx \&Fn for the normalised behaviour in mandoc. .It .Sx \&In ignores additional arguments and is not treated specially in the .Em SYNOPSIS . \*[hist] .It .Sx \&It sometimes requires a .Fl nested flag. \*[hist] In new groff and mandoc, any list may be nested by default and .Fl enum lists will restart the sequence only for the sub-list. .It .Sx \&Li followed by a delimiter is incorrectly used in some manuals instead of properly quoting that character, which sometimes works with historic groff. .It .Sx \&Lk only accepts a single link-name argument; the remainder is misformatted. .It .Sx \&Pa does not format its arguments when used in the FILES section under certain list types. .It .Sx \&Ta can only be called by other macros, but not at the beginning of a line. .It .Sx \&%C is not implemented (up to and including groff-1.22.2). .It Historic groff only allows up to eight or nine arguments per macro input line, depending on the exact situation. Providing more arguments causes garbled output. The number of arguments on one input line is not limited with mandoc. .It Historic groff has many un-callable macros. Most of these (excluding some block-level macros) are callable in new groff and mandoc. .It .Sq \(ba (vertical bar) is not fully supported as a delimiter. \*[hist] .It .Sq \ef .Pq font face and .Sq \eF .Pq font family face .Sx Text Decoration escapes behave irregularly when specified within line-macro scopes. .It Negative scaling units return to prior lines. Instead, mandoc truncates them to zero. .El .Pp The following features are unimplemented in mandoc: .Pp .Bl -dash -compact .It .Sx \&Bd .Fl file Ar file . .It .Sx \&Bd .Fl offset Cm center and .Fl offset Cm right . Groff does not implement centred and flush-right rendering either, but produces large indentations. .El .Sh SEE ALSO .Xr man 1 , .Xr mandoc 1 , .Xr eqn 7 , .Xr man 7 , .Xr mandoc_char 7 , .Xr roff 7 , .Xr tbl 7 .Sh HISTORY The .Nm language first appeared as a troff macro package in .Bx 4.4 . It was later significantly updated by Werner Lemberg and Ruslan Ermilov in groff-1.17. The standalone implementation that is part of the .Xr mandoc 1 utility written by Kristaps Dzonsons appeared in .Ox 4.6 . .Sh AUTHORS The .Nm reference was written by .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv . Index: vendor/mdocml/dist/mdoc.c =================================================================== --- vendor/mdocml/dist/mdoc.c (revision 275396) +++ vendor/mdocml/dist/mdoc.c (revision 275397) @@ -1,981 +1,898 @@ -/* $Id: mdoc.c,v 1.223 2014/08/06 15:09:05 schwarze Exp $ */ +/* $Id: mdoc.c,v 1.233 2014/11/28 06:27:05 schwarze Exp $ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include #include #include #include #include #include #include #include #include "mdoc.h" #include "mandoc.h" #include "mandoc_aux.h" #include "libmdoc.h" #include "libmandoc.h" const char *const __mdoc_macronames[MDOC_MAX + 1] = { "Ap", "Dd", "Dt", "Os", "Sh", "Ss", "Pp", "D1", "Dl", "Bd", "Ed", "Bl", "El", "It", "Ad", "An", "Ar", "Cd", "Cm", "Dv", "Er", "Ev", "Ex", "Fa", "Fd", "Fl", "Fn", "Ft", "Ic", "In", "Li", "Nd", "Nm", "Op", "Ot", "Pa", "Rv", "St", "Va", "Vt", "Xr", "%A", "%B", "%D", "%I", "%J", "%N", "%O", "%P", "%R", "%T", "%V", "Ac", "Ao", "Aq", "At", "Bc", "Bf", "Bo", "Bq", "Bsx", "Bx", "Db", "Dc", "Do", "Dq", "Ec", "Ef", "Em", "Eo", "Fx", "Ms", "No", "Ns", "Nx", "Ox", "Pc", "Pf", "Po", "Pq", "Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Sc", "So", "Sq", "Sm", "Sx", "Sy", "Tn", "Ux", "Xc", "Xo", "Fo", "Fc", "Oo", "Oc", "Bk", "Ek", "Bt", "Hf", "Fr", "Ud", "Lb", "Lp", "Lk", "Mt", "Brq", "Bro", "Brc", "%C", "Es", "En", "Dx", "%Q", "br", "sp", "%U", "Ta", "ll", "text", }; const char *const __mdoc_argnames[MDOC_ARG_MAX] = { "split", "nosplit", "ragged", "unfilled", "literal", "file", "offset", "bullet", "dash", "hyphen", "item", "enum", "tag", "diag", "hang", "ohang", "inset", "column", "width", "compact", "std", "filled", "words", "emphasis", "symbolic", "nested", "centered" }; const char * const *mdoc_macronames = __mdoc_macronames; const char * const *mdoc_argnames = __mdoc_argnames; static void mdoc_node_free(struct mdoc_node *); static void mdoc_node_unlink(struct mdoc *, struct mdoc_node *); static void mdoc_free1(struct mdoc *); static void mdoc_alloc1(struct mdoc *); static struct mdoc_node *node_alloc(struct mdoc *, int, int, enum mdoct, enum mdoc_type); -static int node_append(struct mdoc *, - struct mdoc_node *); -#if 0 -static int mdoc_preptext(struct mdoc *, int, char *, int); -#endif +static void node_append(struct mdoc *, struct mdoc_node *); static int mdoc_ptext(struct mdoc *, int, char *, int); static int mdoc_pmacro(struct mdoc *, int, char *, int); const struct mdoc_node * mdoc_node(const struct mdoc *mdoc) { return(mdoc->first); } const struct mdoc_meta * mdoc_meta(const struct mdoc *mdoc) { return(&mdoc->meta); } /* * Frees volatile resources (parse tree, meta-data, fields). */ static void mdoc_free1(struct mdoc *mdoc) { if (mdoc->first) mdoc_node_delete(mdoc, mdoc->first); free(mdoc->meta.msec); free(mdoc->meta.vol); free(mdoc->meta.arch); free(mdoc->meta.date); free(mdoc->meta.title); free(mdoc->meta.os); free(mdoc->meta.name); } /* * Allocate all volatile resources (parse tree, meta-data, fields). */ static void mdoc_alloc1(struct mdoc *mdoc) { memset(&mdoc->meta, 0, sizeof(struct mdoc_meta)); mdoc->flags = 0; mdoc->lastnamed = mdoc->lastsec = SEC_NONE; mdoc->last = mandoc_calloc(1, sizeof(struct mdoc_node)); mdoc->first = mdoc->last; mdoc->last->type = MDOC_ROOT; mdoc->last->tok = MDOC_MAX; mdoc->next = MDOC_NEXT_CHILD; } /* * Free up volatile resources (see mdoc_free1()) then re-initialises the * data with mdoc_alloc1(). After invocation, parse data has been reset * and the parser is ready for re-invocation on a new tree; however, * cross-parse non-volatile data is kept intact. */ void mdoc_reset(struct mdoc *mdoc) { mdoc_free1(mdoc); mdoc_alloc1(mdoc); } /* * Completely free up all volatile and non-volatile parse resources. * After invocation, the pointer is no longer usable. */ void mdoc_free(struct mdoc *mdoc) { mdoc_free1(mdoc); free(mdoc); } /* * Allocate volatile and non-volatile parse resources. */ struct mdoc * mdoc_alloc(struct roff *roff, struct mparse *parse, const char *defos, int quick) { struct mdoc *p; p = mandoc_calloc(1, sizeof(struct mdoc)); p->parse = parse; p->defos = defos; p->quick = quick; p->roff = roff; mdoc_hash_init(); mdoc_alloc1(p); return(p); } int mdoc_endparse(struct mdoc *mdoc) { - return(mdoc_macroend(mdoc)); + mdoc_macroend(mdoc); + return(1); } -int +void mdoc_addeqn(struct mdoc *mdoc, const struct eqn *ep) { struct mdoc_node *n; n = node_alloc(mdoc, ep->ln, ep->pos, MDOC_MAX, MDOC_EQN); n->eqn = ep; - - if ( ! node_append(mdoc, n)) - return(0); - + if (ep->ln > mdoc->last->line) + n->flags |= MDOC_LINE; + node_append(mdoc, n); mdoc->next = MDOC_NEXT_SIBLING; - return(1); } -int +void mdoc_addspan(struct mdoc *mdoc, const struct tbl_span *sp) { struct mdoc_node *n; n = node_alloc(mdoc, sp->line, 0, MDOC_MAX, MDOC_TBL); n->span = sp; - - if ( ! node_append(mdoc, n)) - return(0); - + node_append(mdoc, n); mdoc->next = MDOC_NEXT_SIBLING; - return(1); } /* * Main parse routine. Parses a single line -- really just hands off to * the macro (mdoc_pmacro()) or text parser (mdoc_ptext()). */ int mdoc_parseln(struct mdoc *mdoc, int ln, char *buf, int offs) { - mdoc->flags |= MDOC_NEWLINE; + if (mdoc->last->type != MDOC_EQN || ln > mdoc->last->line) + mdoc->flags |= MDOC_NEWLINE; /* * Let the roff nS register switch SYNOPSIS mode early, * such that the parser knows at all times * whether this mode is on or off. * Note that this mode is also switched by the Sh macro. */ if (roff_getreg(mdoc->roff, "nS")) mdoc->flags |= MDOC_SYNOPSIS; else mdoc->flags &= ~MDOC_SYNOPSIS; return(roff_getcontrol(mdoc->roff, buf, &offs) ? mdoc_pmacro(mdoc, ln, buf, offs) : mdoc_ptext(mdoc, ln, buf, offs)); } -int +void mdoc_macro(MACRO_PROT_ARGS) { assert(tok < MDOC_MAX); if (mdoc->flags & MDOC_PBODY) { if (tok == MDOC_Dt) { mandoc_vmsg(MANDOCERR_DT_LATE, mdoc->parse, line, ppos, "Dt %s", buf + *pos); - return(1); + return; } } else if ( ! (mdoc_macros[tok].flags & MDOC_PROLOGUE)) { if (mdoc->meta.title == NULL) { mandoc_vmsg(MANDOCERR_DT_NOTITLE, mdoc->parse, line, ppos, "%s %s", mdoc_macronames[tok], buf + *pos); mdoc->meta.title = mandoc_strdup("UNTITLED"); } if (NULL == mdoc->meta.vol) mdoc->meta.vol = mandoc_strdup("LOCAL"); mdoc->flags |= MDOC_PBODY; } - - return((*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf)); + (*mdoc_macros[tok].fp)(mdoc, tok, line, ppos, pos, buf); } -static int +static void node_append(struct mdoc *mdoc, struct mdoc_node *p) { assert(mdoc->last); assert(mdoc->first); assert(MDOC_ROOT != p->type); switch (mdoc->next) { case MDOC_NEXT_SIBLING: mdoc->last->next = p; p->prev = mdoc->last; p->parent = mdoc->last->parent; break; case MDOC_NEXT_CHILD: mdoc->last->child = p; p->parent = mdoc->last; break; default: abort(); /* NOTREACHED */ } p->parent->nchild++; /* * Copy over the normalised-data pointer of our parent. Not * everybody has one, but copying a null pointer is fine. */ switch (p->type) { case MDOC_BODY: if (ENDBODY_NOT != p->end) break; /* FALLTHROUGH */ case MDOC_TAIL: /* FALLTHROUGH */ case MDOC_HEAD: p->norm = p->parent->norm; break; default: break; } - if ( ! mdoc_valid_pre(mdoc, p)) - return(0); + mdoc_valid_pre(mdoc, p); switch (p->type) { case MDOC_HEAD: assert(MDOC_BLOCK == p->parent->type); p->parent->head = p; break; case MDOC_TAIL: assert(MDOC_BLOCK == p->parent->type); p->parent->tail = p; break; case MDOC_BODY: if (p->end) break; assert(MDOC_BLOCK == p->parent->type); p->parent->body = p; break; default: break; } mdoc->last = p; switch (p->type) { case MDOC_TBL: /* FALLTHROUGH */ case MDOC_TEXT: - if ( ! mdoc_valid_post(mdoc)) - return(0); + mdoc_valid_post(mdoc); break; default: break; } - - return(1); } static struct mdoc_node * 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 = mdoc->lastsec; p->line = line; p->pos = pos; p->lastline = line; p->tok = tok; p->type = type; /* Flag analysis. */ if (MDOC_SYNOPSIS & mdoc->flags) p->flags |= MDOC_SYNPRETTY; else p->flags &= ~MDOC_SYNPRETTY; if (MDOC_NEWLINE & mdoc->flags) p->flags |= MDOC_LINE; mdoc->flags &= ~MDOC_NEWLINE; return(p); } -int +void mdoc_tail_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) { struct mdoc_node *p; p = node_alloc(mdoc, line, pos, tok, MDOC_TAIL); - if ( ! node_append(mdoc, p)) - return(0); + node_append(mdoc, p); mdoc->next = MDOC_NEXT_CHILD; - return(1); } -int +struct mdoc_node * mdoc_head_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) { struct mdoc_node *p; assert(mdoc->first); assert(mdoc->last); - p = node_alloc(mdoc, line, pos, tok, MDOC_HEAD); - if ( ! node_append(mdoc, p)) - return(0); + node_append(mdoc, p); mdoc->next = MDOC_NEXT_CHILD; - return(1); + return(p); } -int +struct mdoc_node * mdoc_body_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok) { struct mdoc_node *p; p = node_alloc(mdoc, line, pos, tok, MDOC_BODY); - if ( ! node_append(mdoc, p)) - return(0); + node_append(mdoc, p); mdoc->next = MDOC_NEXT_CHILD; - return(1); + return(p); } -int +void 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(mdoc, line, pos, tok, MDOC_BODY); p->pending = body; p->norm = body->norm; p->end = end; - if ( ! node_append(mdoc, p)) - return(0); + node_append(mdoc, p); mdoc->next = MDOC_NEXT_SIBLING; - return(1); } -int +struct mdoc_node * mdoc_block_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok, struct mdoc_arg *args) { struct mdoc_node *p; p = node_alloc(mdoc, line, pos, tok, MDOC_BLOCK); p->args = args; if (p->args) (args->refcnt)++; switch (tok) { case MDOC_Bd: /* FALLTHROUGH */ case MDOC_Bf: /* FALLTHROUGH */ case MDOC_Bl: /* FALLTHROUGH */ case MDOC_En: /* FALLTHROUGH */ case MDOC_Rs: p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); break; default: break; } - - if ( ! node_append(mdoc, p)) - return(0); + node_append(mdoc, p); mdoc->next = MDOC_NEXT_CHILD; - return(1); + return(p); } -int +void mdoc_elem_alloc(struct mdoc *mdoc, int line, int pos, enum mdoct tok, struct mdoc_arg *args) { struct mdoc_node *p; p = node_alloc(mdoc, line, pos, tok, MDOC_ELEM); p->args = args; if (p->args) (args->refcnt)++; switch (tok) { case MDOC_An: p->norm = mandoc_calloc(1, sizeof(union mdoc_data)); break; default: break; } - - if ( ! node_append(mdoc, p)) - return(0); + node_append(mdoc, p); mdoc->next = MDOC_NEXT_CHILD; - return(1); } -int +void mdoc_word_alloc(struct mdoc *mdoc, int line, int pos, const char *p) { struct mdoc_node *n; n = node_alloc(mdoc, line, pos, MDOC_MAX, MDOC_TEXT); n->string = roff_strdup(mdoc->roff, p); - - if ( ! node_append(mdoc, n)) - return(0); - + node_append(mdoc, n); 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); mandoc_asprintf(&newstr, "%s %s", n->string, addstr); free(addstr); free(n->string); n->string = newstr; mdoc->next = MDOC_NEXT_SIBLING; } static void mdoc_node_free(struct mdoc_node *p) { if (MDOC_BLOCK == p->type || MDOC_ELEM == p->type) free(p->norm); if (p->string) free(p->string); if (p->args) mdoc_argv_free(p->args); free(p); } static void mdoc_node_unlink(struct mdoc *mdoc, struct mdoc_node *n) { /* Adjust siblings. */ if (n->prev) n->prev->next = n->next; if (n->next) n->next->prev = n->prev; /* Adjust parent. */ if (n->parent) { n->parent->nchild--; if (n->parent->child == n) n->parent->child = n->prev ? n->prev : n->next; if (n->parent->last == n) n->parent->last = n->prev ? n->prev : NULL; } /* Adjust parse point, if applicable. */ if (mdoc && mdoc->last == n) { if (n->prev) { mdoc->last = n->prev; mdoc->next = MDOC_NEXT_SIBLING; } else { mdoc->last = n->parent; mdoc->next = MDOC_NEXT_CHILD; } } if (mdoc && mdoc->first == n) mdoc->first = NULL; } void mdoc_node_delete(struct mdoc *mdoc, struct mdoc_node *p) { while (p->child) { assert(p->nchild); mdoc_node_delete(mdoc, p->child); } assert(0 == p->nchild); mdoc_node_unlink(mdoc, p); mdoc_node_free(p); } -int +void mdoc_node_relink(struct mdoc *mdoc, struct mdoc_node *p) { mdoc_node_unlink(mdoc, p); - return(node_append(mdoc, p)); + node_append(mdoc, p); } -#if 0 /* - * Pre-treat a text line. - * Text lines can consist of equations, which must be handled apart from - * the regular text. - * Thus, use this function to step through a line checking if it has any - * equations embedded in it. - * This must handle multiple equations AND equations that do not end at - * the end-of-line, i.e., will re-enter in the next roff parse. - */ -static int -mdoc_preptext(struct mdoc *mdoc, int line, char *buf, int offs) -{ - char *start, *end; - char delim; - - while ('\0' != buf[offs]) { - /* Mark starting position if eqn is set. */ - start = NULL; - if ('\0' != (delim = roff_eqndelim(mdoc->roff))) - if (NULL != (start = strchr(buf + offs, delim))) - *start++ = '\0'; - - /* Parse text as normal. */ - if ( ! mdoc_ptext(mdoc, line, buf, offs)) - return(0); - - /* Continue only if an equation exists. */ - if (NULL == start) - break; - - /* Read past the end of the equation. */ - offs += start - (buf + offs); - assert(start == &buf[offs]); - if (NULL != (end = strchr(buf + offs, delim))) { - *end++ = '\0'; - while (' ' == *end) - end++; - } - - /* Parse the equation itself. */ - roff_openeqn(mdoc->roff, NULL, line, offs, buf); - - /* Process a finished equation? */ - if (roff_closeeqn(mdoc->roff)) - if ( ! mdoc_addeqn(mdoc, roff_eqn(mdoc->roff))) - return(0); - offs += (end - (buf + offs)); - } - - return(1); -} -#endif - -/* * Parse free-form text, that is, a line that does not begin with the * control character. */ static int mdoc_ptext(struct mdoc *mdoc, int line, char *buf, int offs) { char *c, *ws, *end; struct mdoc_node *n; assert(mdoc->last); n = mdoc->last; /* * Divert directly to list processing if we're encountering a * columnar MDOC_BLOCK with or without a prior MDOC_BLOCK entry * (a MDOC_BODY means it's already open, in which case we should * process within its context in the normal way). */ if (MDOC_Bl == n->tok && MDOC_BODY == n->type && LIST_column == n->norm->Bl.type) { /* `Bl' is open without any children. */ mdoc->flags |= MDOC_FREECOL; - return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf)); + mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf); + return(1); } if (MDOC_It == n->tok && MDOC_BLOCK == n->type && NULL != n->parent && MDOC_Bl == n->parent->tok && LIST_column == n->parent->norm->Bl.type) { /* `Bl' has block-level `It' children. */ mdoc->flags |= MDOC_FREECOL; - return(mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf)); + mdoc_macro(mdoc, MDOC_It, line, offs, &offs, buf); + return(1); } /* * Search for the beginning of unescaped trailing whitespace (ws) * and for the first character not to be output (end). */ /* FIXME: replace with strcspn(). */ ws = NULL; for (c = end = buf + offs; *c; c++) { switch (*c) { case ' ': if (NULL == ws) ws = c; continue; case '\t': /* * Always warn about trailing tabs, * even outside literal context, * where they should be put on the next line. */ if (NULL == ws) ws = c; /* * Strip trailing tabs in literal context only; * outside, they affect the next line. */ if (MDOC_LITERAL & mdoc->flags) continue; break; case '\\': /* Skip the escaped character, too, if any. */ if (c[1]) c++; /* FALLTHROUGH */ default: ws = NULL; break; } end = c + 1; } *end = '\0'; if (ws) mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, line, (int)(ws-buf), NULL); - if ('\0' == buf[offs] && ! (MDOC_LITERAL & mdoc->flags)) { + if (buf[offs] == '\0' && ! (mdoc->flags & MDOC_LITERAL)) { mandoc_msg(MANDOCERR_FI_BLANK, mdoc->parse, line, (int)(c - buf), NULL); /* * Insert a `sp' in the case of a blank line. Technically, * blank lines aren't allowed, but enough manuals assume this * behaviour that we want to work around it. */ - if ( ! mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL)) - return(0); - + mdoc_elem_alloc(mdoc, line, offs, MDOC_sp, NULL); mdoc->next = MDOC_NEXT_SIBLING; - - return(mdoc_valid_post(mdoc)); + mdoc_valid_post(mdoc); + return(1); } - if ( ! mdoc_word_alloc(mdoc, line, offs, buf+offs)) - return(0); + mdoc_word_alloc(mdoc, line, offs, buf+offs); - if (MDOC_LITERAL & mdoc->flags) + if (mdoc->flags & MDOC_LITERAL) return(1); /* * End-of-sentence check. If the last character is an unescaped * EOS character, then flag the node as being the end of a * sentence. The front-end will know how to interpret this. */ assert(buf < end); if (mandoc_eos(buf+offs, (size_t)(end-buf-offs))) mdoc->last->flags |= MDOC_EOS; - return(1); } /* * Parse a macro line, that is, a line beginning with the control * character. */ static int mdoc_pmacro(struct mdoc *mdoc, int ln, char *buf, int offs) { + struct mdoc_node *n; + const char *cp; enum mdoct tok; int i, sv; char mac[5]; - struct mdoc_node *n; - /* Empty post-control lines are ignored. */ - - if ('"' == buf[offs]) { - mandoc_msg(MANDOCERR_COMMENT_BAD, mdoc->parse, - ln, offs, NULL); - return(1); - } else if ('\0' == buf[offs]) - return(1); - sv = offs; /* * Copy the first word into a nil-terminated buffer. - * Stop copying when a tab, space, or eoln is encountered. + * Stop when a space, tab, escape, or eoln is encountered. */ i = 0; - while (i < 4 && '\0' != buf[offs] && ' ' != buf[offs] && - '\t' != buf[offs]) + while (i < 4 && strchr(" \t\\", buf[offs]) == NULL) mac[i++] = buf[offs++]; mac[i] = '\0'; tok = (i > 1 && i < 4) ? mdoc_hash_find(mac) : MDOC_MAX; - if (MDOC_MAX == tok) { + if (tok == MDOC_MAX) { mandoc_msg(MANDOCERR_MACRO, mdoc->parse, ln, sv, buf + sv - 1); return(1); } - /* Disregard the first trailing tab, if applicable. */ + /* Skip a leading escape sequence or tab. */ - if ('\t' == buf[offs]) + switch (buf[offs]) { + case '\\': + cp = buf + offs + 1; + mandoc_escape(&cp, NULL, NULL); + offs = cp - buf; + break; + case '\t': offs++; + break; + default: + break; + } /* Jump to the next non-whitespace word. */ while (buf[offs] && ' ' == buf[offs]) offs++; /* * Trailing whitespace. Note that tabs are allowed to be passed * into the parser as "text", so we only warn about spaces here. */ if ('\0' == buf[offs] && ' ' == buf[offs - 1]) mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, ln, offs - 1, NULL); /* * If an initial macro or a list invocation, divert directly * into macro processing. */ - if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) - return(mdoc_macro(mdoc, tok, ln, sv, &offs, buf)); + if (NULL == mdoc->last || MDOC_It == tok || MDOC_El == tok) { + mdoc_macro(mdoc, tok, ln, sv, &offs, buf); + return(1); + } n = mdoc->last; assert(mdoc->last); /* * If the first macro of a `Bl -column', open an `It' block * context around the parsed macro. */ if (MDOC_Bl == n->tok && MDOC_BODY == n->type && LIST_column == n->norm->Bl.type) { mdoc->flags |= MDOC_FREECOL; - return(mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf)); + mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf); + return(1); } /* * If we're following a block-level `It' within a `Bl -column' * context (perhaps opened in the above block or in ptext()), * then open an `It' block context around the parsed macro. */ if (MDOC_It == n->tok && MDOC_BLOCK == n->type && NULL != n->parent && MDOC_Bl == n->parent->tok && LIST_column == n->parent->norm->Bl.type) { mdoc->flags |= MDOC_FREECOL; - return(mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf)); + mdoc_macro(mdoc, MDOC_It, ln, sv, &sv, buf); + return(1); } /* Normal processing of a macro. */ - if ( ! mdoc_macro(mdoc, tok, ln, sv, &offs, buf)) - return(0); + mdoc_macro(mdoc, tok, ln, sv, &offs, buf); /* In quick mode (for mandocdb), abort after the NAME section. */ if (mdoc->quick && MDOC_Sh == tok && SEC_NAME != mdoc->last->sec) return(2); return(1); } enum mdelim mdoc_isdelim(const char *p) { if ('\0' == p[0]) return(DELIM_NONE); if ('\0' == p[1]) switch (p[0]) { case '(': /* FALLTHROUGH */ case '[': return(DELIM_OPEN); case '|': return(DELIM_MIDDLE); case '.': /* FALLTHROUGH */ case ',': /* FALLTHROUGH */ case ';': /* FALLTHROUGH */ case ':': /* FALLTHROUGH */ case '?': /* FALLTHROUGH */ case '!': /* FALLTHROUGH */ case ')': /* FALLTHROUGH */ case ']': return(DELIM_CLOSE); default: return(DELIM_NONE); } if ('\\' != p[0]) return(DELIM_NONE); if (0 == strcmp(p + 1, ".")) return(DELIM_CLOSE); if (0 == strcmp(p + 1, "fR|\\fP")) return(DELIM_MIDDLE); return(DELIM_NONE); } void mdoc_deroff(char **dest, const struct mdoc_node *n) { char *cp; size_t sz; if (MDOC_TEXT != n->type) { for (n = n->child; n; n = n->next) mdoc_deroff(dest, n); return; } /* Skip leading whitespace. */ for (cp = n->string; '\0' != *cp; cp++) if (0 == isspace((unsigned char)*cp)) break; /* Skip trailing whitespace. */ for (sz = strlen(cp); sz; sz--) if (0 == isspace((unsigned char)cp[sz-1])) break; /* Skip empty strings. */ if (0 == sz) return; if (NULL == *dest) { *dest = mandoc_strndup(cp, sz); return; } mandoc_asprintf(&cp, "%s %*s", *dest, (int)sz, cp); free(*dest); *dest = cp; } Index: vendor/mdocml/dist/mdoc_argv.c =================================================================== --- vendor/mdocml/dist/mdoc_argv.c (revision 275396) +++ vendor/mdocml/dist/mdoc_argv.c (revision 275397) @@ -1,701 +1,691 @@ -/* $Id: mdoc_argv.c,v 1.95 2014/07/06 19:09:00 schwarze Exp $ */ +/* $OpenBSD$ */ /* * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons - * Copyright (c) 2012 Ingo Schwarze + * Copyright (c) 2012, 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include #include #include #include #include #include "mdoc.h" #include "mandoc.h" #include "mandoc_aux.h" #include "libmdoc.h" #include "libmandoc.h" #define MULTI_STEP 5 /* pre-allocate argument values */ #define DELIMSZ 6 /* max possible size of a delimiter */ enum argsflag { ARGSFL_NONE = 0, ARGSFL_DELIM, /* handle delimiters of [[::delim::][ ]+]+ */ ARGSFL_TABSEP /* handle tab/`Ta' separated phrases */ }; 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) */ }; struct mdocarg { enum argsflag flags; const enum mdocargt *argvs; }; static void argn_free(struct mdoc_arg *, int); static enum margserr args(struct mdoc *, int, int *, char *, enum argsflag, char **); static int args_checkpunct(const char *, int); -static int argv_multi(struct mdoc *, int, +static void argv_multi(struct mdoc *, int, struct mdoc_argv *, int *, char *); -static int argv_single(struct mdoc *, int, +static void argv_single(struct mdoc *, int, struct mdoc_argv *, int *, char *); static const enum argvflag argvflags[MDOC_ARG_MAX] = { ARGV_NONE, /* MDOC_Split */ ARGV_NONE, /* MDOC_Nosplit */ ARGV_NONE, /* MDOC_Ragged */ ARGV_NONE, /* MDOC_Unfilled */ ARGV_NONE, /* MDOC_Literal */ ARGV_SINGLE, /* MDOC_File */ ARGV_SINGLE, /* MDOC_Offset */ ARGV_NONE, /* MDOC_Bullet */ ARGV_NONE, /* MDOC_Dash */ ARGV_NONE, /* MDOC_Hyphen */ ARGV_NONE, /* MDOC_Item */ ARGV_NONE, /* MDOC_Enum */ ARGV_NONE, /* MDOC_Tag */ ARGV_NONE, /* MDOC_Diag */ ARGV_NONE, /* MDOC_Hang */ ARGV_NONE, /* MDOC_Ohang */ ARGV_NONE, /* MDOC_Inset */ ARGV_MULTI, /* MDOC_Column */ ARGV_SINGLE, /* MDOC_Width */ ARGV_NONE, /* MDOC_Compact */ ARGV_NONE, /* MDOC_Std */ ARGV_NONE, /* MDOC_Filled */ ARGV_NONE, /* MDOC_Words */ ARGV_NONE, /* MDOC_Emphasis */ ARGV_NONE, /* MDOC_Symbolic */ ARGV_NONE /* MDOC_Symbolic */ }; static const enum mdocargt args_Ex[] = { MDOC_Std, MDOC_ARG_MAX }; static const enum mdocargt args_An[] = { MDOC_Split, MDOC_Nosplit, MDOC_ARG_MAX }; static const enum mdocargt args_Bd[] = { MDOC_Ragged, MDOC_Unfilled, MDOC_Filled, MDOC_Literal, MDOC_File, MDOC_Offset, MDOC_Compact, MDOC_Centred, MDOC_ARG_MAX }; static const enum mdocargt args_Bf[] = { MDOC_Emphasis, MDOC_Literal, MDOC_Symbolic, MDOC_ARG_MAX }; static const enum mdocargt args_Bk[] = { MDOC_Words, MDOC_ARG_MAX }; static const enum mdocargt args_Bl[] = { MDOC_Bullet, MDOC_Dash, MDOC_Hyphen, MDOC_Item, MDOC_Enum, MDOC_Tag, MDOC_Diag, MDOC_Hang, MDOC_Ohang, MDOC_Inset, MDOC_Column, MDOC_Width, MDOC_Offset, MDOC_Compact, MDOC_Nested, MDOC_ARG_MAX }; static const struct mdocarg mdocargs[MDOC_MAX] = { { ARGSFL_DELIM, NULL }, /* Ap */ { ARGSFL_NONE, NULL }, /* Dd */ { ARGSFL_NONE, NULL }, /* Dt */ { ARGSFL_NONE, NULL }, /* Os */ { ARGSFL_NONE, NULL }, /* Sh */ { ARGSFL_NONE, NULL }, /* Ss */ { ARGSFL_NONE, NULL }, /* Pp */ { ARGSFL_DELIM, NULL }, /* D1 */ { ARGSFL_DELIM, NULL }, /* Dl */ { ARGSFL_NONE, args_Bd }, /* Bd */ { ARGSFL_NONE, NULL }, /* Ed */ { ARGSFL_NONE, args_Bl }, /* Bl */ { ARGSFL_NONE, NULL }, /* El */ { ARGSFL_NONE, NULL }, /* It */ { ARGSFL_DELIM, NULL }, /* Ad */ { ARGSFL_DELIM, args_An }, /* An */ { ARGSFL_DELIM, NULL }, /* Ar */ { ARGSFL_DELIM, NULL }, /* Cd */ { ARGSFL_DELIM, NULL }, /* Cm */ { ARGSFL_DELIM, NULL }, /* Dv */ { ARGSFL_DELIM, NULL }, /* Er */ { ARGSFL_DELIM, NULL }, /* Ev */ { ARGSFL_NONE, args_Ex }, /* Ex */ { ARGSFL_DELIM, NULL }, /* Fa */ { ARGSFL_NONE, NULL }, /* Fd */ { ARGSFL_DELIM, NULL }, /* Fl */ { ARGSFL_DELIM, NULL }, /* Fn */ { ARGSFL_DELIM, NULL }, /* Ft */ { ARGSFL_DELIM, NULL }, /* Ic */ { ARGSFL_DELIM, NULL }, /* In */ { ARGSFL_DELIM, NULL }, /* Li */ { ARGSFL_NONE, NULL }, /* Nd */ { ARGSFL_DELIM, NULL }, /* Nm */ { ARGSFL_DELIM, NULL }, /* Op */ { ARGSFL_DELIM, NULL }, /* Ot */ { ARGSFL_DELIM, NULL }, /* Pa */ { ARGSFL_NONE, args_Ex }, /* Rv */ { ARGSFL_DELIM, NULL }, /* St */ { ARGSFL_DELIM, NULL }, /* Va */ { ARGSFL_DELIM, NULL }, /* Vt */ { ARGSFL_DELIM, NULL }, /* Xr */ { ARGSFL_NONE, NULL }, /* %A */ { ARGSFL_NONE, NULL }, /* %B */ { ARGSFL_NONE, NULL }, /* %D */ { ARGSFL_NONE, NULL }, /* %I */ { ARGSFL_NONE, NULL }, /* %J */ { ARGSFL_NONE, NULL }, /* %N */ { ARGSFL_NONE, NULL }, /* %O */ { ARGSFL_NONE, NULL }, /* %P */ { ARGSFL_NONE, NULL }, /* %R */ { ARGSFL_NONE, NULL }, /* %T */ { ARGSFL_NONE, NULL }, /* %V */ { ARGSFL_DELIM, NULL }, /* Ac */ { ARGSFL_NONE, NULL }, /* Ao */ { ARGSFL_DELIM, NULL }, /* Aq */ { ARGSFL_DELIM, NULL }, /* At */ { ARGSFL_DELIM, NULL }, /* Bc */ { ARGSFL_NONE, args_Bf }, /* Bf */ { ARGSFL_NONE, NULL }, /* Bo */ { ARGSFL_DELIM, NULL }, /* Bq */ { ARGSFL_DELIM, NULL }, /* Bsx */ { ARGSFL_DELIM, NULL }, /* Bx */ { ARGSFL_NONE, NULL }, /* Db */ { ARGSFL_DELIM, NULL }, /* Dc */ { ARGSFL_NONE, NULL }, /* Do */ { ARGSFL_DELIM, NULL }, /* Dq */ { ARGSFL_DELIM, NULL }, /* Ec */ { ARGSFL_NONE, NULL }, /* Ef */ { ARGSFL_DELIM, NULL }, /* Em */ { ARGSFL_NONE, NULL }, /* Eo */ { ARGSFL_DELIM, NULL }, /* Fx */ { ARGSFL_DELIM, NULL }, /* Ms */ { ARGSFL_DELIM, NULL }, /* No */ { ARGSFL_DELIM, NULL }, /* Ns */ { ARGSFL_DELIM, NULL }, /* Nx */ { ARGSFL_DELIM, NULL }, /* Ox */ { ARGSFL_DELIM, NULL }, /* Pc */ { ARGSFL_DELIM, NULL }, /* Pf */ { ARGSFL_NONE, NULL }, /* Po */ { ARGSFL_DELIM, NULL }, /* Pq */ { ARGSFL_DELIM, NULL }, /* Qc */ { ARGSFL_DELIM, NULL }, /* Ql */ { ARGSFL_NONE, NULL }, /* Qo */ { ARGSFL_DELIM, NULL }, /* Qq */ { ARGSFL_NONE, NULL }, /* Re */ { ARGSFL_NONE, NULL }, /* Rs */ { ARGSFL_DELIM, NULL }, /* Sc */ { ARGSFL_NONE, NULL }, /* So */ { ARGSFL_DELIM, NULL }, /* Sq */ { ARGSFL_NONE, NULL }, /* Sm */ { ARGSFL_DELIM, NULL }, /* Sx */ { ARGSFL_DELIM, NULL }, /* Sy */ { ARGSFL_DELIM, NULL }, /* Tn */ { ARGSFL_DELIM, NULL }, /* Ux */ { ARGSFL_DELIM, NULL }, /* Xc */ { ARGSFL_NONE, NULL }, /* Xo */ { ARGSFL_NONE, NULL }, /* Fo */ { ARGSFL_DELIM, NULL }, /* Fc */ { ARGSFL_NONE, NULL }, /* Oo */ { ARGSFL_DELIM, NULL }, /* Oc */ { ARGSFL_NONE, args_Bk }, /* Bk */ { ARGSFL_NONE, NULL }, /* Ek */ { ARGSFL_NONE, NULL }, /* Bt */ { ARGSFL_NONE, NULL }, /* Hf */ { ARGSFL_DELIM, NULL }, /* Fr */ { ARGSFL_NONE, NULL }, /* Ud */ { ARGSFL_DELIM, NULL }, /* Lb */ { ARGSFL_NONE, NULL }, /* Lp */ { ARGSFL_DELIM, NULL }, /* Lk */ { ARGSFL_DELIM, NULL }, /* Mt */ { ARGSFL_DELIM, NULL }, /* Brq */ { ARGSFL_NONE, NULL }, /* Bro */ { ARGSFL_DELIM, NULL }, /* Brc */ { ARGSFL_NONE, NULL }, /* %C */ { ARGSFL_NONE, NULL }, /* Es */ { ARGSFL_DELIM, NULL }, /* En */ { ARGSFL_DELIM, NULL }, /* Dx */ { ARGSFL_NONE, NULL }, /* %Q */ { ARGSFL_NONE, NULL }, /* br */ { ARGSFL_NONE, NULL }, /* sp */ { ARGSFL_NONE, NULL }, /* %U */ { ARGSFL_NONE, NULL }, /* Ta */ { ARGSFL_NONE, NULL }, /* ll */ }; /* - * Parse an argument from line text. This comes in the form of -key - * [value0...], which may either have a single mandatory value, at least - * one mandatory value, an optional single value, or no value. + * Parse flags and their arguments from the input line. + * These come in the form -flag [argument ...]. + * Some flags take no argument, some one, some multiple. */ -enum margverr +void mdoc_argv(struct mdoc *mdoc, int line, enum mdoct tok, - struct mdoc_arg **v, int *pos, char *buf) + struct mdoc_arg **reta, int *pos, char *buf) { - char *p, sv; - struct mdoc_argv tmp; - struct mdoc_arg *arg; - const enum mdocargt *ap; + struct mdoc_argv tmpv; + struct mdoc_argv **retv; + const enum mdocargt *argtable; + char *argname; + int ipos, retc; + char savechar; - if ('\0' == buf[*pos]) - return(ARGV_EOLN); - else if (NULL == (ap = mdocargs[tok].argvs)) - return(ARGV_WORD); - else if ('-' != buf[*pos]) - return(ARGV_WORD); + *reta = NULL; - /* Seek to the first unescaped space. */ + /* Which flags does this macro support? */ - p = &buf[++(*pos)]; + argtable = mdocargs[tok].argvs; + if (argtable == NULL) + return; - assert(*pos > 0); + /* Loop over the flags on the input line. */ - for ( ; buf[*pos] ; (*pos)++) - if (' ' == buf[*pos] && '\\' != buf[*pos - 1]) - break; + ipos = *pos; + while (buf[ipos] == '-') { - /* - * We want to nil-terminate the word to look it up (it's easier - * that way). But we may not have a flag, in which case we need - * to restore the line as-is. So keep around the stray byte, - * which we'll reset upon exiting (if necessary). - */ + /* Seek to the first unescaped space. */ - if ('\0' != (sv = buf[*pos])) - buf[(*pos)++] = '\0'; + for (argname = buf + ++ipos; buf[ipos] != '\0'; ipos++) + if (buf[ipos] == ' ' && buf[ipos - 1] != '\\') + break; - /* - * Now look up the word as a flag. Use temporary storage that - * we'll copy into the node's flags, if necessary. - */ + /* + * We want to nil-terminate the word to look it up. + * But we may not have a flag, in which case we need + * to restore the line as-is. So keep around the + * stray byte, which we'll reset upon exiting. + */ - memset(&tmp, 0, sizeof(struct mdoc_argv)); + if ((savechar = buf[ipos]) != '\0') + buf[ipos++] = '\0'; - tmp.line = line; - tmp.pos = *pos; - tmp.arg = MDOC_ARG_MAX; + /* + * Now look up the word as a flag. Use temporary + * storage that we'll copy into the node's flags. + */ - while (MDOC_ARG_MAX != (tmp.arg = *ap++)) - if (0 == strcmp(p, mdoc_argnames[tmp.arg])) + while ((tmpv.arg = *argtable++) != MDOC_ARG_MAX) + if ( ! strcmp(argname, mdoc_argnames[tmpv.arg])) + break; + + /* If it isn't a flag, restore the saved byte. */ + + if (tmpv.arg == MDOC_ARG_MAX) { + if (savechar != '\0') + buf[ipos - 1] = savechar; break; + } - if (MDOC_ARG_MAX == tmp.arg) { - /* - * The flag was not found. - * Restore saved zeroed byte and return as a word. - */ - if (sv) - buf[*pos - 1] = sv; - return(ARGV_WORD); - } + /* Read to the next word (the first argument). */ - /* Read to the next word (the argument). */ + while (buf[ipos] == ' ') + ipos++; - while (buf[*pos] && ' ' == buf[*pos]) - (*pos)++; + /* Parse the arguments of the flag. */ - switch (argvflags[tmp.arg]) { - case ARGV_SINGLE: - if ( ! argv_single(mdoc, line, &tmp, pos, buf)) - return(ARGV_ERROR); - break; - case ARGV_MULTI: - if ( ! argv_multi(mdoc, line, &tmp, pos, buf)) - return(ARGV_ERROR); - break; - case ARGV_NONE: - break; - } + tmpv.line = line; + tmpv.pos = ipos; + tmpv.sz = 0; + tmpv.value = NULL; - if (NULL == (arg = *v)) - arg = *v = mandoc_calloc(1, sizeof(struct mdoc_arg)); + switch (argvflags[tmpv.arg]) { + case ARGV_SINGLE: + argv_single(mdoc, line, &tmpv, &ipos, buf); + break; + case ARGV_MULTI: + argv_multi(mdoc, line, &tmpv, &ipos, buf); + break; + case ARGV_NONE: + break; + } - arg->argc++; - arg->argv = mandoc_reallocarray(arg->argv, - arg->argc, sizeof(struct mdoc_argv)); + /* Append to the return values. */ - memcpy(&arg->argv[(int)arg->argc - 1], &tmp, - sizeof(struct mdoc_argv)); + if (*reta == NULL) + *reta = mandoc_calloc(1, sizeof(**reta)); - return(ARGV_ARG); + retc = ++(*reta)->argc; + retv = &(*reta)->argv; + *retv = mandoc_reallocarray(*retv, retc, sizeof(**retv)); + memcpy(*retv + retc - 1, &tmpv, sizeof(**retv)); + + /* Prepare for parsing the next flag. */ + + *pos = ipos; + argtable = mdocargs[tok].argvs; + } } void mdoc_argv_free(struct mdoc_arg *p) { int i; if (NULL == p) return; if (p->refcnt) { --(p->refcnt); if (p->refcnt) return; } assert(p->argc); for (i = (int)p->argc - 1; i >= 0; i--) argn_free(p, i); free(p->argv); free(p); } static void argn_free(struct mdoc_arg *p, int iarg) { struct mdoc_argv *arg; int j; arg = &p->argv[iarg]; if (arg->sz && arg->value) { for (j = (int)arg->sz - 1; j >= 0; j--) free(arg->value[j]); free(arg->value); } for (--p->argc; iarg < (int)p->argc; iarg++) p->argv[iarg] = p->argv[iarg+1]; } enum margserr -mdoc_zargs(struct mdoc *mdoc, int line, int *pos, char *buf, char **v) -{ - - return(args(mdoc, line, pos, buf, ARGSFL_NONE, v)); -} - -enum margserr mdoc_args(struct mdoc *mdoc, int line, int *pos, char *buf, enum mdoct tok, char **v) { - enum argsflag fl; struct mdoc_node *n; + char *v_local; + enum argsflag fl; - fl = mdocargs[tok].flags; - - if (MDOC_It != tok) + if (v == NULL) + v = &v_local; + fl = tok == MDOC_MAX ? ARGSFL_NONE : mdocargs[tok].flags; + if (tok != MDOC_It) return(args(mdoc, line, pos, buf, fl, v)); /* * We know that we're in an `It', so it's reasonable to expect * us to be sitting in a `Bl'. Someday this may not be the case * (if we allow random `It's sitting out there), so provide a * safe fall-back into the default behaviour. */ for (n = mdoc->last; n; n = n->parent) if (MDOC_Bl == n->tok) if (LIST_column == n->norm->Bl.type) { fl = ARGSFL_TABSEP; break; } return(args(mdoc, line, pos, buf, fl, v)); } static enum margserr 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 & mdoc->flags) return(ARGS_EOLN); /* * If we're not in a partial phrase and the flag for * being a phrase literal is still set, the punctuation * is unterminated. */ if (MDOC_PHRASELIT & mdoc->flags) mandoc_msg(MANDOCERR_ARG_QUOTE, mdoc->parse, line, *pos, NULL); mdoc->flags &= ~MDOC_PHRASELIT; return(ARGS_EOLN); } *v = &buf[*pos]; if (ARGSFL_DELIM == fl) if (args_checkpunct(buf, *pos)) return(ARGS_PUNCT); /* * First handle TABSEP items, restricted to `Bl -column'. This * ignores conventional token parsing and instead uses tabs or * `Ta' macros to separate phrases. Phrases are parsed again * for arguments at a later phase. */ if (ARGSFL_TABSEP == fl) { /* Scan ahead to tab (can't be escaped). */ p = strchr(*v, '\t'); pp = NULL; /* Scan ahead to unescaped `Ta'. */ if ( ! (MDOC_PHRASELIT & mdoc->flags)) for (pp = *v; ; pp++) { if (NULL == (pp = strstr(pp, "Ta"))) break; if (pp > *v && ' ' != *(pp - 1)) continue; if (' ' == *(pp + 2) || '\0' == *(pp + 2)) break; } /* By default, assume a phrase. */ rc = ARGS_PHRASE; /* * Adjust new-buffer position to be beyond delimiter * mark (e.g., Ta -> end + 2). */ if (p && pp) { *pos += pp < p ? 2 : 1; rc = pp < p ? ARGS_PHRASE : ARGS_PPHRASE; p = pp < p ? pp : p; } else if (p && ! pp) { rc = ARGS_PPHRASE; *pos += 1; } else if (pp && ! p) { p = pp; *pos += 2; } else { rc = ARGS_PEND; p = strchr(*v, 0); } /* Whitespace check for eoln case... */ if ('\0' == *p && ' ' == *(p - 1)) mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, line, *pos, NULL); *pos += (int)(p - *v); /* Strip delimiter's preceding whitespace. */ pp = p - 1; while (pp > *v && ' ' == *pp) { if (pp > *v && '\\' == *(pp - 1)) break; pp--; } *(pp + 1) = 0; /* Strip delimiter's proceeding whitespace. */ for (pp = &buf[*pos]; ' ' == *pp; pp++, (*pos)++) /* Skip ahead. */ ; return(rc); } /* * Process a quoted literal. A quote begins with a double-quote * and ends with a double-quote NOT preceded by a double-quote. * NUL-terminate the literal in place. * Collapse pairs of quotes inside quoted literals. * Whitespace is NOT involved in literal termination. */ if (MDOC_PHRASELIT & mdoc->flags || '\"' == buf[*pos]) { if ( ! (MDOC_PHRASELIT & mdoc->flags)) *v = &buf[++(*pos)]; 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 & mdoc->flags) return(ARGS_QWORD); mandoc_msg(MANDOCERR_ARG_QUOTE, mdoc->parse, line, *pos, NULL); return(ARGS_QWORD); } mdoc->flags &= ~MDOC_PHRASELIT; buf[(*pos)++] = '\0'; if ('\0' == buf[*pos]) return(ARGS_QWORD); while (' ' == buf[*pos]) (*pos)++; if ('\0' == buf[*pos]) mandoc_msg(MANDOCERR_SPACE_EOL, mdoc->parse, line, *pos, NULL); return(ARGS_QWORD); } p = &buf[*pos]; *v = mandoc_getarg(mdoc->parse, &p, line, pos); return(ARGS_WORD); } /* * Check if the string consists only of space-separated closing * delimiters. This is a bit of a dance: the first must be a close * delimiter, but it may be followed by middle delimiters. Arbitrary * whitespace may separate these tokens. */ static int args_checkpunct(const char *buf, int i) { int j; char dbuf[DELIMSZ]; enum mdelim d; /* First token must be a close-delimiter. */ for (j = 0; buf[i] && ' ' != buf[i] && j < DELIMSZ; j++, i++) dbuf[j] = buf[i]; if (DELIMSZ == j) return(0); dbuf[j] = '\0'; if (DELIM_CLOSE != mdoc_isdelim(dbuf)) return(0); while (' ' == buf[i]) i++; /* Remaining must NOT be open/none. */ while (buf[i]) { j = 0; while (buf[i] && ' ' != buf[i] && j < DELIMSZ) dbuf[j++] = buf[i++]; if (DELIMSZ == j) return(0); dbuf[j] = '\0'; d = mdoc_isdelim(dbuf); if (DELIM_NONE == d || DELIM_OPEN == d) return(0); while (' ' == buf[i]) i++; } return('\0' == buf[i]); } -static int +static void argv_multi(struct mdoc *mdoc, int line, struct mdoc_argv *v, int *pos, char *buf) { enum margserr ac; char *p; for (v->sz = 0; ; v->sz++) { - if ('-' == buf[*pos]) + if (buf[*pos] == '-') break; ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p); - if (ARGS_ERROR == ac) - return(0); - else if (ARGS_EOLN == ac) + if (ac == ARGS_EOLN) break; - if (0 == v->sz % MULTI_STEP) + if (v->sz % MULTI_STEP == 0) v->value = mandoc_reallocarray(v->value, v->sz + MULTI_STEP, sizeof(char *)); v->value[(int)v->sz] = mandoc_strdup(p); } - - return(1); } -static int +static void argv_single(struct mdoc *mdoc, int line, struct mdoc_argv *v, int *pos, char *buf) { enum margserr ac; char *p; ac = args(mdoc, line, pos, buf, ARGSFL_NONE, &p); - if (ARGS_ERROR == ac) - return(0); - if (ARGS_EOLN == ac) - return(1); + if (ac == ARGS_EOLN) + return; v->sz = 1; v->value = mandoc_malloc(sizeof(char *)); v->value[0] = mandoc_strdup(p); - - return(1); } Index: vendor/mdocml/dist/mdoc_hash.c =================================================================== --- vendor/mdocml/dist/mdoc_hash.c (revision 275396) +++ vendor/mdocml/dist/mdoc_hash.c (revision 275397) @@ -1,94 +1,92 @@ -/* $Id: mdoc_hash.c,v 1.20 2014/04/20 16:46:05 schwarze Exp $ */ +/* $Id: mdoc_hash.c,v 1.21 2014/08/10 23:54:41 schwarze Exp $ */ /* * Copyright (c) 2008, 2009 Kristaps Dzonsons * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include #include #include #include #include #include #include #include "mdoc.h" #include "libmdoc.h" static unsigned char table[27 * 12]; /* * XXX - this hash has global scope, so if intended for use as a library * with multiple callers, it will need re-invocation protection. */ void mdoc_hash_init(void) { int i, j, major; const char *p; memset(table, UCHAR_MAX, sizeof(table)); for (i = 0; i < (int)MDOC_MAX; i++) { p = mdoc_macronames[i]; if (isalpha((unsigned char)p[1])) major = 12 * (tolower((unsigned char)p[1]) - 97); else major = 12 * 26; for (j = 0; j < 12; j++) if (UCHAR_MAX == table[major + j]) { table[major + j] = (unsigned char)i; break; } assert(j < 12); } } enum mdoct mdoc_hash_find(const char *p) { int major, i, j; if (0 == p[0]) return(MDOC_MAX); if ( ! isalpha((unsigned char)p[0]) && '%' != p[0]) return(MDOC_MAX); if (isalpha((unsigned char)p[1])) major = 12 * (tolower((unsigned char)p[1]) - 97); else if ('1' == p[1]) major = 12 * 26; else return(MDOC_MAX); if (p[2] && p[3]) return(MDOC_MAX); for (j = 0; j < 12; j++) { if (UCHAR_MAX == (i = table[major + j])) break; if (0 == strcmp(p, mdoc_macronames[i])) return((enum mdoct)i); } return(MDOC_MAX); } Index: vendor/mdocml/dist/mdoc_html.c =================================================================== --- vendor/mdocml/dist/mdoc_html.c (revision 275396) +++ vendor/mdocml/dist/mdoc_html.c (revision 275397) @@ -1,2199 +1,2212 @@ -/* $Id: mdoc_html.c,v 1.195 2014/08/06 15:09:05 schwarze Exp $ */ +/* $Id: mdoc_html.c,v 1.213 2014/11/27 22:27:56 schwarze Exp $ */ /* - * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons + * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons * Copyright (c) 2014 Ingo Schwarze * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif #include #include #include #include #include #include #include #include "mandoc.h" #include "mandoc_aux.h" #include "out.h" #include "html.h" #include "mdoc.h" #include "main.h" #define INDENT 5 #define MDOC_ARGS const struct mdoc_meta *meta, \ const struct mdoc_node *n, \ struct html *h #ifndef MIN #define MIN(a,b) ((/*CONSTCOND*/(a)<(b))?(a):(b)) #endif struct htmlmdoc { int (*pre)(MDOC_ARGS); void (*post)(MDOC_ARGS); }; static void print_mdoc(MDOC_ARGS); static void print_mdoc_head(MDOC_ARGS); static void print_mdoc_node(MDOC_ARGS); static void print_mdoc_nodelist(MDOC_ARGS); static void synopsis_pre(struct html *, const struct mdoc_node *); static void a2width(const char *, struct roffsu *); -static void a2offs(const char *, struct roffsu *); static void mdoc_root_post(MDOC_ARGS); static int mdoc_root_pre(MDOC_ARGS); static void mdoc__x_post(MDOC_ARGS); static int mdoc__x_pre(MDOC_ARGS); static int mdoc_ad_pre(MDOC_ARGS); static int mdoc_an_pre(MDOC_ARGS); static int mdoc_ap_pre(MDOC_ARGS); static int mdoc_ar_pre(MDOC_ARGS); static int mdoc_bd_pre(MDOC_ARGS); static int mdoc_bf_pre(MDOC_ARGS); static void mdoc_bk_post(MDOC_ARGS); static int mdoc_bk_pre(MDOC_ARGS); static int mdoc_bl_pre(MDOC_ARGS); static int mdoc_bt_pre(MDOC_ARGS); static int mdoc_bx_pre(MDOC_ARGS); static int mdoc_cd_pre(MDOC_ARGS); static int mdoc_d1_pre(MDOC_ARGS); static int mdoc_dv_pre(MDOC_ARGS); static int mdoc_fa_pre(MDOC_ARGS); static int mdoc_fd_pre(MDOC_ARGS); static int mdoc_fl_pre(MDOC_ARGS); static int mdoc_fn_pre(MDOC_ARGS); static int mdoc_ft_pre(MDOC_ARGS); static int mdoc_em_pre(MDOC_ARGS); static int mdoc_er_pre(MDOC_ARGS); static int mdoc_ev_pre(MDOC_ARGS); static int mdoc_ex_pre(MDOC_ARGS); static void mdoc_fo_post(MDOC_ARGS); static int mdoc_fo_pre(MDOC_ARGS); static int mdoc_ic_pre(MDOC_ARGS); static int mdoc_igndelim_pre(MDOC_ARGS); static int mdoc_in_pre(MDOC_ARGS); static int mdoc_it_pre(MDOC_ARGS); static int mdoc_lb_pre(MDOC_ARGS); static int mdoc_li_pre(MDOC_ARGS); static int mdoc_lk_pre(MDOC_ARGS); static int mdoc_mt_pre(MDOC_ARGS); static int mdoc_ms_pre(MDOC_ARGS); static int mdoc_nd_pre(MDOC_ARGS); static int mdoc_nm_pre(MDOC_ARGS); +static int mdoc_no_pre(MDOC_ARGS); static int mdoc_ns_pre(MDOC_ARGS); static int mdoc_pa_pre(MDOC_ARGS); static void mdoc_pf_post(MDOC_ARGS); static int mdoc_pp_pre(MDOC_ARGS); static void mdoc_quote_post(MDOC_ARGS); static int mdoc_quote_pre(MDOC_ARGS); static int mdoc_rs_pre(MDOC_ARGS); static int mdoc_rv_pre(MDOC_ARGS); static int mdoc_sh_pre(MDOC_ARGS); static int mdoc_skip_pre(MDOC_ARGS); static int mdoc_sm_pre(MDOC_ARGS); static int mdoc_sp_pre(MDOC_ARGS); static int mdoc_ss_pre(MDOC_ARGS); static int mdoc_sx_pre(MDOC_ARGS); static int mdoc_sy_pre(MDOC_ARGS); static int mdoc_ud_pre(MDOC_ARGS); static int mdoc_va_pre(MDOC_ARGS); static int mdoc_vt_pre(MDOC_ARGS); static int mdoc_xr_pre(MDOC_ARGS); static int mdoc_xx_pre(MDOC_ARGS); static const struct htmlmdoc mdocs[MDOC_MAX] = { {mdoc_ap_pre, NULL}, /* Ap */ {NULL, NULL}, /* Dd */ {NULL, NULL}, /* Dt */ {NULL, NULL}, /* Os */ {mdoc_sh_pre, NULL }, /* Sh */ {mdoc_ss_pre, NULL }, /* Ss */ {mdoc_pp_pre, NULL}, /* Pp */ {mdoc_d1_pre, NULL}, /* D1 */ {mdoc_d1_pre, NULL}, /* Dl */ {mdoc_bd_pre, NULL}, /* Bd */ {NULL, NULL}, /* Ed */ {mdoc_bl_pre, NULL}, /* Bl */ {NULL, NULL}, /* El */ {mdoc_it_pre, NULL}, /* It */ {mdoc_ad_pre, NULL}, /* Ad */ {mdoc_an_pre, NULL}, /* An */ {mdoc_ar_pre, NULL}, /* Ar */ {mdoc_cd_pre, NULL}, /* Cd */ {mdoc_fl_pre, NULL}, /* Cm */ {mdoc_dv_pre, NULL}, /* Dv */ {mdoc_er_pre, NULL}, /* Er */ {mdoc_ev_pre, NULL}, /* Ev */ {mdoc_ex_pre, NULL}, /* Ex */ {mdoc_fa_pre, NULL}, /* Fa */ {mdoc_fd_pre, NULL}, /* Fd */ {mdoc_fl_pre, NULL}, /* Fl */ {mdoc_fn_pre, NULL}, /* Fn */ {mdoc_ft_pre, NULL}, /* Ft */ {mdoc_ic_pre, NULL}, /* Ic */ {mdoc_in_pre, NULL}, /* In */ {mdoc_li_pre, NULL}, /* Li */ {mdoc_nd_pre, NULL}, /* Nd */ {mdoc_nm_pre, NULL}, /* Nm */ {mdoc_quote_pre, mdoc_quote_post}, /* Op */ {mdoc_ft_pre, NULL}, /* Ot */ {mdoc_pa_pre, NULL}, /* Pa */ {mdoc_rv_pre, NULL}, /* Rv */ {NULL, NULL}, /* St */ {mdoc_va_pre, NULL}, /* Va */ {mdoc_vt_pre, NULL}, /* Vt */ {mdoc_xr_pre, NULL}, /* Xr */ {mdoc__x_pre, mdoc__x_post}, /* %A */ {mdoc__x_pre, mdoc__x_post}, /* %B */ {mdoc__x_pre, mdoc__x_post}, /* %D */ {mdoc__x_pre, mdoc__x_post}, /* %I */ {mdoc__x_pre, mdoc__x_post}, /* %J */ {mdoc__x_pre, mdoc__x_post}, /* %N */ {mdoc__x_pre, mdoc__x_post}, /* %O */ {mdoc__x_pre, mdoc__x_post}, /* %P */ {mdoc__x_pre, mdoc__x_post}, /* %R */ {mdoc__x_pre, mdoc__x_post}, /* %T */ {mdoc__x_pre, mdoc__x_post}, /* %V */ {NULL, NULL}, /* Ac */ {mdoc_quote_pre, mdoc_quote_post}, /* Ao */ {mdoc_quote_pre, mdoc_quote_post}, /* Aq */ {NULL, NULL}, /* At */ {NULL, NULL}, /* Bc */ {mdoc_bf_pre, NULL}, /* Bf */ {mdoc_quote_pre, mdoc_quote_post}, /* Bo */ {mdoc_quote_pre, mdoc_quote_post}, /* Bq */ {mdoc_xx_pre, NULL}, /* Bsx */ {mdoc_bx_pre, NULL}, /* Bx */ - {NULL, NULL}, /* Db */ + {mdoc_skip_pre, NULL}, /* Db */ {NULL, NULL}, /* Dc */ {mdoc_quote_pre, mdoc_quote_post}, /* Do */ {mdoc_quote_pre, mdoc_quote_post}, /* Dq */ {NULL, NULL}, /* Ec */ /* FIXME: no space */ {NULL, NULL}, /* Ef */ {mdoc_em_pre, NULL}, /* Em */ {mdoc_quote_pre, mdoc_quote_post}, /* Eo */ {mdoc_xx_pre, NULL}, /* Fx */ {mdoc_ms_pre, NULL}, /* Ms */ - {mdoc_igndelim_pre, NULL}, /* No */ + {mdoc_no_pre, NULL}, /* No */ {mdoc_ns_pre, NULL}, /* Ns */ {mdoc_xx_pre, NULL}, /* Nx */ {mdoc_xx_pre, NULL}, /* Ox */ {NULL, NULL}, /* Pc */ {mdoc_igndelim_pre, mdoc_pf_post}, /* Pf */ {mdoc_quote_pre, mdoc_quote_post}, /* Po */ {mdoc_quote_pre, mdoc_quote_post}, /* Pq */ {NULL, NULL}, /* Qc */ {mdoc_quote_pre, mdoc_quote_post}, /* Ql */ {mdoc_quote_pre, mdoc_quote_post}, /* Qo */ {mdoc_quote_pre, mdoc_quote_post}, /* Qq */ {NULL, NULL}, /* Re */ {mdoc_rs_pre, NULL}, /* Rs */ {NULL, NULL}, /* Sc */ {mdoc_quote_pre, mdoc_quote_post}, /* So */ {mdoc_quote_pre, mdoc_quote_post}, /* Sq */ {mdoc_sm_pre, NULL}, /* Sm */ {mdoc_sx_pre, NULL}, /* Sx */ {mdoc_sy_pre, NULL}, /* Sy */ {NULL, NULL}, /* Tn */ {mdoc_xx_pre, NULL}, /* Ux */ {NULL, NULL}, /* Xc */ {NULL, NULL}, /* Xo */ {mdoc_fo_pre, mdoc_fo_post}, /* Fo */ {NULL, NULL}, /* Fc */ {mdoc_quote_pre, mdoc_quote_post}, /* Oo */ {NULL, NULL}, /* Oc */ {mdoc_bk_pre, mdoc_bk_post}, /* Bk */ {NULL, NULL}, /* Ek */ {mdoc_bt_pre, NULL}, /* Bt */ {NULL, NULL}, /* Hf */ {mdoc_em_pre, NULL}, /* Fr */ {mdoc_ud_pre, NULL}, /* Ud */ {mdoc_lb_pre, NULL}, /* Lb */ {mdoc_pp_pre, NULL}, /* Lp */ {mdoc_lk_pre, NULL}, /* Lk */ {mdoc_mt_pre, NULL}, /* Mt */ {mdoc_quote_pre, mdoc_quote_post}, /* Brq */ {mdoc_quote_pre, mdoc_quote_post}, /* Bro */ {NULL, NULL}, /* Brc */ {mdoc__x_pre, mdoc__x_post}, /* %C */ {mdoc_skip_pre, NULL}, /* Es */ {mdoc_quote_pre, mdoc_quote_post}, /* En */ {mdoc_xx_pre, NULL}, /* Dx */ {mdoc__x_pre, mdoc__x_post}, /* %Q */ {mdoc_sp_pre, NULL}, /* br */ {mdoc_sp_pre, NULL}, /* sp */ {mdoc__x_pre, mdoc__x_post}, /* %U */ {NULL, NULL}, /* Ta */ {mdoc_skip_pre, NULL}, /* ll */ }; static const char * const lists[LIST_MAX] = { NULL, "list-bul", "list-col", "list-dash", "list-diag", "list-enum", "list-hang", "list-hyph", "list-inset", "list-item", "list-ohang", "list-tag" }; void html_mdoc(void *arg, const struct mdoc *mdoc) { print_mdoc(mdoc_meta(mdoc), mdoc_node(mdoc), (struct html *)arg); putchar('\n'); } /* * Calculate the scaling unit passed in a `-width' argument. This uses * either a native scaling unit (e.g., 1i, 2m) or the string length of * the value. */ static void a2width(const char *p, struct roffsu *su) { if ( ! a2roffsu(p, su, SCALE_MAX)) { - su->unit = SCALE_BU; + su->unit = SCALE_EN; su->scale = html_strlen(p); } } /* * See the same function in mdoc_term.c for documentation. */ static void synopsis_pre(struct html *h, const struct mdoc_node *n) { if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags)) return; if (n->prev->tok == n->tok && MDOC_Fo != n->tok && MDOC_Ft != n->tok && MDOC_Fn != n->tok) { print_otag(h, TAG_BR, 0, NULL); return; } switch (n->prev->tok) { case MDOC_Fd: /* FALLTHROUGH */ case MDOC_Fn: /* FALLTHROUGH */ case MDOC_Fo: /* FALLTHROUGH */ case MDOC_In: /* FALLTHROUGH */ case MDOC_Vt: - print_otag(h, TAG_P, 0, NULL); + print_paragraph(h); break; case MDOC_Ft: if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) { - print_otag(h, TAG_P, 0, NULL); + print_paragraph(h); break; } /* FALLTHROUGH */ default: print_otag(h, TAG_BR, 0, NULL); break; } } -/* - * Calculate the scaling unit passed in an `-offset' argument. This - * uses either a native scaling unit (e.g., 1i, 2m), one of a set of - * predefined strings (indent, etc.), or the string length of the value. - */ static void -a2offs(const char *p, struct roffsu *su) -{ - - /* FIXME: "right"? */ - - if (0 == strcmp(p, "left")) - SCALE_HS_INIT(su, 0); - else if (0 == strcmp(p, "indent")) - SCALE_HS_INIT(su, INDENT); - else if (0 == strcmp(p, "indent-two")) - SCALE_HS_INIT(su, INDENT * 2); - else if ( ! a2roffsu(p, su, SCALE_MAX)) - SCALE_HS_INIT(su, html_strlen(p)); -} - -static void print_mdoc(MDOC_ARGS) { struct tag *t, *tt; struct htmlpair tag; PAIR_CLASS_INIT(&tag, "mandoc"); if ( ! (HTML_FRAGMENT & h->oflags)) { print_gen_decls(h); t = print_otag(h, TAG_HTML, 0, NULL); tt = print_otag(h, TAG_HEAD, 0, NULL); print_mdoc_head(meta, n, h); print_tagq(h, tt); print_otag(h, TAG_BODY, 0, NULL); print_otag(h, TAG_DIV, 1, &tag); } else t = print_otag(h, TAG_DIV, 1, &tag); print_mdoc_nodelist(meta, n, h); print_tagq(h, t); } static void print_mdoc_head(MDOC_ARGS) { print_gen_head(h); bufinit(h); bufcat(h, meta->title); if (meta->msec) bufcat_fmt(h, "(%s)", meta->msec); if (meta->arch) bufcat_fmt(h, " (%s)", meta->arch); print_otag(h, TAG_TITLE, 0, NULL); print_text(h, h->buf); } static void print_mdoc_nodelist(MDOC_ARGS) { print_mdoc_node(meta, n, h); if (n->next) print_mdoc_nodelist(meta, n->next, h); } static void print_mdoc_node(MDOC_ARGS) { int child; struct tag *t; child = 1; t = h->tags.head; switch (n->type) { case MDOC_ROOT: child = mdoc_root_pre(meta, n, h); break; case MDOC_TEXT: /* No tables in this mode... */ assert(NULL == h->tblt); /* * Make sure that if we're in a literal mode already * (i.e., within a
) don't print the newline.
 		 */
 		if (' ' == *n->string && MDOC_LINE & n->flags)
 			if ( ! (HTML_LITERAL & h->flags))
 				print_otag(h, TAG_BR, 0, NULL);
 		if (MDOC_DELIMC & n->flags)
 			h->flags |= HTML_NOSPACE;
 		print_text(h, n->string);
 		if (MDOC_DELIMO & n->flags)
 			h->flags |= HTML_NOSPACE;
 		return;
 	case MDOC_EQN:
 		print_eqn(h, n->eqn);
 		break;
 	case MDOC_TBL:
 		/*
 		 * This will take care of initialising all of the table
 		 * state data for the first table, then tearing it down
 		 * for the last one.
 		 */
 		print_tbl(h, n->span);
 		return;
 	default:
 		/*
 		 * Close out the current table, if it's open, and unset
 		 * the "meta" table state.  This will be reopened on the
 		 * next table element.
 		 */
-		if (h->tblt) {
+		if (h->tblt != NULL) {
 			print_tblclose(h);
 			t = h->tags.head;
 		}
-
-		assert(NULL == h->tblt);
-		if (mdocs[n->tok].pre && ENDBODY_NOT == n->end)
+		assert(h->tblt == NULL);
+		if (mdocs[n->tok].pre && (n->end == ENDBODY_NOT || n->child))
 			child = (*mdocs[n->tok].pre)(meta, n, h);
 		break;
 	}
 
 	if (HTML_KEEP & h->flags) {
 		if (n->prev ? (n->prev->lastline != n->line) :
 		    (n->parent && n->parent->line != n->line)) {
 			h->flags &= ~HTML_KEEP;
 			h->flags |= HTML_PREKEEP;
 		}
 	}
 
 	if (child && n->child)
 		print_mdoc_nodelist(meta, n->child, h);
 
 	print_stagq(h, t);
 
 	switch (n->type) {
 	case MDOC_ROOT:
 		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)(meta, n, h);
+		if ( ! mdocs[n->tok].post || n->flags & MDOC_ENDED)
+			break;
+		(*mdocs[n->tok].post)(meta, n, h);
+		if (n->end != ENDBODY_NOT)
+			n->pending->flags |= MDOC_ENDED;
+		if (n->end == ENDBODY_NOSPACE)
+			h->flags |= HTML_NOSPACE;
 		break;
 	}
 }
 
 static void
 mdoc_root_post(MDOC_ARGS)
 {
-	struct htmlpair	 tag[3];
+	struct htmlpair	 tag;
 	struct tag	*t, *tt;
 
-	PAIR_SUMMARY_INIT(&tag[0], "Document Footer");
-	PAIR_CLASS_INIT(&tag[1], "foot");
-	PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
-	t = print_otag(h, TAG_TABLE, 3, tag);
-	PAIR_INIT(&tag[0], ATTR_WIDTH, "50%");
-	print_otag(h, TAG_COL, 1, tag);
-	print_otag(h, TAG_COL, 1, tag);
+	PAIR_CLASS_INIT(&tag, "foot");
+	t = print_otag(h, TAG_TABLE, 1, &tag);
 
 	print_otag(h, TAG_TBODY, 0, NULL);
 
 	tt = print_otag(h, TAG_TR, 0, NULL);
 
-	PAIR_CLASS_INIT(&tag[0], "foot-date");
-	print_otag(h, TAG_TD, 1, tag);
+	PAIR_CLASS_INIT(&tag, "foot-date");
+	print_otag(h, TAG_TD, 1, &tag);
 	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);
+	PAIR_CLASS_INIT(&tag, "foot-os");
+	print_otag(h, TAG_TD, 1, &tag);
 	print_text(h, meta->os);
 	print_tagq(h, t);
 }
 
 static int
 mdoc_root_pre(MDOC_ARGS)
 {
-	struct htmlpair	 tag[3];
+	struct htmlpair	 tag;
 	struct tag	*t, *tt;
 	char		*volume, *title;
 
 	if (NULL == meta->arch)
 		volume = mandoc_strdup(meta->vol);
 	else
 		mandoc_asprintf(&volume, "%s (%s)",
 		    meta->vol, meta->arch);
 
 	if (NULL == meta->msec)
 		title = mandoc_strdup(meta->title);
 	else
 		mandoc_asprintf(&title, "%s(%s)",
 		    meta->title, meta->msec);
 
-	PAIR_SUMMARY_INIT(&tag[0], "Document Header");
-	PAIR_CLASS_INIT(&tag[1], "head");
-	PAIR_INIT(&tag[2], ATTR_WIDTH, "100%");
-	t = print_otag(h, TAG_TABLE, 3, tag);
-	PAIR_INIT(&tag[0], ATTR_WIDTH, "30%");
-	print_otag(h, TAG_COL, 1, tag);
-	print_otag(h, TAG_COL, 1, tag);
-	print_otag(h, TAG_COL, 1, tag);
+	PAIR_CLASS_INIT(&tag, "head");
+	t = print_otag(h, TAG_TABLE, 1, &tag);
 
 	print_otag(h, TAG_TBODY, 0, NULL);
 
 	tt = print_otag(h, TAG_TR, 0, NULL);
 
-	PAIR_CLASS_INIT(&tag[0], "head-ltitle");
-	print_otag(h, TAG_TD, 1, tag);
+	PAIR_CLASS_INIT(&tag, "head-ltitle");
+	print_otag(h, TAG_TD, 1, &tag);
 	print_text(h, title);
 	print_stagq(h, tt);
 
-	PAIR_CLASS_INIT(&tag[0], "head-vol");
-	PAIR_INIT(&tag[1], ATTR_ALIGN, "center");
-	print_otag(h, TAG_TD, 2, tag);
+	PAIR_CLASS_INIT(&tag, "head-vol");
+	print_otag(h, TAG_TD, 1, &tag);
 	print_text(h, volume);
 	print_stagq(h, tt);
 
-	PAIR_CLASS_INIT(&tag[0], "head-rtitle");
-	PAIR_INIT(&tag[1], ATTR_ALIGN, "right");
-	print_otag(h, TAG_TD, 2, tag);
+	PAIR_CLASS_INIT(&tag, "head-rtitle");
+	print_otag(h, TAG_TD, 1, &tag);
 	print_text(h, title);
 	print_tagq(h, t);
 
 	free(title);
 	free(volume);
 	return(1);
 }
 
 static int
 mdoc_sh_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 
-	if (MDOC_BLOCK == n->type) {
+	switch (n->type) {
+	case MDOC_BLOCK:
 		PAIR_CLASS_INIT(&tag, "section");
 		print_otag(h, TAG_DIV, 1, &tag);
 		return(1);
-	} else if (MDOC_BODY == n->type)
+	case MDOC_BODY:
+		if (n->sec == SEC_AUTHORS)
+			h->flags &= ~(HTML_SPLIT|HTML_NOSPLIT);
 		return(1);
+	default:
+		break;
+	}
 
 	bufinit(h);
 	bufcat(h, "x");
 
 	for (n = n->child; n && MDOC_TEXT == n->type; ) {
 		bufcat_id(h, n->string);
 		if (NULL != (n = n->next))
 			bufcat_id(h, " ");
 	}
 
 	if (NULL == n) {
 		PAIR_ID_INIT(&tag, h->buf);
 		print_otag(h, TAG_H1, 1, &tag);
 	} else
 		print_otag(h, TAG_H1, 0, NULL);
 
 	return(1);
 }
 
 static int
 mdoc_ss_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 
 	if (MDOC_BLOCK == n->type) {
 		PAIR_CLASS_INIT(&tag, "subsection");
 		print_otag(h, TAG_DIV, 1, &tag);
 		return(1);
 	} else if (MDOC_BODY == n->type)
 		return(1);
 
 	bufinit(h);
 	bufcat(h, "x");
 
 	for (n = n->child; n && MDOC_TEXT == n->type; ) {
 		bufcat_id(h, n->string);
 		if (NULL != (n = n->next))
 			bufcat_id(h, " ");
 	}
 
 	if (NULL == n) {
 		PAIR_ID_INIT(&tag, h->buf);
 		print_otag(h, TAG_H2, 1, &tag);
 	} else
 		print_otag(h, TAG_H2, 0, NULL);
 
 	return(1);
 }
 
 static int
 mdoc_fl_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 
 	PAIR_CLASS_INIT(&tag, "flag");
 	print_otag(h, TAG_B, 1, &tag);
 
 	/* `Cm' has no leading hyphen. */
 
 	if (MDOC_Cm == n->tok)
 		return(1);
 
 	print_text(h, "\\-");
 
-	if (n->child)
+	if ( ! (n->nchild == 0 &&
+	    (n->next == NULL ||
+	     n->next->type == MDOC_TEXT ||
+	     n->next->flags & MDOC_LINE)))
 		h->flags |= HTML_NOSPACE;
-	else if (n->next && n->next->line == n->line)
-		h->flags |= HTML_NOSPACE;
 
 	return(1);
 }
 
 static int
 mdoc_nd_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 
 	if (MDOC_BODY != n->type)
 		return(1);
 
 	/* XXX: this tag in theory can contain block elements. */
 
 	print_text(h, "\\(em");
 	PAIR_CLASS_INIT(&tag, "desc");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_nm_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 	struct roffsu	 su;
 	int		 len;
 
 	switch (n->type) {
 	case MDOC_ELEM:
 		synopsis_pre(h, n);
 		PAIR_CLASS_INIT(&tag, "name");
 		print_otag(h, TAG_B, 1, &tag);
 		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 && meta->name)
 			print_text(h, meta->name);
 		return(1);
 	case MDOC_BODY:
 		print_otag(h, TAG_TD, 0, NULL);
 		return(1);
 	default:
 		break;
 	}
 
 	synopsis_pre(h, n);
 	PAIR_CLASS_INIT(&tag, "synopsis");
 	print_otag(h, TAG_TABLE, 1, &tag);
 
 	for (len = 0, n = n->child; n; n = n->next)
 		if (MDOC_TEXT == n->type)
 			len += html_strlen(n->string);
 
 	if (0 == len && meta->name)
 		len = html_strlen(meta->name);
 
 	SCALE_HS_INIT(&su, len);
 	bufinit(h);
 	bufcat_su(h, "width", &su);
 	PAIR_STYLE_INIT(&tag, h);
 	print_otag(h, TAG_COL, 1, &tag);
 	print_otag(h, TAG_COL, 0, NULL);
 	print_otag(h, TAG_TBODY, 0, NULL);
 	print_otag(h, TAG_TR, 0, NULL);
 	return(1);
 }
 
 static int
 mdoc_xr_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag[2];
 
 	if (NULL == n->child)
 		return(0);
 
 	PAIR_CLASS_INIT(&tag[0], "link-man");
 
 	if (h->base_man) {
 		buffmt_man(h, n->child->string,
 		    n->child->next ?
 		    n->child->next->string : NULL);
 		PAIR_HREF_INIT(&tag[1], h->buf);
 		print_otag(h, TAG_A, 2, tag);
 	} else
 		print_otag(h, TAG_A, 1, tag);
 
 	n = n->child;
 	print_text(h, n->string);
 
 	if (NULL == (n = n->next))
 		return(0);
 
 	h->flags |= HTML_NOSPACE;
 	print_text(h, "(");
 	h->flags |= HTML_NOSPACE;
 	print_text(h, n->string);
 	h->flags |= HTML_NOSPACE;
 	print_text(h, ")");
 	return(0);
 }
 
 static int
 mdoc_ns_pre(MDOC_ARGS)
 {
 
 	if ( ! (MDOC_LINE & n->flags))
 		h->flags |= HTML_NOSPACE;
 	return(1);
 }
 
 static int
 mdoc_ar_pre(MDOC_ARGS)
 {
 	struct htmlpair tag;
 
 	PAIR_CLASS_INIT(&tag, "arg");
 	print_otag(h, TAG_I, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_xx_pre(MDOC_ARGS)
 {
 	const char	*pp;
 	struct htmlpair	 tag;
 	int		 flags;
 
 	switch (n->tok) {
 	case MDOC_Bsx:
 		pp = "BSD/OS";
 		break;
 	case MDOC_Dx:
 		pp = "DragonFly";
 		break;
 	case MDOC_Fx:
 		pp = "FreeBSD";
 		break;
 	case MDOC_Nx:
 		pp = "NetBSD";
 		break;
 	case MDOC_Ox:
 		pp = "OpenBSD";
 		break;
 	case MDOC_Ux:
 		pp = "UNIX";
 		break;
 	default:
 		return(1);
 	}
 
 	PAIR_CLASS_INIT(&tag, "unix");
 	print_otag(h, TAG_SPAN, 1, &tag);
 
 	print_text(h, pp);
 	if (n->child) {
 		flags = h->flags;
 		h->flags |= HTML_KEEP;
 		print_text(h, n->child->string);
 		h->flags = flags;
 	}
 	return(0);
 }
 
 static int
 mdoc_bx_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 
 	PAIR_CLASS_INIT(&tag, "unix");
 	print_otag(h, TAG_SPAN, 1, &tag);
 
 	if (NULL != (n = n->child)) {
 		print_text(h, n->string);
 		h->flags |= HTML_NOSPACE;
 		print_text(h, "BSD");
 	} else {
 		print_text(h, "BSD");
 		return(0);
 	}
 
 	if (NULL != (n = n->next)) {
 		h->flags |= HTML_NOSPACE;
 		print_text(h, "-");
 		h->flags |= HTML_NOSPACE;
 		print_text(h, n->string);
 	}
 
 	return(0);
 }
 
 static int
 mdoc_it_pre(MDOC_ARGS)
 {
 	struct roffsu	 su;
 	enum mdoc_list	 type;
 	struct htmlpair	 tag[2];
 	const struct mdoc_node *bl;
 
 	bl = n->parent;
 	while (bl && MDOC_Bl != bl->tok)
 		bl = bl->parent;
 
 	assert(bl);
 
 	type = bl->norm->Bl.type;
 
 	assert(lists[type]);
 	PAIR_CLASS_INIT(&tag[0], lists[type]);
 
 	bufinit(h);
 
 	if (MDOC_HEAD == n->type) {
 		switch (type) {
 		case LIST_bullet:
 			/* FALLTHROUGH */
 		case LIST_dash:
 			/* FALLTHROUGH */
 		case LIST_item:
 			/* FALLTHROUGH */
 		case LIST_hyphen:
 			/* FALLTHROUGH */
 		case LIST_enum:
 			return(0);
 		case LIST_diag:
 			/* FALLTHROUGH */
 		case LIST_hang:
 			/* FALLTHROUGH */
 		case LIST_inset:
 			/* FALLTHROUGH */
 		case LIST_ohang:
 			/* FALLTHROUGH */
 		case LIST_tag:
 			SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
 			bufcat_su(h, "margin-top", &su);
 			PAIR_STYLE_INIT(&tag[1], h);
 			print_otag(h, TAG_DT, 2, tag);
 			if (LIST_diag != type)
 				break;
 			PAIR_CLASS_INIT(&tag[0], "diag");
 			print_otag(h, TAG_B, 1, tag);
 			break;
 		case LIST_column:
 			break;
 		default:
 			break;
 		}
 	} else if (MDOC_BODY == n->type) {
 		switch (type) {
 		case LIST_bullet:
 			/* FALLTHROUGH */
 		case LIST_hyphen:
 			/* FALLTHROUGH */
 		case LIST_dash:
 			/* FALLTHROUGH */
 		case LIST_enum:
 			/* FALLTHROUGH */
 		case LIST_item:
 			SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
 			bufcat_su(h, "margin-top", &su);
 			PAIR_STYLE_INIT(&tag[1], h);
 			print_otag(h, TAG_LI, 2, tag);
 			break;
 		case LIST_diag:
 			/* FALLTHROUGH */
 		case LIST_hang:
 			/* FALLTHROUGH */
 		case LIST_inset:
 			/* FALLTHROUGH */
 		case LIST_ohang:
 			/* FALLTHROUGH */
 		case LIST_tag:
 			if (NULL == bl->norm->Bl.width) {
 				print_otag(h, TAG_DD, 1, tag);
 				break;
 			}
 			a2width(bl->norm->Bl.width, &su);
 			bufcat_su(h, "margin-left", &su);
 			PAIR_STYLE_INIT(&tag[1], h);
 			print_otag(h, TAG_DD, 2, tag);
 			break;
 		case LIST_column:
 			SCALE_VS_INIT(&su, ! bl->norm->Bl.comp);
 			bufcat_su(h, "margin-top", &su);
 			PAIR_STYLE_INIT(&tag[1], h);
 			print_otag(h, TAG_TD, 2, tag);
 			break;
 		default:
 			break;
 		}
 	} else {
 		switch (type) {
 		case LIST_column:
 			print_otag(h, TAG_TR, 1, tag);
 			break;
 		default:
 			break;
 		}
 	}
 
 	return(1);
 }
 
 static int
 mdoc_bl_pre(MDOC_ARGS)
 {
 	int		 i;
 	struct htmlpair	 tag[3];
 	struct roffsu	 su;
 	char		 buf[BUFSIZ];
 
 	if (MDOC_BODY == n->type) {
 		if (LIST_column == n->norm->Bl.type)
 			print_otag(h, TAG_TBODY, 0, NULL);
 		return(1);
 	}
 
 	if (MDOC_HEAD == n->type) {
 		if (LIST_column != n->norm->Bl.type)
 			return(0);
 
 		/*
 		 * For each column, print out the  tag with our
 		 * suggested width.  The last column gets min-width, as
 		 * in terminal mode it auto-sizes to the width of the
 		 * screen and we want to preserve that behaviour.
 		 */
 
 		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);
 			else
 				bufcat_su(h, "min-width", &su);
 			PAIR_STYLE_INIT(&tag[0], h);
 			print_otag(h, TAG_COL, 1, tag);
 		}
 
 		return(0);
 	}
 
 	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);
 
 	assert(lists[n->norm->Bl.type]);
 	(void)strlcpy(buf, "list ", BUFSIZ);
 	(void)strlcat(buf, lists[n->norm->Bl.type], BUFSIZ);
 	PAIR_INIT(&tag[1], ATTR_CLASS, buf);
 
 	/* Set the block's left-hand margin. */
 
 	if (n->norm->Bl.offs) {
-		a2offs(n->norm->Bl.offs, &su);
+		a2width(n->norm->Bl.offs, &su);
 		bufcat_su(h, "margin-left", &su);
 	}
 
 	switch (n->norm->Bl.type) {
 	case LIST_bullet:
 		/* FALLTHROUGH */
 	case LIST_dash:
 		/* FALLTHROUGH */
 	case LIST_hyphen:
 		/* FALLTHROUGH */
 	case LIST_item:
 		print_otag(h, TAG_UL, 2, tag);
 		break;
 	case LIST_enum:
 		print_otag(h, TAG_OL, 2, tag);
 		break;
 	case LIST_diag:
 		/* FALLTHROUGH */
 	case LIST_hang:
 		/* FALLTHROUGH */
 	case LIST_inset:
 		/* FALLTHROUGH */
 	case LIST_ohang:
 		/* FALLTHROUGH */
 	case LIST_tag:
 		print_otag(h, TAG_DL, 2, tag);
 		break;
 	case LIST_column:
 		print_otag(h, TAG_TABLE, 2, tag);
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	return(1);
 }
 
 static int
 mdoc_ex_pre(MDOC_ARGS)
 {
 	struct tag	*t;
 	struct htmlpair	 tag;
 	int		 nchild;
 
 	if (n->prev)
 		print_otag(h, TAG_BR, 0, NULL);
 
 	PAIR_CLASS_INIT(&tag, "utility");
 
 	print_text(h, "The");
 
 	nchild = n->nchild;
 	for (n = n->child; n; n = n->next) {
 		assert(MDOC_TEXT == n->type);
 
 		t = print_otag(h, TAG_B, 1, &tag);
 		print_text(h, n->string);
 		print_tagq(h, t);
 
 		if (nchild > 2 && n->next) {
 			h->flags |= HTML_NOSPACE;
 			print_text(h, ",");
 		}
 
 		if (n->next && NULL == n->next->next)
 			print_text(h, "and");
 	}
 
 	if (nchild > 1)
 		print_text(h, "utilities exit\\~0");
 	else
 		print_text(h, "utility exits\\~0");
 
 	print_text(h, "on success, and\\~>0 if an error occurs.");
 	return(0);
 }
 
 static int
 mdoc_em_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "emph");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_d1_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag[2];
 	struct roffsu	 su;
 
 	if (MDOC_BLOCK != n->type)
 		return(1);
 
 	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);
 	print_otag(h, TAG_BLOCKQUOTE, 1, tag);
 
 	/* BLOCKQUOTE needs a block body. */
 
 	PAIR_CLASS_INIT(&tag[0], "display");
 	print_otag(h, TAG_DIV, 1, tag);
 
 	if (MDOC_Dl == n->tok) {
 		PAIR_CLASS_INIT(&tag[0], "lit");
 		print_otag(h, TAG_CODE, 1, tag);
 	}
 
 	return(1);
 }
 
 static int
 mdoc_sx_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag[2];
 
 	bufinit(h);
 	bufcat(h, "#x");
 
 	for (n = n->child; n; ) {
 		bufcat_id(h, n->string);
 		if (NULL != (n = n->next))
 			bufcat_id(h, " ");
 	}
 
 	PAIR_CLASS_INIT(&tag[0], "link-sec");
 	PAIR_HREF_INIT(&tag[1], h->buf);
 
 	print_otag(h, TAG_I, 1, tag);
 	print_otag(h, TAG_A, 2, tag);
 	return(1);
 }
 
 static int
 mdoc_bd_pre(MDOC_ARGS)
 {
 	struct htmlpair		 tag[2];
 	int			 comp, sv;
 	const struct mdoc_node	*nn;
 	struct roffsu		 su;
 
 	if (MDOC_HEAD == n->type)
 		return(0);
 
 	if (MDOC_BLOCK == n->type) {
 		comp = n->norm->Bd.comp;
 		for (nn = n; nn && ! comp; nn = nn->parent) {
 			if (MDOC_BLOCK != nn->type)
 				continue;
 			if (MDOC_Ss == nn->tok || MDOC_Sh == nn->tok)
 				comp = 1;
 			if (nn->prev)
 				break;
 		}
 		if ( ! comp)
-			print_otag(h, TAG_P, 0, NULL);
+			print_paragraph(h);
 		return(1);
 	}
 
-	SCALE_HS_INIT(&su, 0);
-	if (n->norm->Bd.offs)
-		a2offs(n->norm->Bd.offs, &su);
+	/* Handle the -offset argument. */
 
+	if (n->norm->Bd.offs == NULL ||
+	    ! strcmp(n->norm->Bd.offs, "left"))
+		SCALE_HS_INIT(&su, 0);
+	else if ( ! strcmp(n->norm->Bd.offs, "indent"))
+		SCALE_HS_INIT(&su, INDENT);
+	else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
+		SCALE_HS_INIT(&su, INDENT * 2);
+	else
+		a2width(n->norm->Bd.offs, &su);
+
 	bufinit(h);
 	bufcat_su(h, "margin-left", &su);
 	PAIR_STYLE_INIT(&tag[0], h);
 
 	if (DISP_unfilled != n->norm->Bd.type &&
 	    DISP_literal != n->norm->Bd.type) {
 		PAIR_CLASS_INIT(&tag[1], "display");
 		print_otag(h, TAG_DIV, 2, tag);
 		return(1);
 	}
 
 	PAIR_CLASS_INIT(&tag[1], "lit display");
 	print_otag(h, TAG_PRE, 2, tag);
 
 	/* This can be recursive: save & set our literal state. */
 
 	sv = h->flags & HTML_LITERAL;
 	h->flags |= HTML_LITERAL;
 
 	for (nn = n->child; nn; nn = nn->next) {
 		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
 		 * notion of selective eoln whitespace is pretty dumb
 		 * anyway, so don't sweat it.
 		 */
 		switch (nn->tok) {
 		case MDOC_Sm:
 			/* FALLTHROUGH */
 		case MDOC_br:
 			/* FALLTHROUGH */
 		case MDOC_sp:
 			/* FALLTHROUGH */
 		case MDOC_Bl:
 			/* FALLTHROUGH */
 		case MDOC_D1:
 			/* FALLTHROUGH */
 		case MDOC_Dl:
 			/* FALLTHROUGH */
 		case MDOC_Lp:
 			/* FALLTHROUGH */
 		case MDOC_Pp:
 			continue;
 		default:
 			break;
 		}
 		if (nn->next && nn->next->line == nn->line)
 			continue;
 		else if (nn->next)
 			print_text(h, "\n");
 
 		h->flags |= HTML_NOSPACE;
 	}
 
 	if (0 == sv)
 		h->flags &= ~HTML_LITERAL;
 
 	return(0);
 }
 
 static int
 mdoc_pa_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "file");
 	print_otag(h, TAG_I, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_ad_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "addr");
 	print_otag(h, TAG_I, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_an_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
-	/* TODO: -split and -nosplit (see termp_an_pre()). */
+	if (n->norm->An.auth == AUTH_split) {
+		h->flags &= ~HTML_NOSPLIT;
+		h->flags |= HTML_SPLIT;
+		return(0);
+	}
+	if (n->norm->An.auth == AUTH_nosplit) {
+		h->flags &= ~HTML_SPLIT;
+		h->flags |= HTML_NOSPLIT;
+		return(0);
+	}
 
+	if (n->child == NULL)
+		return(0);
+
+	if (h->flags & HTML_SPLIT)
+		print_otag(h, TAG_BR, 0, NULL);
+
+	if (n->sec == SEC_AUTHORS && ! (h->flags & HTML_NOSPLIT))
+		h->flags |= HTML_SPLIT;
+
 	PAIR_CLASS_INIT(&tag, "author");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_cd_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	synopsis_pre(h, n);
 	PAIR_CLASS_INIT(&tag, "config");
 	print_otag(h, TAG_B, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_dv_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "define");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_ev_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "env");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_er_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "errno");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_fa_pre(MDOC_ARGS)
 {
 	const struct mdoc_node	*nn;
 	struct htmlpair		 tag;
 	struct tag		*t;
 
 	PAIR_CLASS_INIT(&tag, "farg");
 	if (n->parent->tok != MDOC_Fo) {
 		print_otag(h, TAG_I, 1, &tag);
 		return(1);
 	}
 
 	for (nn = n->child; nn; nn = nn->next) {
 		t = print_otag(h, TAG_I, 1, &tag);
 		print_text(h, nn->string);
 		print_tagq(h, t);
 		if (nn->next) {
 			h->flags |= HTML_NOSPACE;
 			print_text(h, ",");
 		}
 	}
 
 	if (n->child && n->next && n->next->tok == MDOC_Fa) {
 		h->flags |= HTML_NOSPACE;
 		print_text(h, ",");
 	}
 
 	return(0);
 }
 
 static int
 mdoc_fd_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag[2];
 	char		 buf[BUFSIZ];
 	size_t		 sz;
 	int		 i;
 	struct tag	*t;
 
 	synopsis_pre(h, n);
 
 	if (NULL == (n = n->child))
 		return(0);
 
 	assert(MDOC_TEXT == n->type);
 
 	if (strcmp(n->string, "#include")) {
 		PAIR_CLASS_INIT(&tag[0], "macro");
 		print_otag(h, TAG_B, 1, tag);
 		return(1);
 	}
 
 	PAIR_CLASS_INIT(&tag[0], "includes");
 	print_otag(h, TAG_B, 1, tag);
 	print_text(h, n->string);
 
 	if (NULL != (n = n->next)) {
 		assert(MDOC_TEXT == n->type);
 
 		/*
 		 * XXX This is broken and not easy to fix.
 		 * When using -Oincludes, truncation may occur.
 		 * Dynamic allocation wouldn't help because
 		 * passing long strings to buffmt_includes()
 		 * does not work either.
 		 */
 
 		strlcpy(buf, '<' == *n->string || '"' == *n->string ?
 		    n->string + 1 : n->string, BUFSIZ);
 
 		sz = strlen(buf);
 		if (sz && ('>' == buf[sz - 1] || '"' == buf[sz - 1]))
 			buf[sz - 1] = '\0';
 
 		PAIR_CLASS_INIT(&tag[0], "link-includes");
 
 		i = 1;
 		if (h->base_includes) {
 			buffmt_includes(h, buf);
 			PAIR_HREF_INIT(&tag[i], h->buf);
 			i++;
 		}
 
 		t = print_otag(h, TAG_A, i, tag);
 		print_text(h, n->string);
 		print_tagq(h, t);
 
 		n = n->next;
 	}
 
 	for ( ; n; n = n->next) {
 		assert(MDOC_TEXT == n->type);
 		print_text(h, n->string);
 	}
 
 	return(0);
 }
 
 static int
 mdoc_vt_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 
 	if (MDOC_BLOCK == n->type) {
 		synopsis_pre(h, n);
 		return(1);
 	} else if (MDOC_ELEM == n->type) {
 		synopsis_pre(h, n);
 	} else if (MDOC_HEAD == n->type)
 		return(0);
 
 	PAIR_CLASS_INIT(&tag, "type");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_ft_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 
 	synopsis_pre(h, n);
 	PAIR_CLASS_INIT(&tag, "ftype");
 	print_otag(h, TAG_I, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_fn_pre(MDOC_ARGS)
 {
 	struct tag	*t;
 	struct htmlpair	 tag[2];
 	char		 nbuf[BUFSIZ];
 	const char	*sp, *ep;
 	int		 sz, i, pretty;
 
 	pretty = MDOC_SYNPRETTY & n->flags;
 	synopsis_pre(h, n);
 
 	/* Split apart into type and name. */
 	assert(n->child->string);
 	sp = n->child->string;
 
 	ep = strchr(sp, ' ');
 	if (NULL != ep) {
 		PAIR_CLASS_INIT(&tag[0], "ftype");
 		t = print_otag(h, TAG_I, 1, tag);
 
 		while (ep) {
 			sz = MIN((int)(ep - sp), BUFSIZ - 1);
 			(void)memcpy(nbuf, sp, (size_t)sz);
 			nbuf[sz] = '\0';
 			print_text(h, nbuf);
 			sp = ++ep;
 			ep = strchr(sp, ' ');
 		}
 		print_tagq(h, t);
 	}
 
 	PAIR_CLASS_INIT(&tag[0], "fname");
 
 	/*
 	 * FIXME: only refer to IDs that we know exist.
 	 */
 
 #if 0
 	if (MDOC_SYNPRETTY & n->flags) {
 		nbuf[0] = '\0';
 		html_idcat(nbuf, sp, BUFSIZ);
 		PAIR_ID_INIT(&tag[1], nbuf);
 	} else {
 		strlcpy(nbuf, "#", BUFSIZ);
 		html_idcat(nbuf, sp, BUFSIZ);
 		PAIR_HREF_INIT(&tag[1], nbuf);
 	}
 #endif
 
 	t = print_otag(h, TAG_B, 1, tag);
 
 	if (sp)
 		print_text(h, sp);
 
 	print_tagq(h, t);
 
 	h->flags |= HTML_NOSPACE;
 	print_text(h, "(");
 	h->flags |= HTML_NOSPACE;
 
 	PAIR_CLASS_INIT(&tag[0], "farg");
 	bufinit(h);
 	bufcat_style(h, "white-space", "nowrap");
 	PAIR_STYLE_INIT(&tag[1], h);
 
 	for (n = n->child->next; n; n = n->next) {
 		i = 1;
 		if (MDOC_SYNPRETTY & n->flags)
 			i = 2;
 		t = print_otag(h, TAG_I, i, tag);
 		print_text(h, n->string);
 		print_tagq(h, t);
 		if (n->next) {
 			h->flags |= HTML_NOSPACE;
 			print_text(h, ",");
 		}
 	}
 
 	h->flags |= HTML_NOSPACE;
 	print_text(h, ")");
 
 	if (pretty) {
 		h->flags |= HTML_NOSPACE;
 		print_text(h, ";");
 	}
 
 	return(0);
 }
 
 static int
 mdoc_sm_pre(MDOC_ARGS)
 {
 
 	if (NULL == n->child)
 		h->flags ^= HTML_NONOSPACE;
 	else if (0 == strcmp("on", n->child->string))
 		h->flags &= ~HTML_NONOSPACE;
 	else
 		h->flags |= HTML_NONOSPACE;
 
 	if ( ! (HTML_NONOSPACE & h->flags))
 		h->flags &= ~HTML_NOSPACE;
 
 	return(0);
 }
 
 static int
 mdoc_skip_pre(MDOC_ARGS)
 {
 
 	return(0);
 }
 
 static int
 mdoc_pp_pre(MDOC_ARGS)
 {
 
-	print_otag(h, TAG_P, 0, NULL);
+	print_paragraph(h);
 	return(0);
 }
 
 static int
 mdoc_sp_pre(MDOC_ARGS)
 {
 	struct roffsu	 su;
 	struct htmlpair	 tag;
 
 	SCALE_VS_INIT(&su, 1);
 
 	if (MDOC_sp == n->tok) {
 		if (NULL != (n = n->child))
 			if ( ! a2roffsu(n->string, &su, SCALE_VS))
 				SCALE_VS_INIT(&su, atoi(n->string));
 	} else
 		su.scale = 0.0;
 
 	bufinit(h);
 	bufcat_su(h, "height", &su);
 	PAIR_STYLE_INIT(&tag, h);
 	print_otag(h, TAG_DIV, 1, &tag);
 
 	/* So the div isn't empty: */
 	print_text(h, "\\~");
 
 	return(0);
 
 }
 
 static int
 mdoc_lk_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag[2];
 
 	if (NULL == (n = n->child))
 		return(0);
 
 	assert(MDOC_TEXT == n->type);
 
 	PAIR_CLASS_INIT(&tag[0], "link-ext");
 	PAIR_HREF_INIT(&tag[1], n->string);
 
 	print_otag(h, TAG_A, 2, tag);
 
 	if (NULL == n->next)
 		print_text(h, n->string);
 
 	for (n = n->next; n; n = n->next)
 		print_text(h, n->string);
 
 	return(0);
 }
 
 static int
 mdoc_mt_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag[2];
 	struct tag	*t;
 
 	PAIR_CLASS_INIT(&tag[0], "link-mail");
 
 	for (n = n->child; n; n = n->next) {
 		assert(MDOC_TEXT == n->type);
 
 		bufinit(h);
 		bufcat(h, "mailto:");
 		bufcat(h, n->string);
 
 		PAIR_HREF_INIT(&tag[1], h->buf);
 		t = print_otag(h, TAG_A, 2, tag);
 		print_text(h, n->string);
 		print_tagq(h, t);
 	}
 
 	return(0);
 }
 
 static int
 mdoc_fo_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 	struct tag	*t;
 
 	if (MDOC_BODY == n->type) {
 		h->flags |= HTML_NOSPACE;
 		print_text(h, "(");
 		h->flags |= HTML_NOSPACE;
 		return(1);
 	} else if (MDOC_BLOCK == n->type) {
 		synopsis_pre(h, n);
 		return(1);
 	}
 
 	/* XXX: we drop non-initial arguments as per groff. */
 
 	assert(n->child);
 	assert(n->child->string);
 
 	PAIR_CLASS_INIT(&tag, "fname");
 	t = print_otag(h, TAG_B, 1, &tag);
 	print_text(h, n->child->string);
 	print_tagq(h, t);
 	return(0);
 }
 
 static void
 mdoc_fo_post(MDOC_ARGS)
 {
 
 	if (MDOC_BODY != n->type)
 		return;
 	h->flags |= HTML_NOSPACE;
 	print_text(h, ")");
 	h->flags |= HTML_NOSPACE;
 	print_text(h, ";");
 }
 
 static int
 mdoc_in_pre(MDOC_ARGS)
 {
 	struct tag	*t;
 	struct htmlpair	 tag[2];
 	int		 i;
 
 	synopsis_pre(h, n);
 
 	PAIR_CLASS_INIT(&tag[0], "includes");
 	print_otag(h, TAG_B, 1, tag);
 
 	/*
 	 * The first argument of the `In' gets special treatment as
 	 * being a linked value.  Subsequent values are printed
 	 * afterward.  groff does similarly.  This also handles the case
 	 * of no children.
 	 */
 
 	if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags)
 		print_text(h, "#include");
 
 	print_text(h, "<");
 	h->flags |= HTML_NOSPACE;
 
 	if (NULL != (n = n->child)) {
 		assert(MDOC_TEXT == n->type);
 
 		PAIR_CLASS_INIT(&tag[0], "link-includes");
 
 		i = 1;
 		if (h->base_includes) {
 			buffmt_includes(h, n->string);
 			PAIR_HREF_INIT(&tag[i], h->buf);
 			i++;
 		}
 
 		t = print_otag(h, TAG_A, i, tag);
 		print_text(h, n->string);
 		print_tagq(h, t);
 
 		n = n->next;
 	}
 
 	h->flags |= HTML_NOSPACE;
 	print_text(h, ">");
 
 	for ( ; n; n = n->next) {
 		assert(MDOC_TEXT == n->type);
 		print_text(h, n->string);
 	}
 
 	return(0);
 }
 
 static int
 mdoc_ic_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "cmd");
 	print_otag(h, TAG_B, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_rv_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 	struct tag	*t;
 	int		 nchild;
 
 	if (n->prev)
 		print_otag(h, TAG_BR, 0, NULL);
 
 	PAIR_CLASS_INIT(&tag, "fname");
 
 	nchild = n->nchild;
 	if (nchild > 0) {
 		print_text(h, "The");
 
 		for (n = n->child; n; n = n->next) {
 			t = print_otag(h, TAG_B, 1, &tag);
 			print_text(h, n->string);
 			print_tagq(h, t);
 
 			h->flags |= HTML_NOSPACE;
 			print_text(h, "()");
 
 			if (n->next == NULL)
 				continue;
 
 			if (nchild > 2) {
 				h->flags |= HTML_NOSPACE;
 				print_text(h, ",");
 			}
 			if (n->next->next == NULL)
 				print_text(h, "and");
 		}
 
 		if (nchild > 1)
 			print_text(h, "functions return");
 		else
 			print_text(h, "function returns");
 
 		print_text(h, "the value\\~0 if successful;");
 	} else
 		print_text(h, "Upon successful completion,"
                     " the value\\~0 is returned;");
 
 	print_text(h, "otherwise the value\\~\\-1 is returned"
 	   " and the global variable");
 
 	PAIR_CLASS_INIT(&tag, "var");
 	t = print_otag(h, TAG_B, 1, &tag);
 	print_text(h, "errno");
 	print_tagq(h, t);
 	print_text(h, "is set to indicate the error.");
 	return(0);
 }
 
 static int
 mdoc_va_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "var");
 	print_otag(h, TAG_B, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_ap_pre(MDOC_ARGS)
 {
 
 	h->flags |= HTML_NOSPACE;
 	print_text(h, "\\(aq");
 	h->flags |= HTML_NOSPACE;
 	return(1);
 }
 
 static int
 mdoc_bf_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag[2];
 	struct roffsu	 su;
 
 	if (MDOC_HEAD == n->type)
 		return(0);
 	else if (MDOC_BODY != n->type)
 		return(1);
 
 	if (FONT_Em == n->norm->Bf.font)
 		PAIR_CLASS_INIT(&tag[0], "emph");
 	else if (FONT_Sy == n->norm->Bf.font)
 		PAIR_CLASS_INIT(&tag[0], "symb");
 	else if (FONT_Li == n->norm->Bf.font)
 		PAIR_CLASS_INIT(&tag[0], "lit");
 	else
 		PAIR_CLASS_INIT(&tag[0], "none");
 
 	/*
 	 * We want this to be inline-formatted, but needs to be div to
 	 * accept block children.
 	 */
 	bufinit(h);
 	bufcat_style(h, "display", "inline");
 	SCALE_HS_INIT(&su, 1);
 	/* Needs a left-margin for spacing. */
 	bufcat_su(h, "margin-left", &su);
 	PAIR_STYLE_INIT(&tag[1], h);
 	print_otag(h, TAG_DIV, 2, tag);
 	return(1);
 }
 
 static int
 mdoc_ms_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "symb");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_igndelim_pre(MDOC_ARGS)
 {
 
 	h->flags |= HTML_IGNDELIM;
 	return(1);
 }
 
 static void
 mdoc_pf_post(MDOC_ARGS)
 {
 
 	h->flags |= HTML_NOSPACE;
 }
 
 static int
 mdoc_rs_pre(MDOC_ARGS)
 {
 	struct htmlpair	 tag;
 
 	if (MDOC_BLOCK != n->type)
 		return(1);
 
 	if (n->prev && SEC_SEE_ALSO == n->sec)
-		print_otag(h, TAG_P, 0, NULL);
+		print_paragraph(h);
 
 	PAIR_CLASS_INIT(&tag, "ref");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
+mdoc_no_pre(MDOC_ARGS)
+{
+	struct htmlpair	tag;
+
+	PAIR_CLASS_INIT(&tag, "none");
+	print_otag(h, TAG_CODE, 1, &tag);
+	return(1);
+}
+
+static int
 mdoc_li_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "lit");
 	print_otag(h, TAG_CODE, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_sy_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	PAIR_CLASS_INIT(&tag, "symb");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc_bt_pre(MDOC_ARGS)
 {
 
 	print_text(h, "is currently in beta test.");
 	return(0);
 }
 
 static int
 mdoc_ud_pre(MDOC_ARGS)
 {
 
 	print_text(h, "currently under development.");
 	return(0);
 }
 
 static int
 mdoc_lb_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags && n->prev)
 		print_otag(h, TAG_BR, 0, NULL);
 
 	PAIR_CLASS_INIT(&tag, "lib");
 	print_otag(h, TAG_SPAN, 1, &tag);
 	return(1);
 }
 
 static int
 mdoc__x_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag[2];
 	enum htmltag	t;
 
 	t = TAG_SPAN;
 
 	switch (n->tok) {
 	case MDOC__A:
 		PAIR_CLASS_INIT(&tag[0], "ref-auth");
 		if (n->prev && MDOC__A == n->prev->tok)
 			if (NULL == n->next || MDOC__A != n->next->tok)
 				print_text(h, "and");
 		break;
 	case MDOC__B:
 		PAIR_CLASS_INIT(&tag[0], "ref-book");
 		t = TAG_I;
 		break;
 	case MDOC__C:
 		PAIR_CLASS_INIT(&tag[0], "ref-city");
 		break;
 	case MDOC__D:
 		PAIR_CLASS_INIT(&tag[0], "ref-date");
 		break;
 	case MDOC__I:
 		PAIR_CLASS_INIT(&tag[0], "ref-issue");
 		t = TAG_I;
 		break;
 	case MDOC__J:
 		PAIR_CLASS_INIT(&tag[0], "ref-jrnl");
 		t = TAG_I;
 		break;
 	case MDOC__N:
 		PAIR_CLASS_INIT(&tag[0], "ref-num");
 		break;
 	case MDOC__O:
 		PAIR_CLASS_INIT(&tag[0], "ref-opt");
 		break;
 	case MDOC__P:
 		PAIR_CLASS_INIT(&tag[0], "ref-page");
 		break;
 	case MDOC__Q:
 		PAIR_CLASS_INIT(&tag[0], "ref-corp");
 		break;
 	case MDOC__R:
 		PAIR_CLASS_INIT(&tag[0], "ref-rep");
 		break;
 	case MDOC__T:
 		PAIR_CLASS_INIT(&tag[0], "ref-title");
 		break;
 	case MDOC__U:
 		PAIR_CLASS_INIT(&tag[0], "link-ref");
 		break;
 	case MDOC__V:
 		PAIR_CLASS_INIT(&tag[0], "ref-vol");
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	if (MDOC__U != n->tok) {
 		print_otag(h, t, 1, tag);
 		return(1);
 	}
 
 	PAIR_HREF_INIT(&tag[1], n->child->string);
 	print_otag(h, TAG_A, 2, tag);
 
 	return(1);
 }
 
 static void
 mdoc__x_post(MDOC_ARGS)
 {
 
 	if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
 		if (NULL == n->next->next || MDOC__A != n->next->next->tok)
 			if (NULL == n->prev || MDOC__A != n->prev->tok)
 				return;
 
 	/* TODO: %U */
 
 	if (NULL == n->parent || MDOC_Rs != n->parent->tok)
 		return;
 
 	h->flags |= HTML_NOSPACE;
 	print_text(h, n->next ? "," : ".");
 }
 
 static int
 mdoc_bk_pre(MDOC_ARGS)
 {
 
 	switch (n->type) {
 	case MDOC_BLOCK:
 		break;
 	case MDOC_HEAD:
 		return(0);
 	case MDOC_BODY:
 		if (n->parent->args || 0 == n->prev->nchild)
 			h->flags |= HTML_PREKEEP;
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	return(1);
 }
 
 static void
 mdoc_bk_post(MDOC_ARGS)
 {
 
 	if (MDOC_BODY == n->type)
 		h->flags &= ~(HTML_KEEP | HTML_PREKEEP);
 }
 
 static int
 mdoc_quote_pre(MDOC_ARGS)
 {
 	struct htmlpair	tag;
 
 	if (MDOC_BODY != n->type)
 		return(1);
 
 	switch (n->tok) {
 	case MDOC_Ao:
 		/* FALLTHROUGH */
 	case MDOC_Aq:
-		print_text(h, "\\(la");
+		print_text(h, n->parent->prev != NULL &&
+		    n->parent->prev->tok == MDOC_An ?  "<" : "\\(la");
 		break;
 	case MDOC_Bro:
 		/* FALLTHROUGH */
 	case MDOC_Brq:
 		print_text(h, "\\(lC");
 		break;
 	case MDOC_Bo:
 		/* FALLTHROUGH */
 	case MDOC_Bq:
 		print_text(h, "\\(lB");
 		break;
 	case MDOC_Oo:
 		/* FALLTHROUGH */
 	case MDOC_Op:
 		print_text(h, "\\(lB");
 		h->flags |= HTML_NOSPACE;
 		PAIR_CLASS_INIT(&tag, "opt");
 		print_otag(h, TAG_SPAN, 1, &tag);
 		break;
 	case MDOC_En:
 		if (NULL == n->norm->Es ||
 		    NULL == n->norm->Es->child)
 			return(1);
 		print_text(h, n->norm->Es->child->string);
 		break;
 	case MDOC_Eo:
 		break;
 	case MDOC_Do:
 		/* FALLTHROUGH */
 	case MDOC_Dq:
 		/* FALLTHROUGH */
 	case MDOC_Qo:
 		/* FALLTHROUGH */
 	case MDOC_Qq:
 		print_text(h, "\\(lq");
 		break;
 	case MDOC_Po:
 		/* FALLTHROUGH */
 	case MDOC_Pq:
 		print_text(h, "(");
 		break;
 	case MDOC_Ql:
 		print_text(h, "\\(oq");
 		h->flags |= HTML_NOSPACE;
 		PAIR_CLASS_INIT(&tag, "lit");
 		print_otag(h, TAG_CODE, 1, &tag);
 		break;
 	case MDOC_So:
 		/* FALLTHROUGH */
 	case MDOC_Sq:
 		print_text(h, "\\(oq");
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	h->flags |= HTML_NOSPACE;
 	return(1);
 }
 
 static void
 mdoc_quote_post(MDOC_ARGS)
 {
 
-	if (MDOC_BODY != n->type)
+	if (n->type != MDOC_BODY && n->type != MDOC_ELEM)
 		return;
 
-	if (MDOC_En != n->tok)
+	if ( ! (n->tok == MDOC_En ||
+	    (n->tok == MDOC_Eo && n->end == ENDBODY_SPACE)))
 		h->flags |= HTML_NOSPACE;
 
 	switch (n->tok) {
 	case MDOC_Ao:
 		/* FALLTHROUGH */
 	case MDOC_Aq:
-		print_text(h, "\\(ra");
+		print_text(h, n->parent->prev != NULL &&
+		    n->parent->prev->tok == MDOC_An ?  ">" : "\\(ra");
 		break;
 	case MDOC_Bro:
 		/* FALLTHROUGH */
 	case MDOC_Brq:
 		print_text(h, "\\(rC");
 		break;
 	case MDOC_Oo:
 		/* FALLTHROUGH */
 	case MDOC_Op:
 		/* FALLTHROUGH */
 	case MDOC_Bo:
 		/* FALLTHROUGH */
 	case MDOC_Bq:
 		print_text(h, "\\(rB");
 		break;
 	case MDOC_En:
 		if (NULL != n->norm->Es &&
 		    NULL != n->norm->Es->child &&
 		    NULL != n->norm->Es->child->next) {
 			h->flags |= HTML_NOSPACE;
 			print_text(h, n->norm->Es->child->next->string);
 		}
 		break;
 	case MDOC_Eo:
 		break;
 	case MDOC_Qo:
 		/* FALLTHROUGH */
 	case MDOC_Qq:
 		/* FALLTHROUGH */
 	case MDOC_Do:
 		/* FALLTHROUGH */
 	case MDOC_Dq:
 		print_text(h, "\\(rq");
 		break;
 	case MDOC_Po:
 		/* FALLTHROUGH */
 	case MDOC_Pq:
 		print_text(h, ")");
 		break;
 	case MDOC_Ql:
 		/* FALLTHROUGH */
 	case MDOC_So:
 		/* FALLTHROUGH */
 	case MDOC_Sq:
 		print_text(h, "\\(cq");
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 }
Index: vendor/mdocml/dist/mdoc_macro.c
===================================================================
--- vendor/mdocml/dist/mdoc_macro.c	(revision 275396)
+++ vendor/mdocml/dist/mdoc_macro.c	(revision 275397)
@@ -1,1822 +1,1548 @@
-/*	$Id: mdoc_macro.c,v 1.139 2014/08/01 17:27:44 schwarze Exp $ */
+/*	$Id: mdoc_macro.c,v 1.154 2014/11/29 04:31:35 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons 
- * Copyright (c) 2010, 2012, 2013 Ingo Schwarze 
+ * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mdoc.h"
 #include "mandoc.h"
 #include "libmdoc.h"
 #include "libmandoc.h"
 
 enum	rew {	/* see rew_dohalt() */
 	REWIND_NONE,
 	REWIND_THIS,
 	REWIND_MORE,
 	REWIND_FORCE,
 	REWIND_LATER,
 	REWIND_ERROR
 };
 
-static	int		blk_full(MACRO_PROT_ARGS);
-static	int		blk_exp_close(MACRO_PROT_ARGS);
-static	int		blk_part_exp(MACRO_PROT_ARGS);
-static	int		blk_part_imp(MACRO_PROT_ARGS);
-static	int		ctx_synopsis(MACRO_PROT_ARGS);
-static	int		in_line_eoln(MACRO_PROT_ARGS);
-static	int		in_line_argn(MACRO_PROT_ARGS);
-static	int		in_line(MACRO_PROT_ARGS);
-static	int		phrase_ta(MACRO_PROT_ARGS);
+static	void		blk_full(MACRO_PROT_ARGS);
+static	void		blk_exp_close(MACRO_PROT_ARGS);
+static	void		blk_part_exp(MACRO_PROT_ARGS);
+static	void		blk_part_imp(MACRO_PROT_ARGS);
+static	void		ctx_synopsis(MACRO_PROT_ARGS);
+static	void		in_line_eoln(MACRO_PROT_ARGS);
+static	void		in_line_argn(MACRO_PROT_ARGS);
+static	void		in_line(MACRO_PROT_ARGS);
+static	void		phrase_ta(MACRO_PROT_ARGS);
 
-static	int		dword(struct mdoc *, int, int, const char *,
+static	void		dword(struct mdoc *, int, int, const char *,
 				 enum mdelim, int);
-static	int		append_delims(struct mdoc *,
-				int, int *, char *);
+static	void		append_delims(struct mdoc *, int, int *, char *);
 static	enum mdoct	lookup(enum mdoct, const char *);
-static	enum mdoct	lookup_raw(const char *);
+static	int		macro_or_word(MACRO_PROT_ARGS, int);
 static	int		make_pending(struct mdoc_node *, enum mdoct,
 				struct mdoc *, int, int);
-static	int		phrase(struct mdoc *, int, int, char *);
+static	int		parse_rest(struct mdoc *, enum mdoct,
+				int, int *, char *);
 static	enum mdoct	rew_alt(enum mdoct);
 static	enum rew	rew_dohalt(enum mdoct, enum mdoc_type,
 				const struct mdoc_node *);
-static	int		rew_elem(struct mdoc *, enum mdoct);
-static	int		rew_last(struct mdoc *,
-				const struct mdoc_node *);
-static	int		rew_sub(enum mdoc_type, struct mdoc *,
+static	void		rew_elem(struct mdoc *, enum mdoct);
+static	void		rew_last(struct mdoc *, const struct mdoc_node *);
+static	void		rew_sub(enum mdoc_type, struct mdoc *,
 				enum mdoct, int, int);
 
 const	struct mdoc_macro __mdoc_macros[MDOC_MAX] = {
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED | 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 | 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 | MDOC_JOIN }, /* Ed */
 	{ blk_full, MDOC_EXPLICIT }, /* Bl */
 	{ 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 | MDOC_JOIN }, /* 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_eoln, 0 }, /* Ex */
 	{ 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 | MDOC_JOIN }, /* Ic */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* In */
 	{ 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 */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Ot */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Pa */
 	{ in_line_eoln, 0 }, /* Rv */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* St */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* Va */
 	{ ctx_synopsis, MDOC_CALLABLE | MDOC_PARSED }, /* Vt */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Xr */
 	{ 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, MDOC_JOIN }, /* %O */
 	{ in_line_eoln, 0 }, /* %P */
 	{ in_line_eoln, MDOC_JOIN }, /* %R */
 	{ in_line_eoln, MDOC_JOIN }, /* %T */
 	{ in_line_eoln, 0 }, /* %V */
 	{ 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_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_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, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* No */
 	{ 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_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 | 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_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_argn, 0 }, /* Sm */
 	{ 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 | 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_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 | MDOC_JOIN }, /* Ek */
 	{ in_line_eoln, 0 }, /* Bt */
 	{ in_line_eoln, 0 }, /* Hf */
 	{ in_line, MDOC_CALLABLE | MDOC_PARSED }, /* 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 | 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 */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Es */
 	{ blk_part_imp, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* En */
 	{ in_line_argn, MDOC_CALLABLE | MDOC_PARSED }, /* Dx */
 	{ 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 | MDOC_JOIN }, /* Ta */
-	{ in_line_eoln, 0 }, /* ll */
+	{ in_line_eoln, MDOC_PROLOGUE }, /* ll */
 };
 
 const	struct mdoc_macro * const mdoc_macros = __mdoc_macros;
 
 
 /*
  * This is called at the end of parsing.  It must traverse up the tree,
  * closing out open [implicit] scopes.  Obviously, open explicit scopes
  * are errors.
  */
-int
+void
 mdoc_macroend(struct mdoc *mdoc)
 {
 	struct mdoc_node *n;
 
 	/* Scan for open explicit scopes. */
 
-	n = MDOC_VALID & mdoc->last->flags ?
+	n = mdoc->last->flags & MDOC_VALID ?
 	    mdoc->last->parent : mdoc->last;
 
 	for ( ; n; n = n->parent)
-		if (MDOC_BLOCK == n->type &&
-		    MDOC_EXPLICIT & mdoc_macros[n->tok].flags)
+		if (n->type == MDOC_BLOCK &&
+		    mdoc_macros[n->tok].flags & MDOC_EXPLICIT)
 			mandoc_msg(MANDOCERR_BLK_NOEND, mdoc->parse,
 			    n->line, n->pos, mdoc_macronames[n->tok]);
 
 	/* Rewind to the first. */
 
-	return(rew_last(mdoc, mdoc->first));
+	rew_last(mdoc, mdoc->first);
 }
 
 /*
- * Look up a macro from within a subsequent context.
+ * Look up the macro at *p called by "from",
+ * or as a line macro if from == MDOC_MAX.
  */
 static enum mdoct
 lookup(enum mdoct from, const char *p)
 {
-
-	if ( ! (MDOC_PARSED & mdoc_macros[from].flags))
-		return(MDOC_MAX);
-	return(lookup_raw(p));
-}
-
-/*
- * Lookup a macro following the initial line macro.
- */
-static enum mdoct
-lookup_raw(const char *p)
-{
 	enum mdoct	 res;
 
-	if (MDOC_MAX == (res = mdoc_hash_find(p)))
-		return(MDOC_MAX);
-	if (MDOC_CALLABLE & mdoc_macros[res].flags)
-		return(res);
+	if (from == MDOC_MAX || mdoc_macros[from].flags & MDOC_PARSED) {
+		res = mdoc_hash_find(p);
+		if (res != MDOC_MAX && mdoc_macros[res].flags & MDOC_CALLABLE)
+			return(res);
+	}
 	return(MDOC_MAX);
 }
 
-static int
+static void
 rew_last(struct mdoc *mdoc, const struct mdoc_node *to)
 {
 	struct mdoc_node *n, *np;
 
 	assert(to);
 	mdoc->next = MDOC_NEXT_SIBLING;
-
-
 	while (mdoc->last != to) {
 		/*
 		 * Save the parent here, because we may delete the
 		 * 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;
-		if ( ! mdoc_valid_post(mdoc))
-			return(0);
+		mdoc_valid_post(mdoc);
 		n = mdoc->last;
 		mdoc->last = np;
 		assert(mdoc->last);
 		mdoc->last->last = n;
 	}
-
-	return(mdoc_valid_post(mdoc));
+	mdoc_valid_post(mdoc);
 }
 
 /*
  * For a block closing macro, return the corresponding opening one.
  * Otherwise, return the macro itself.
  */
 static enum mdoct
 rew_alt(enum mdoct tok)
 {
 	switch (tok) {
 	case MDOC_Ac:
 		return(MDOC_Ao);
 	case MDOC_Bc:
 		return(MDOC_Bo);
 	case MDOC_Brc:
 		return(MDOC_Bro);
 	case MDOC_Dc:
 		return(MDOC_Do);
 	case MDOC_Ec:
 		return(MDOC_Eo);
 	case MDOC_Ed:
 		return(MDOC_Bd);
 	case MDOC_Ef:
 		return(MDOC_Bf);
 	case MDOC_Ek:
 		return(MDOC_Bk);
 	case MDOC_El:
 		return(MDOC_Bl);
 	case MDOC_Fc:
 		return(MDOC_Fo);
 	case MDOC_Oc:
 		return(MDOC_Oo);
 	case MDOC_Pc:
 		return(MDOC_Po);
 	case MDOC_Qc:
 		return(MDOC_Qo);
 	case MDOC_Re:
 		return(MDOC_Rs);
 	case MDOC_Sc:
 		return(MDOC_So);
 	case MDOC_Xc:
 		return(MDOC_Xo);
 	default:
 		return(tok);
 	}
 	/* NOTREACHED */
 }
 
 /*
  * Rewinding to tok, how do we have to handle *p?
  * REWIND_NONE: *p would delimit tok, but no tok scope is open
  *   inside *p, so there is no need to rewind anything at all.
  * REWIND_THIS: *p matches tok, so rewind *p and nothing else.
  * REWIND_MORE: *p is implicit, rewind it and keep searching for tok.
  * REWIND_FORCE: *p is explicit, but tok is full, force rewinding *p.
  * REWIND_LATER: *p is explicit and still open, postpone rewinding.
  * REWIND_ERROR: No tok block is open at all.
  */
 static enum rew
 rew_dohalt(enum mdoct tok, enum mdoc_type type,
 		const struct mdoc_node *p)
 {
 
 	/*
 	 * No matching token, no delimiting block, no broken block.
 	 * This can happen when full implicit macros are called for
 	 * the first time but try to rewind their previous
 	 * instance anyway.
 	 */
 	if (MDOC_ROOT == p->type)
 		return(MDOC_BLOCK == type &&
 		    MDOC_EXPLICIT & mdoc_macros[tok].flags ?
 		    REWIND_ERROR : REWIND_NONE);
 
 	/*
 	 * When starting to rewind, skip plain text
 	 * and nodes that have already been rewound.
 	 */
 	if (MDOC_TEXT == p->type || MDOC_VALID & p->flags)
 		return(REWIND_MORE);
 
 	/*
 	 * The easiest case:  Found a matching token.
 	 * This applies to both blocks and elements.
 	 */
 	tok = rew_alt(tok);
 	if (tok == p->tok)
 		return(p->end ? REWIND_NONE :
 		    type == p->type ? REWIND_THIS : REWIND_MORE);
 
 	/*
 	 * While elements do require rewinding for themselves,
 	 * they never affect rewinding of other nodes.
 	 */
 	if (MDOC_ELEM == p->type)
 		return(REWIND_MORE);
 
 	/*
 	 * Blocks delimited by our target token get REWIND_MORE.
 	 * Blocks delimiting our target token get REWIND_NONE.
 	 */
 	switch (tok) {
 	case MDOC_Bl:
 		if (MDOC_It == p->tok)
 			return(REWIND_MORE);
 		break;
 	case MDOC_It:
 		if (MDOC_BODY == p->type && MDOC_Bl == p->tok)
 			return(REWIND_NONE);
 		break;
 	/*
 	 * XXX Badly nested block handling still fails badly
 	 * when one block is breaking two blocks of the same type.
 	 * This is an incomplete and extremely ugly workaround,
 	 * required to let the OpenBSD tree build.
 	 */
 	case MDOC_Oo:
 		if (MDOC_Op == p->tok)
 			return(REWIND_MORE);
 		break;
 	case MDOC_Nm:
 		return(REWIND_NONE);
 	case MDOC_Nd:
 		/* FALLTHROUGH */
 	case MDOC_Ss:
 		if (MDOC_BODY == p->type && MDOC_Sh == p->tok)
 			return(REWIND_NONE);
 		/* FALLTHROUGH */
 	case MDOC_Sh:
 		if (MDOC_ROOT == p->parent->type)
 			return(REWIND_THIS);
 		if (MDOC_Nd == p->tok || MDOC_Ss == p->tok ||
 		    MDOC_Sh == p->tok)
 			return(REWIND_MORE);
 		break;
 	default:
 		break;
 	}
 
 	/*
 	 * Default block rewinding rules.
 	 * In particular, always skip block end markers,
 	 * and let all blocks rewind Nm children.
+	 * Do not warn again when closing a block,
+	 * since closing the body already warned.
 	 */
 	if (ENDBODY_NOT != p->end || MDOC_Nm == p->tok ||
-	    (MDOC_BLOCK == p->type &&
+	    MDOC_BLOCK == type || (MDOC_BLOCK == p->type &&
 	    ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)))
 		return(REWIND_MORE);
 
 	/*
 	 * By default, closing out full blocks
 	 * forces closing of broken explicit blocks,
 	 * while closing out partial blocks
 	 * allows delayed rewinding by default.
 	 */
 	return (&blk_full == mdoc_macros[tok].fp ?
 	    REWIND_FORCE : REWIND_LATER);
 }
 
-static int
+static void
 rew_elem(struct mdoc *mdoc, enum mdoct tok)
 {
 	struct mdoc_node *n;
 
 	n = mdoc->last;
 	if (MDOC_ELEM != n->type)
 		n = n->parent;
 	assert(MDOC_ELEM == n->type);
 	assert(tok == n->tok);
-
-	return(rew_last(mdoc, n));
+	rew_last(mdoc, n);
 }
 
 /*
  * We are trying to close a block identified by tok,
  * but the child block *broken is still open.
  * Thus, postpone closing the tok block
  * until the rew_sub call closing *broken.
  */
 static int
 make_pending(struct mdoc_node *broken, enum mdoct tok,
 		struct mdoc *mdoc, int line, int ppos)
 {
 	struct mdoc_node *breaker;
 
 	/*
 	 * Iterate backwards, searching for the block matching tok,
 	 * that is, the block breaking the *broken block.
 	 */
 	for (breaker = broken->parent; breaker; breaker = breaker->parent) {
 
 		/*
 		 * If the *broken block had already been broken before
 		 * and we encounter its breaker, make the tok block
 		 * pending on the inner breaker.
 		 * Graphically, "[A breaker=[B broken=[C->B B] tok=A] C]"
 		 * becomes "[A broken=[B [C->B B] tok=A] C]"
 		 * and finally "[A [B->A [C->B B] A] C]".
 		 */
 		if (breaker == broken->pending) {
 			broken = breaker;
 			continue;
 		}
 
 		if (REWIND_THIS != rew_dohalt(tok, MDOC_BLOCK, breaker))
 			continue;
 		if (MDOC_BODY == broken->type)
 			broken = broken->parent;
 
 		/*
 		 * Found the breaker.
 		 * If another, outer breaker is already pending on
 		 * the *broken block, we must not clobber the link
 		 * to the outer breaker, but make it pending on the
 		 * new, now inner breaker.
 		 * Graphically, "[A breaker=[B broken=[C->A A] tok=B] C]"
 		 * becomes "[A breaker=[B->A broken=[C A] tok=B] C]"
 		 * and finally "[A [B->A [C->B A] B] C]".
 		 */
 		if (broken->pending) {
 			struct mdoc_node *taker;
 
 			/*
 			 * If the breaker had also been broken before,
 			 * it cannot take on the outer breaker itself,
 			 * but must hand it on to its own breakers.
 			 * Graphically, this is the following situation:
 			 * "[A [B breaker=[C->B B] broken=[D->A A] tok=C] D]"
 			 * "[A taker=[B->A breaker=[C->B B] [D->C A] C] D]"
 			 */
 			taker = breaker;
 			while (taker->pending)
 				taker = taker->pending;
 			taker->pending = broken->pending;
 		}
 		broken->pending = breaker;
 		mandoc_vmsg(MANDOCERR_BLK_NEST, mdoc->parse, line, ppos,
 		    "%s breaks %s", mdoc_macronames[tok],
 		    mdoc_macronames[broken->tok]);
 		return(1);
 	}
 
 	/*
 	 * Found no matching block for tok.
 	 * Are you trying to close a block that is not open?
 	 */
 	return(0);
 }
 
-static int
+static void
 rew_sub(enum mdoc_type t, struct mdoc *mdoc,
 		enum mdoct tok, int line, int ppos)
 {
 	struct mdoc_node *n;
 
 	n = mdoc->last;
 	while (n) {
 		switch (rew_dohalt(tok, t, n)) {
 		case REWIND_NONE:
-			return(1);
+			return;
 		case REWIND_THIS:
 			n->lastline = line -
-			    (MDOC_NEWLINE & mdoc->flags &&
-			     ! (MDOC_EXPLICIT & mdoc_macros[tok].flags));
+			    (mdoc->flags & MDOC_NEWLINE &&
+			     ! (mdoc_macros[tok].flags & MDOC_EXPLICIT));
 			break;
 		case REWIND_FORCE:
 			mandoc_vmsg(MANDOCERR_BLK_BROKEN, 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);
+			    (mdoc->flags & MDOC_NEWLINE ? 1 : 0);
 			n = n->parent;
 			continue;
 		case REWIND_LATER:
 			if (make_pending(n, tok, mdoc, line, ppos) ||
-			    MDOC_BLOCK != t)
-				return(1);
+			    t != MDOC_BLOCK)
+				return;
 			/* FALLTHROUGH */
 		case REWIND_ERROR:
 			mandoc_msg(MANDOCERR_BLK_NOTOPEN,
 			    mdoc->parse, line, ppos,
 			    mdoc_macronames[tok]);
-			return(1);
+			return;
 		}
 		break;
 	}
-
 	assert(n);
-	if ( ! rew_last(mdoc, n))
-		return(0);
+	rew_last(mdoc, n);
 
 	/*
 	 * The current block extends an enclosing block.
 	 * Now that the current block ends, close the enclosing block, too.
 	 */
-	while (NULL != (n = n->pending)) {
-		if ( ! rew_last(mdoc, n))
-			return(0);
-		if (MDOC_HEAD == n->type &&
-		    ! mdoc_body_alloc(mdoc, n->line, n->pos, n->tok))
-			return(0);
+	while ((n = n->pending) != NULL) {
+		rew_last(mdoc, n);
+		if (n->type == MDOC_HEAD)
+			mdoc_body_alloc(mdoc, n->line, n->pos, n->tok);
 	}
-
-	return(1);
 }
 
 /*
  * Allocate a word and check whether it's punctuation or not.
  * Punctuation consists of those tokens found in mdoc_isdelim().
  */
-static int
+static void
 dword(struct mdoc *mdoc, int line, int col, const char *p,
 		enum mdelim d, int may_append)
 {
 
-	if (DELIM_MAX == d)
+	if (d == DELIM_MAX)
 		d = mdoc_isdelim(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->flags & (MDOC_SYNOPSIS | MDOC_KEEP | MDOC_SMOFF)) &&
+	    d == DELIM_NONE && mdoc->last->type == MDOC_TEXT &&
+	    mdoc_isdelim(mdoc->last->string) == DELIM_NONE) {
 		mdoc_word_append(mdoc, p);
-		return(1);
+		return;
 	}
 
-	if ( ! mdoc_word_alloc(mdoc, line, col, p))
-		return(0);
+	mdoc_word_alloc(mdoc, line, col, p);
 
-	if (DELIM_OPEN == d)
-		mdoc->last->flags |= MDOC_DELIMO;
-
 	/*
-	 * Closing delimiters only suppress the preceding space
-	 * when they follow something, not when they start a new
-	 * block or element, and not when they follow `No'.
-	 *
-	 * XXX	Explicitly special-casing MDOC_No here feels
-	 *	like a layering violation.  Find a better way
-	 *	and solve this in the code related to `No'!
+	 * If the word consists of a bare delimiter,
+	 * flag the new node accordingly,
+	 * unless doing so was vetoed by the invoking macro.
+	 * Always clear the veto, it is only valid for one word.
 	 */
 
-	else if (DELIM_CLOSE == d && mdoc->last->prev &&
-	    mdoc->last->prev->tok != MDOC_No &&
+	if (d == DELIM_OPEN)
+		mdoc->last->flags |= MDOC_DELIMO;
+	else if (d == DELIM_CLOSE &&
+	    ! (mdoc->flags & MDOC_NODELIMC) &&
 	    mdoc->last->parent->tok != MDOC_Fd)
 		mdoc->last->flags |= MDOC_DELIMC;
-
-	return(1);
+	mdoc->flags &= ~MDOC_NODELIMC;
 }
 
-static int
+static void
 append_delims(struct mdoc *mdoc, int line, int *pos, char *buf)
 {
-	int		 la;
-	enum margserr	 ac;
 	char		*p;
+	int		 la;
 
-	if ('\0' == buf[*pos])
-		return(1);
+	if (buf[*pos] == '\0')
+		return;
 
 	for (;;) {
 		la = *pos;
-		ac = mdoc_zargs(mdoc, line, pos, buf, &p);
-
-		if (ARGS_ERROR == ac)
-			return(0);
-		else if (ARGS_EOLN == ac)
+		if (mdoc_args(mdoc, line, pos, buf, MDOC_MAX, &p) == ARGS_EOLN)
 			break;
-
 		dword(mdoc, line, la, p, DELIM_MAX, 1);
 
 		/*
 		 * If we encounter end-of-sentence symbols, then trigger
 		 * the double-space.
 		 *
 		 * XXX: it's easy to allow this to propagate outward to
 		 * the last symbol, such that `. )' will cause the
 		 * correct double-spacing.  However, (1) groff isn't
 		 * smart enough to do this and (2) it would require
 		 * knowing which symbols break this behaviour, for
 		 * example, `.  ;' shouldn't propagate the double-space.
 		 */
+
 		if (mandoc_eos(p, strlen(p)))
 			mdoc->last->flags |= MDOC_EOS;
 	}
+}
 
-	return(1);
+/*
+ * Parse one word.
+ * If it is a macro, call it and return 1.
+ * Otherwise, allocate it and return 0.
+ */
+static int
+macro_or_word(MACRO_PROT_ARGS, int parsed)
+{
+	char		*p;
+	enum mdoct	 ntok;
+
+	p = buf + ppos;
+	ntok = MDOC_MAX;
+	if (mdoc->flags & MDOC_PHRASELIT)
+		/* nothing */;
+	else if (*p == '"')
+		p++;
+	else if (parsed)
+		ntok = lookup(tok, p);
+
+	if (ntok == MDOC_MAX) {
+		dword(mdoc, line, ppos, p, DELIM_MAX, tok == MDOC_MAX ||
+		    mdoc_macros[tok].flags & MDOC_JOIN);
+		return(0);
+	} else {
+		if (mdoc_macros[tok].fp == in_line_eoln)
+			rew_elem(mdoc, tok);
+		mdoc_macro(mdoc, ntok, line, ppos, pos, buf);
+		if (tok == MDOC_MAX)
+			append_delims(mdoc, line, pos, buf);
+		return(1);
+	}
 }
 
 /*
  * Close out block partial/full explicit.
  */
-static int
+static void
 blk_exp_close(MACRO_PROT_ARGS)
 {
 	struct mdoc_node *body;		/* Our own body. */
+	struct mdoc_node *endbody;	/* Our own end marker. */
 	struct mdoc_node *later;	/* A sub-block starting later. */
 	struct mdoc_node *n;		/* For searching backwards. */
 
 	int		 j, lastarg, maxargs, flushed, nl;
 	enum margserr	 ac;
 	enum mdoct	 atok, ntok;
 	char		*p;
 
 	nl = MDOC_NEWLINE & mdoc->flags;
 
 	switch (tok) {
 	case MDOC_Ec:
 		maxargs = 1;
 		break;
 	case MDOC_Ek:
 		mdoc->flags &= ~MDOC_KEEP;
+		/* FALLTHROUGH */
 	default:
 		maxargs = 0;
 		break;
 	}
 
 	/*
 	 * Search backwards for beginnings of blocks,
 	 * both of our own and of pending sub-blocks.
 	 */
+
 	atok = rew_alt(tok);
-	body = later = NULL;
+	body = endbody = later = NULL;
 	for (n = mdoc->last; n; n = n->parent) {
-		if (MDOC_VALID & n->flags)
+		if (n->flags & MDOC_VALID)
 			continue;
 
 		/* Remember the start of our own body. */
-		if (MDOC_BODY == n->type && atok == n->tok) {
-			if (ENDBODY_NOT == n->end)
+
+		if (n->type == MDOC_BODY && atok == n->tok) {
+			if (n->end == ENDBODY_NOT)
 				body = n;
 			continue;
 		}
 
-		if (MDOC_BLOCK != n->type || MDOC_Nm == n->tok)
+		if (n->type != MDOC_BLOCK || n->tok == MDOC_Nm)
 			continue;
 		if (atok == n->tok) {
 			assert(body);
 
 			/*
 			 * Found the start of our own block.
 			 * When there is no pending sub block,
 			 * just proceed to closing out.
 			 */
-			if (NULL == later)
+
+			if (later == NULL)
 				break;
 
 			/*
 			 * When there is a pending sub block,
 			 * postpone closing out the current block
 			 * until the rew_sub() closing out the sub-block.
 			 */
+
 			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(mdoc, line, ppos,
-			    atok, body, ENDBODY_SPACE))
-				return(0);
+
+			mdoc_endbody_alloc(mdoc, line, ppos,
+			    atok, body, ENDBODY_SPACE);
+
+			/*
+			 * If a block closing macro taking arguments
+			 * breaks another block, put the arguments
+			 * into the end marker and remeber the
+			 * end marker in order to close it out.
+			 */
+
+			if (maxargs) {
+				endbody = mdoc->last;
+				mdoc->next = MDOC_NEXT_CHILD;
+			}
 			break;
 		}
 
 		/*
 		 * When finding an open sub block, remember the last
 		 * open explicit block, or, in case there are only
 		 * implicit ones, the first open implicit block.
 		 */
+
 		if (later &&
-		    MDOC_EXPLICIT & mdoc_macros[later->tok].flags)
+		    mdoc_macros[later->tok].flags & MDOC_EXPLICIT)
 			continue;
-		if (MDOC_It != n->tok)
+		if (n->tok != MDOC_It)
 			later = n;
 	}
+	rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
 
-	if ( ! (MDOC_PARSED & mdoc_macros[tok].flags)) {
-		if ('\0' != buf[*pos])
+	if ( ! (mdoc_macros[tok].flags & MDOC_PARSED)) {
+		if (buf[*pos] != '\0')
 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
 			    mdoc->parse, line, ppos,
 			    "%s %s", mdoc_macronames[tok],
 			    buf + *pos);
-		if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
-			return(0);
-		return(rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos));
+		rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
+		return;
 	}
 
-	if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
-		return(0);
+	if (maxargs && endbody == NULL) {
+		if (n == NULL) {
+			/*
+			 * Stray .Ec without previous .Eo:
+			 * Break the output line, ignore any arguments.
+			 */
+			mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL);
+			rew_elem(mdoc, MDOC_br);
+		} else
+			mdoc_tail_alloc(mdoc, line, ppos, atok);
+	}
 
-	if (NULL == later && maxargs > 0)
-		if ( ! mdoc_tail_alloc(mdoc, line, ppos, rew_alt(tok)))
-			return(0);
-
-	for (flushed = j = 0; ; j++) {
+	flushed = n == NULL;
+	for (j = 0; ; j++) {
 		lastarg = *pos;
 
 		if (j == maxargs && ! flushed) {
-			if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
-				return(0);
+			if (endbody == NULL)
+				rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
+			else
+				rew_last(mdoc, endbody);
 			flushed = 1;
 		}
 
 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
-
-		if (ARGS_ERROR == ac)
-			return(0);
-		if (ARGS_PUNCT == ac)
+		if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
 			break;
-		if (ARGS_EOLN == ac)
-			break;
 
-		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
+		ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
 
-		if (MDOC_MAX == ntok) {
-			if ( ! dword(mdoc, line, lastarg, p, DELIM_MAX,
-			    MDOC_JOIN & mdoc_macros[tok].flags))
-				return(0);
+		if (ntok == MDOC_MAX) {
+			dword(mdoc, line, lastarg, p, DELIM_MAX,
+			    MDOC_JOIN & mdoc_macros[tok].flags);
 			continue;
 		}
 
 		if ( ! flushed) {
-			if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
-				return(0);
+			if (endbody == NULL)
+				rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
+			else
+				rew_last(mdoc, endbody);
 			flushed = 1;
 		}
-
 		mdoc->flags &= ~MDOC_NEWLINE;
-
-		if ( ! mdoc_macro(mdoc, ntok, line, lastarg, pos, buf))
-			return(0);
+		mdoc_macro(mdoc, ntok, line, lastarg, pos, buf);
 		break;
 	}
 
-	if ( ! flushed && ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
-		return(0);
-
-	if ( ! nl)
-		return(1);
-	return(append_delims(mdoc, line, pos, buf));
+	if ( ! flushed) {
+		if (endbody == NULL)
+			rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
+		else
+			rew_last(mdoc, endbody);
+	}
+	if (nl)
+		append_delims(mdoc, line, pos, buf);
 }
 
-static int
+static void
 in_line(MACRO_PROT_ARGS)
 {
-	int		 la, scope, cnt, nc, nl;
-	enum margverr	 av;
+	int		 la, scope, cnt, firstarg, mayopen, nc, nl;
 	enum mdoct	 ntok;
 	enum margserr	 ac;
 	enum mdelim	 d;
 	struct mdoc_arg	*arg;
 	char		*p;
 
 	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/*
 	 * Whether we allow ignored elements (those without content,
 	 * usually because of reserved words) to squeak by.
 	 */
 
 	switch (tok) {
 	case MDOC_An:
 		/* FALLTHROUGH */
 	case MDOC_Ar:
 		/* FALLTHROUGH */
 	case MDOC_Fl:
 		/* FALLTHROUGH */
 	case MDOC_Mt:
 		/* FALLTHROUGH */
 	case MDOC_Nm:
 		/* FALLTHROUGH */
 	case MDOC_Pa:
 		nc = 1;
 		break;
 	default:
 		nc = 0;
 		break;
 	}
 
-	for (arg = NULL;; ) {
+	mdoc_argv(mdoc, line, tok, &arg, pos, buf);
+
+	d = DELIM_NONE;
+	firstarg = 1;
+	mayopen = 1;
+	for (cnt = scope = 0;; ) {
 		la = *pos;
-		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
+		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
 
-		if (ARGV_WORD == av) {
-			*pos = la;
+		/*
+		 * At the end of a macro line,
+		 * opening delimiters do not suppress spacing.
+		 */
+
+		if (ac == ARGS_EOLN) {
+			if (d == DELIM_OPEN)
+				mdoc->last->flags &= ~MDOC_DELIMO;
 			break;
 		}
-		if (ARGV_EOLN == av)
-			break;
-		if (ARGV_ARG == av)
-			continue;
 
-		mdoc_argv_free(arg);
-		return(0);
-	}
+		/*
+		 * The rest of the macro line is only punctuation,
+		 * to be handled by append_delims().
+		 * If there were no other arguments,
+		 * do not allow the first one to suppress spacing,
+		 * even if it turns out to be a closing one.
+		 */
 
-	for (cnt = scope = 0;; ) {
-		la = *pos;
-		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
-
-		if (ARGS_ERROR == ac)
-			return(0);
-		if (ARGS_EOLN == ac)
+		if (ac == ARGS_PUNCT) {
+			if (cnt == 0 && nc == 0)
+				mdoc->flags |= MDOC_NODELIMC;
 			break;
-		if (ARGS_PUNCT == ac)
-			break;
+		}
 
-		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
+		ntok = (ac == ARGS_QWORD || (tok == MDOC_Fn && !cnt)) ?
+		    MDOC_MAX : lookup(tok, p);
 
 		/*
 		 * In this case, we've located a submacro and must
 		 * execute it.  Close out scope, if open.  If no
 		 * elements have been generated, either create one (nc)
 		 * or raise a warning.
 		 */
 
-		if (MDOC_MAX != ntok) {
-			if (scope && ! rew_elem(mdoc, tok))
-				return(0);
-			if (nc && 0 == cnt) {
-				if ( ! mdoc_elem_alloc(mdoc,
-				    line, ppos, tok, arg))
-					return(0);
-				if ( ! rew_last(mdoc, mdoc->last))
-					return(0);
-			} else if ( ! nc && 0 == cnt) {
+		if (ntok != MDOC_MAX) {
+			if (scope)
+				rew_elem(mdoc, tok);
+			if (nc && ! cnt) {
+				mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
+				rew_last(mdoc, mdoc->last);
+			} else if ( ! nc && ! cnt) {
 				mdoc_argv_free(arg);
 				mandoc_msg(MANDOCERR_MACRO_EMPTY,
 				    mdoc->parse, line, ppos,
 				    mdoc_macronames[tok]);
 			}
-
-			if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
-				return(0);
-			if ( ! nl)
-				return(1);
-			return(append_delims(mdoc, line, pos, buf));
+			mdoc_macro(mdoc, ntok, line, la, pos, buf);
+			if (nl)
+				append_delims(mdoc, line, pos, buf);
+			return;
 		}
 
 		/*
 		 * Non-quote-enclosed punctuation.  Set up our scope, if
 		 * a word; rewind the scope, if a delimiter; then append
 		 * the word.
 		 */
 
-		d = ARGS_QWORD == ac ? DELIM_NONE : mdoc_isdelim(p);
+		d = ac == ARGS_QWORD ? DELIM_NONE : mdoc_isdelim(p);
 
 		if (DELIM_NONE != d) {
 			/*
 			 * If we encounter closing punctuation, no word
-			 * has been omitted, no scope is open, and we're
+			 * has been emitted, no scope is open, and we're
 			 * allowed to have an empty element, then start
-			 * a new scope.  `Ar', `Fl', and `Li', only do
-			 * this once per invocation.  There may be more
-			 * of these (all of them?).
+			 * a new scope.
 			 */
-			if (0 == cnt && (nc || MDOC_Li == tok) &&
-			    DELIM_CLOSE == d && ! scope) {
-				if ( ! mdoc_elem_alloc(mdoc,
-				    line, ppos, tok, arg))
-					return(0);
-				if (MDOC_Ar == tok || MDOC_Li == tok ||
-				    MDOC_Fl == tok)
-					cnt++;
+			if ((d == DELIM_CLOSE ||
+			     (d == DELIM_MIDDLE && tok == MDOC_Fl)) &&
+			    !cnt && !scope && nc && mayopen) {
+				mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
 				scope = 1;
+				cnt++;
+				if (tok == MDOC_Nm)
+					mayopen = 0;
 			}
 			/*
 			 * Close out our scope, if one is open, before
 			 * any punctuation.
 			 */
-			if (scope && ! rew_elem(mdoc, tok))
-				return(0);
+			if (scope)
+				rew_elem(mdoc, tok);
 			scope = 0;
-		} else if ( ! scope) {
-			if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
-				return(0);
+			if (tok == MDOC_Fn)
+				mayopen = 0;
+		} else if (mayopen && !scope) {
+			mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
 			scope = 1;
+			cnt++;
 		}
 
-		if (DELIM_NONE == d)
-			cnt++;
+		dword(mdoc, line, la, p, d,
+		    MDOC_JOIN & mdoc_macros[tok].flags);
 
-		if ( ! dword(mdoc, line, la, p, d,
-		    MDOC_JOIN & mdoc_macros[tok].flags))
-			return(0);
+		/*
+		 * If the first argument is a closing delimiter,
+		 * do not suppress spacing before it.
+		 */
 
+		if (firstarg && d == DELIM_CLOSE && !nc)
+			mdoc->last->flags &= ~MDOC_DELIMC;
+		firstarg = 0;
+
 		/*
 		 * `Fl' macros have their scope re-opened with each new
 		 * word so that the `-' can be added to each one without
 		 * having to parse out spaces.
 		 */
-		if (scope && MDOC_Fl == tok) {
-			if ( ! rew_elem(mdoc, tok))
-				return(0);
+		if (scope && tok == MDOC_Fl) {
+			rew_elem(mdoc, tok);
 			scope = 0;
 		}
 	}
 
-	if (scope && ! rew_elem(mdoc, tok))
-		return(0);
+	if (scope)
+		rew_elem(mdoc, tok);
 
 	/*
 	 * If no elements have been collected and we're allowed to have
 	 * empties (nc), open a scope and close it out.  Otherwise,
 	 * raise a warning.
 	 */
 
-	if (nc && 0 == cnt) {
-		if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
-			return(0);
-		if ( ! rew_last(mdoc, mdoc->last))
-			return(0);
-	} else if ( ! nc && 0 == cnt) {
-		mdoc_argv_free(arg);
-		mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
-		    line, ppos, mdoc_macronames[tok]);
+	if ( ! cnt) {
+		if (nc) {
+			mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
+			rew_last(mdoc, mdoc->last);
+		} else {
+			mdoc_argv_free(arg);
+			mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
+			    line, ppos, mdoc_macronames[tok]);
+		}
 	}
-
-	if ( ! nl)
-		return(1);
-	return(append_delims(mdoc, line, pos, buf));
+	if (nl)
+		append_delims(mdoc, line, pos, buf);
 }
 
-static int
+static void
 blk_full(MACRO_PROT_ARGS)
 {
-	int		  la, nl, nparsed;
+	int		  la, nl, parsed;
 	struct mdoc_arg	 *arg;
 	struct mdoc_node *head; /* save of head macro */
 	struct mdoc_node *body; /* save of body macro */
 	struct mdoc_node *n;
-	enum mdoc_type	  mtt;
-	enum mdoct	  ntok;
 	enum margserr	  ac, lac;
-	enum margverr	  av;
 	char		 *p;
 
 	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/* Skip items outside lists. */
 
 	if (tok == MDOC_It) {
 		for (n = mdoc->last; n; n = n->parent)
-			if (n->tok == MDOC_Bl)
+			if (n->tok == MDOC_Bl &&
+			    ! (n->flags & MDOC_VALID))
 				break;
 		if (n == NULL) {
 			mandoc_vmsg(MANDOCERR_IT_STRAY, mdoc->parse,
 			    line, ppos, "It %s", buf + *pos);
-			if ( ! mdoc_elem_alloc(mdoc, line, ppos,
-			    MDOC_br, NULL))
-				return(0);
-			return(rew_elem(mdoc, MDOC_br));
+			mdoc_elem_alloc(mdoc, line, ppos, MDOC_br, NULL);
+			rew_elem(mdoc, MDOC_br);
+			return;
 		}
 	}
 
 	/* Close out prior implicit scope. */
 
-	if ( ! (MDOC_EXPLICIT & mdoc_macros[tok].flags)) {
-		if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
-			return(0);
-		if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
-			return(0);
+	if ( ! (mdoc_macros[tok].flags & MDOC_EXPLICIT)) {
+		rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
+		rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
 	}
 
 	/*
 	 * This routine accommodates implicitly- and explicitly-scoped
 	 * macro openings.  Implicit ones first close out prior scope
 	 * (seen above).  Delay opening the head until necessary to
 	 * allow leading punctuation to print.  Special consideration
 	 * for `It -column', which has phrase-part syntax instead of
 	 * regular child nodes.
 	 */
 
-	for (arg = NULL;; ) {
-		la = *pos;
-		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
-
-		if (ARGV_WORD == av) {
-			*pos = la;
-			break;
-		}
-
-		if (ARGV_EOLN == av)
-			break;
-		if (ARGV_ARG == av)
-			continue;
-
-		mdoc_argv_free(arg);
-		return(0);
-	}
-
-	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, arg))
-		return(0);
-
+	mdoc_argv(mdoc, line, tok, &arg, pos, buf);
+	mdoc_block_alloc(mdoc, line, ppos, tok, arg);
 	head = body = NULL;
 
 	/*
 	 * Exception: Heads of `It' macros in `-diag' lists are not
 	 * parsed, even though `It' macros in general are parsed.
 	 */
-	nparsed = MDOC_It == tok &&
-	    MDOC_Bl == mdoc->last->parent->tok &&
-	    LIST_diag == mdoc->last->parent->norm->Bl.type;
 
+	parsed = tok != MDOC_It ||
+	    mdoc->last->parent->tok != MDOC_Bl ||
+	    mdoc->last->parent->norm->Bl.type != LIST_diag;
+
 	/*
 	 * The `Nd' macro has all arguments in its body: it's a hybrid
 	 * of block partial-explicit and full-implicit.  Stupid.
 	 */
 
-	if (MDOC_Nd == tok) {
-		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
-			return(0);
-		head = mdoc->last;
-		if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
-			return(0);
-		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
-			return(0);
-		body = mdoc->last;
+	if (tok == MDOC_Nd) {
+		head = mdoc_head_alloc(mdoc, line, ppos, tok);
+		rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
+		body = mdoc_body_alloc(mdoc, line, ppos, tok);
 	}
 
-	if (MDOC_Bk == tok)
+	if (tok == MDOC_Bk)
 		mdoc->flags |= MDOC_KEEP;
 
-	ac = ARGS_ERROR;
-
-	for ( ; ; ) {
+	ac = ARGS_PEND;
+	for (;;) {
 		la = *pos;
-		/* Initialise last-phrase-type with ARGS_PEND. */
-		lac = ARGS_ERROR == ac ? ARGS_PEND : ac;
+		lac = ac;
 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
-
-		if (ARGS_PUNCT == ac)
+		if (ac == ARGS_PUNCT)
 			break;
-
-		if (ARGS_ERROR == ac)
-			return(0);
-
-		if (ARGS_EOLN == ac) {
-			if (ARGS_PPHRASE != lac && ARGS_PHRASE != lac)
+		if (ac == ARGS_EOLN) {
+			if (lac != ARGS_PPHRASE && lac != ARGS_PHRASE)
 				break;
 			/*
 			 * This is necessary: if the last token on a
 			 * line is a `Ta' or tab, then we'll get
 			 * ARGS_EOLN, so we must be smart enough to
 			 * reopen our scope if the last parse was a
 			 * phrase or partial phrase.
 			 */
-			if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
-				return(0);
-			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
-				return(0);
-			body = mdoc->last;
+			rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
+			body = mdoc_body_alloc(mdoc, line, ppos, tok);
 			break;
 		}
 
 		/*
 		 * Emit leading punctuation (i.e., punctuation before
 		 * the MDOC_HEAD) for non-phrase types.
 		 */
 
-		if (NULL == head &&
-		    ARGS_PEND != ac &&
-		    ARGS_PHRASE != ac &&
-		    ARGS_PPHRASE != ac &&
-		    ARGS_QWORD != ac &&
-		    DELIM_OPEN == mdoc_isdelim(p)) {
-			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
-				return(0);
+		if (head == NULL &&
+		    ac != ARGS_PEND &&
+		    ac != ARGS_PHRASE &&
+		    ac != ARGS_PPHRASE &&
+		    ac != ARGS_QWORD &&
+		    mdoc_isdelim(p) == DELIM_OPEN) {
+			dword(mdoc, line, la, p, DELIM_OPEN, 0);
 			continue;
 		}
 
 		/* Open a head if one hasn't been opened. */
 
-		if (NULL == head) {
-			if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
-				return(0);
-			head = mdoc->last;
-		}
+		if (head == NULL)
+			head = mdoc_head_alloc(mdoc, line, ppos, tok);
 
-		if (ARGS_PHRASE == ac ||
-		    ARGS_PEND == ac ||
-		    ARGS_PPHRASE == ac) {
+		if (ac == ARGS_PHRASE ||
+		    ac == ARGS_PEND ||
+		    ac == ARGS_PPHRASE) {
+
 			/*
 			 * If we haven't opened a body yet, rewind the
 			 * head; if we have, rewind that instead.
 			 */
 
-			mtt = body ? MDOC_BODY : MDOC_HEAD;
-			if ( ! rew_sub(mtt, mdoc, tok, line, ppos))
-				return(0);
+			rew_sub(body ? MDOC_BODY : MDOC_HEAD,
+			    mdoc, tok, line, ppos);
+			body = mdoc_body_alloc(mdoc, line, ppos, tok);
 
-			/* Then allocate our body context. */
-
-			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
-				return(0);
-			body = mdoc->last;
-
 			/*
 			 * Process phrases: set whether we're in a
 			 * partial-phrase (this effects line handling)
 			 * then call down into the phrase parser.
 			 */
 
-			if (ARGS_PPHRASE == ac)
+			if (ac == ARGS_PPHRASE)
 				mdoc->flags |= MDOC_PPHRASE;
-			if (ARGS_PEND == ac && ARGS_PPHRASE == lac)
+			if (ac == ARGS_PEND && lac == ARGS_PPHRASE)
 				mdoc->flags |= MDOC_PPHRASE;
-
-			if ( ! phrase(mdoc, line, la, buf))
-				return(0);
-
+			parse_rest(mdoc, MDOC_MAX, line, &la, buf);
 			mdoc->flags &= ~MDOC_PPHRASE;
 			continue;
 		}
 
-		ntok = nparsed || ARGS_QWORD == ac ?
-		    MDOC_MAX : lookup(tok, p);
-
-		if (MDOC_MAX == ntok) {
-			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
-			    MDOC_JOIN & mdoc_macros[tok].flags))
-				return(0);
-			continue;
-		}
-
-		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
-			return(0);
-		break;
+		if (macro_or_word(mdoc, tok, line, la, pos, buf, parsed))
+			break;
 	}
 
-	if (NULL == head) {
-		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
-			return(0);
-		head = mdoc->last;
-	}
-
-	if (nl && ! append_delims(mdoc, line, pos, buf))
-		return(0);
-
-	/* If we've already opened our body, exit now. */
-
-	if (NULL != body)
+	if (head == NULL)
+		head = mdoc_head_alloc(mdoc, line, ppos, tok);
+	if (nl)
+		append_delims(mdoc, line, pos, buf);
+	if (body != NULL)
 		goto out;
 
 	/*
 	 * If there is an open (i.e., unvalidated) sub-block requiring
 	 * explicit close-out, postpone switching the current block from
 	 * head to body until the rew_sub() call closing out that
 	 * sub-block.
 	 */
 	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)) {
+		if (n->type == MDOC_BLOCK &&
+		    mdoc_macros[n->tok].flags & MDOC_EXPLICIT &&
+		    ! (n->flags & MDOC_VALID)) {
 			n->pending = head;
-			return(1);
+			return;
 		}
 	}
 
 	/* Close out scopes to remain in a consistent state. */
 
-	if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
-		return(0);
-	if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
-		return(0);
-
+	rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
+	mdoc_body_alloc(mdoc, line, ppos, tok);
 out:
-	if ( ! (MDOC_FREECOL & mdoc->flags))
-		return(1);
-
-	if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
-		return(0);
-	if ( ! rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos))
-		return(0);
-
-	mdoc->flags &= ~MDOC_FREECOL;
-	return(1);
+	if (mdoc->flags & MDOC_FREECOL) {
+		rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
+		rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
+		mdoc->flags &= ~MDOC_FREECOL;
+	}
 }
 
-static int
+static void
 blk_part_imp(MACRO_PROT_ARGS)
 {
 	int		  la, nl;
-	enum mdoct	  ntok;
 	enum margserr	  ac;
 	char		 *p;
 	struct mdoc_node *blk; /* saved block context */
 	struct mdoc_node *body; /* saved body context */
 	struct mdoc_node *n;
 
 	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/*
 	 * A macro that spans to the end of the line.  This is generally
 	 * (but not necessarily) called as the first macro.  The block
 	 * has a head as the immediate child, which is always empty,
 	 * followed by zero or more opening punctuation nodes, then the
 	 * body (which may be empty, depending on the macro), then zero
 	 * or more closing punctuation nodes.
 	 */
 
-	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
-		return(0);
+	blk = mdoc_block_alloc(mdoc, line, ppos, tok, NULL);
+	mdoc_head_alloc(mdoc, line, ppos, tok);
+	rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
 
-	blk = mdoc->last;
-
-	if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
-		return(0);
-	if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
-		return(0);
-
 	/*
 	 * Open the body scope "on-demand", that is, after we've
 	 * processed all our the leading delimiters (open parenthesis,
 	 * etc.).
 	 */
 
 	for (body = NULL; ; ) {
 		la = *pos;
 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
-
-		if (ARGS_ERROR == ac)
-			return(0);
-		if (ARGS_EOLN == ac)
+		if (ac == ARGS_EOLN || ac == ARGS_PUNCT)
 			break;
-		if (ARGS_PUNCT == ac)
-			break;
 
-		if (NULL == body && ARGS_QWORD != ac &&
-		    DELIM_OPEN == mdoc_isdelim(p)) {
-			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
-				return(0);
+		if (body == NULL && ac != ARGS_QWORD &&
+		    mdoc_isdelim(p) == DELIM_OPEN) {
+			dword(mdoc, line, la, p, DELIM_OPEN, 0);
 			continue;
 		}
 
-		if (NULL == body) {
-		       if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
-			       return(0);
-			body = mdoc->last;
-		}
+		if (body == NULL)
+			body = mdoc_body_alloc(mdoc, line, ppos, tok);
 
-		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
-
-		if (MDOC_MAX == ntok) {
-			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
-			    MDOC_JOIN & mdoc_macros[tok].flags))
-				return(0);
-			continue;
-		}
-
-		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
-			return(0);
-		break;
+		if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
+			break;
 	}
+	if (body == NULL)
+		body = mdoc_body_alloc(mdoc, line, ppos, tok);
 
-	/* Clean-ups to leave in a consistent state. */
-
-	if (NULL == body) {
-		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
-			return(0);
-		body = mdoc->last;
-	}
-
 	/*
 	 * If there is an open sub-block requiring explicit close-out,
 	 * postpone closing out the current block
 	 * until the rew_sub() call closing out the sub-block.
 	 */
+
 	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)) {
+		if (n->type == MDOC_BLOCK &&
+		    mdoc_macros[n->tok].flags & MDOC_EXPLICIT &&
+		    ! (n->flags & MDOC_VALID)) {
 			make_pending(n, tok, mdoc, line, ppos);
-			if ( ! mdoc_endbody_alloc(mdoc, line, ppos,
-			    tok, body, ENDBODY_NOSPACE))
-				return(0);
-			return(1);
+			mdoc_endbody_alloc(mdoc, line, ppos,
+			    tok, body, ENDBODY_NOSPACE);
+			return;
 		}
 	}
 	assert(n == body);
+	rew_sub(MDOC_BODY, mdoc, tok, line, ppos);
+	if (nl)
+		append_delims(mdoc, line, pos, buf);
+	rew_sub(MDOC_BLOCK, mdoc, tok, line, ppos);
 
-	if ( ! rew_sub(MDOC_BODY, mdoc, tok, line, ppos))
-		return(0);
-
-	/* Standard appending of delimiters. */
-
-	if (nl && ! append_delims(mdoc, line, pos, buf))
-		return(0);
-
-	/* Rewind scope, if applicable. */
-
-	if ( ! 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)
+	if (n && n->tok == MDOC_Ns)
 		mdoc_node_relink(mdoc, n);
-
-	return(1);
 }
 
-static int
+static void
 blk_part_exp(MACRO_PROT_ARGS)
 {
 	int		  la, nl;
 	enum margserr	  ac;
 	struct mdoc_node *head; /* keep track of head */
 	struct mdoc_node *body; /* keep track of body */
 	char		 *p;
-	enum mdoct	  ntok;
 
 	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/*
 	 * The opening of an explicit macro having zero or more leading
 	 * punctuation nodes; a head with optional single element (the
 	 * case of `Eo'); and a body that may be empty.
 	 */
 
-	if ( ! mdoc_block_alloc(mdoc, line, ppos, tok, NULL))
-		return(0);
-
+	mdoc_block_alloc(mdoc, line, ppos, tok, NULL);
 	for (head = body = NULL; ; ) {
 		la = *pos;
 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
-
-		if (ARGS_ERROR == ac)
-			return(0);
-		if (ARGS_PUNCT == ac)
+		if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
 			break;
-		if (ARGS_EOLN == ac)
-			break;
 
 		/* Flush out leading punctuation. */
 
-		if (NULL == head && ARGS_QWORD != ac &&
-		    DELIM_OPEN == mdoc_isdelim(p)) {
+		if (head == NULL && ac != ARGS_QWORD &&
+		    mdoc_isdelim(p) == DELIM_OPEN) {
 			assert(NULL == body);
-			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
-				return(0);
+			dword(mdoc, line, la, p, DELIM_OPEN, 0);
 			continue;
 		}
 
-		if (NULL == head) {
-			assert(NULL == body);
-			if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
-				return(0);
-			head = mdoc->last;
+		if (head == NULL) {
+			assert(body == NULL);
+			head = mdoc_head_alloc(mdoc, line, ppos, tok);
 		}
 
 		/*
 		 * `Eo' gobbles any data into the head, but most other
 		 * macros just immediately close out and begin the body.
 		 */
 
-		if (NULL == body) {
+		if (body == NULL) {
 			assert(head);
 			/* No check whether it's a macro! */
-			if (MDOC_Eo == tok)
-				if ( ! dword(mdoc, line, la, p, DELIM_MAX, 0))
-					return(0);
-
-			if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
-				return(0);
-			if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
-				return(0);
-			body = mdoc->last;
-
-			if (MDOC_Eo == tok)
+			if (tok == MDOC_Eo)
+				dword(mdoc, line, la, p, DELIM_MAX, 0);
+			rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
+			body = mdoc_body_alloc(mdoc, line, ppos, tok);
+			if (tok == MDOC_Eo)
 				continue;
 		}
+		assert(head != NULL && body != NULL);
 
-		assert(NULL != head && NULL != body);
-
-		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
-
-		if (MDOC_MAX == ntok) {
-			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
-			    MDOC_JOIN & mdoc_macros[tok].flags))
-				return(0);
-			continue;
-		}
-
-		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
-			return(0);
-		break;
+		if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
+			break;
 	}
 
 	/* Clean-up to leave in a consistent state. */
 
-	if (NULL == head)
-		if ( ! mdoc_head_alloc(mdoc, line, ppos, tok))
-			return(0);
+	if (head == NULL)
+		mdoc_head_alloc(mdoc, line, ppos, tok);
 
-	if (NULL == body) {
-		if ( ! rew_sub(MDOC_HEAD, mdoc, tok, line, ppos))
-			return(0);
-		if ( ! mdoc_body_alloc(mdoc, line, ppos, tok))
-			return(0);
+	if (body == NULL) {
+		rew_sub(MDOC_HEAD, mdoc, tok, line, ppos);
+		mdoc_body_alloc(mdoc, line, ppos, tok);
 	}
-
-	/* Standard appending of delimiters. */
-
-	if ( ! nl)
-		return(1);
-	return(append_delims(mdoc, line, pos, buf));
+	if (nl)
+		append_delims(mdoc, line, pos, buf);
 }
 
-static int
+static void
 in_line_argn(MACRO_PROT_ARGS)
 {
 	int		 la, flushed, j, maxargs, nl;
 	enum margserr	 ac;
-	enum margverr	 av;
 	struct mdoc_arg	*arg;
 	char		*p;
 	enum mdoct	 ntok;
 
 	nl = MDOC_NEWLINE & mdoc->flags;
 
 	/*
 	 * A line macro that has a fixed number of arguments (maxargs).
 	 * Only open the scope once the first non-leading-punctuation is
 	 * found (unless MDOC_IGNDELIM is noted, like in `Pf'), then
 	 * keep it open until the maximum number of arguments are
 	 * exhausted.
 	 */
 
 	switch (tok) {
 	case MDOC_Ap:
 		/* FALLTHROUGH */
-	case MDOC_No:
-		/* FALLTHROUGH */
 	case MDOC_Ns:
 		/* FALLTHROUGH */
 	case MDOC_Ux:
 		maxargs = 0;
 		break;
 	case MDOC_Bx:
 		/* FALLTHROUGH */
 	case MDOC_Es:
 		/* FALLTHROUGH */
 	case MDOC_Xr:
 		maxargs = 2;
 		break;
 	default:
 		maxargs = 1;
 		break;
 	}
 
-	for (arg = NULL; ; ) {
-		la = *pos;
-		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
+	mdoc_argv(mdoc, line, tok, &arg, pos, buf);
 
-		if (ARGV_WORD == av) {
-			*pos = la;
-			break;
-		}
-
-		if (ARGV_EOLN == av)
-			break;
-		if (ARGV_ARG == av)
-			continue;
-
-		mdoc_argv_free(arg);
-		return(0);
-	}
-
 	for (flushed = j = 0; ; ) {
 		la = *pos;
 		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
-
-		if (ARGS_ERROR == ac)
-			return(0);
-		if (ARGS_PUNCT == ac)
+		if (ac == ARGS_PUNCT || ac == ARGS_EOLN)
 			break;
-		if (ARGS_EOLN == ac)
-			break;
 
-		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
-		    ARGS_QWORD != ac && 0 == j &&
-		    DELIM_OPEN == mdoc_isdelim(p)) {
-			if ( ! dword(mdoc, line, la, p, DELIM_OPEN, 0))
-				return(0);
+		if ( ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) &&
+		    ac != ARGS_QWORD && j == 0 &&
+		    mdoc_isdelim(p) == DELIM_OPEN) {
+			dword(mdoc, line, la, p, DELIM_OPEN, 0);
 			continue;
-		} else if (0 == j)
-		       if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
-			       return(0);
+		} else if (j == 0)
+		       mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
 
 		if (j == maxargs && ! flushed) {
-			if ( ! rew_elem(mdoc, tok))
-				return(0);
+			rew_elem(mdoc, tok);
 			flushed = 1;
 		}
 
-		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
+		ntok = ac == ARGS_QWORD ? MDOC_MAX : lookup(tok, p);
 
-		if (MDOC_MAX != ntok) {
-			if ( ! flushed && ! rew_elem(mdoc, tok))
-				return(0);
+		if (ntok != MDOC_MAX) {
+			if ( ! flushed)
+				rew_elem(mdoc, tok);
 			flushed = 1;
-			if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
-				return(0);
+			mdoc_macro(mdoc, ntok, line, la, pos, buf);
 			j++;
 			break;
 		}
 
-		if ( ! (MDOC_IGNDELIM & mdoc_macros[tok].flags) &&
-		    ARGS_QWORD != ac &&
-		    ! flushed &&
-		    DELIM_NONE != mdoc_isdelim(p)) {
-			if ( ! rew_elem(mdoc, tok))
-				return(0);
+		if ( ! (mdoc_macros[tok].flags & MDOC_IGNDELIM) &&
+		    ac != ARGS_QWORD && ! flushed &&
+		    mdoc_isdelim(p) != DELIM_NONE) {
+			rew_elem(mdoc, tok);
 			flushed = 1;
 		}
 
-		if ( ! dword(mdoc, line, la, p, DELIM_MAX,
-		    MDOC_JOIN & mdoc_macros[tok].flags))
-			return(0);
+		dword(mdoc, line, la, p, DELIM_MAX,
+		    MDOC_JOIN & mdoc_macros[tok].flags);
 		j++;
 	}
 
-	if (0 == j && ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
-	       return(0);
-
-	/* Close out in a consistent state. */
-
-	if ( ! flushed && ! rew_elem(mdoc, tok))
-		return(0);
-	if ( ! nl)
-		return(1);
-	return(append_delims(mdoc, line, pos, buf));
+	if (j == 0)
+		mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
+	if ( ! flushed)
+		rew_elem(mdoc, tok);
+	if (nl)
+		append_delims(mdoc, line, pos, buf);
 }
 
-static int
+static void
 in_line_eoln(MACRO_PROT_ARGS)
 {
-	int		 la;
-	enum margserr	 ac;
-	enum margverr	 av;
 	struct mdoc_arg	*arg;
-	char		*p;
-	enum mdoct	 ntok;
 
-	assert( ! (MDOC_PARSED & mdoc_macros[tok].flags));
-
 	if (tok == MDOC_Pp)
 		rew_sub(MDOC_BLOCK, mdoc, MDOC_Nm, line, ppos);
 
-	/* Parse macro arguments. */
+	mdoc_argv(mdoc, line, tok, &arg, pos, buf);
+	mdoc_elem_alloc(mdoc, line, ppos, tok, arg);
+	if (parse_rest(mdoc, tok, line, pos, buf))
+		return;
+	rew_elem(mdoc, tok);
+}
 
-	for (arg = NULL; ; ) {
-		la = *pos;
-		av = mdoc_argv(mdoc, line, tok, &arg, pos, buf);
+/*
+ * The simplest argument parser available: Parse the remaining
+ * words until the end of the phrase or line and return 0
+ * or until the next macro, call that macro, and return 1.
+ */
+static int
+parse_rest(struct mdoc *mdoc, enum mdoct tok, int line, int *pos, char *buf)
+{
+	int		 la;
 
-		if (ARGV_WORD == av) {
-			*pos = la;
-			break;
-		}
-		if (ARGV_EOLN == av)
-			break;
-		if (ARGV_ARG == av)
-			continue;
-
-		mdoc_argv_free(arg);
-		return(0);
-	}
-
-	/* Open element scope. */
-
-	if ( ! mdoc_elem_alloc(mdoc, line, ppos, tok, arg))
-		return(0);
-
-	/* Parse argument terms. */
-
 	for (;;) {
 		la = *pos;
-		ac = mdoc_args(mdoc, line, pos, buf, tok, &p);
-
-		if (ARGS_ERROR == ac)
+		if (mdoc_args(mdoc, line, pos, buf, tok, NULL) == ARGS_EOLN)
 			return(0);
-		if (ARGS_EOLN == ac)
-			break;
-
-		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup(tok, p);
-
-		if (MDOC_MAX == ntok) {
-			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
-			    MDOC_JOIN & mdoc_macros[tok].flags))
-				return(0);
-			continue;
-		}
-
-		if ( ! rew_elem(mdoc, tok))
-			return(0);
-		return(mdoc_macro(mdoc, ntok, line, la, pos, buf));
+		if (macro_or_word(mdoc, tok, line, la, pos, buf, 1))
+			return(1);
 	}
-
-	/* Close out (no delimiters). */
-
-	return(rew_elem(mdoc, tok));
 }
 
-static int
+static void
 ctx_synopsis(MACRO_PROT_ARGS)
 {
-	int		 nl;
 
-	nl = MDOC_NEWLINE & mdoc->flags;
-
-	/* If we're not in the SYNOPSIS, go straight to in-line. */
-	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(mdoc, tok, line, ppos, pos, buf));
-
-	/*
-	 * XXX: this will open a block scope; however, if later we end
-	 * up formatting the block scope, then child nodes will inherit
-	 * the formatting.  Be careful.
-	 */
-	if (MDOC_Nm == tok)
-		return(blk_full(mdoc, tok, line, ppos, pos, buf));
-	assert(MDOC_Vt == tok);
-	return(blk_part_imp(mdoc, tok, line, ppos, pos, buf));
+	if (~mdoc->flags & (MDOC_SYNOPSIS | MDOC_NEWLINE))
+		in_line(mdoc, tok, line, ppos, pos, buf);
+	else if (tok == MDOC_Nm)
+		blk_full(mdoc, tok, line, ppos, pos, buf);
+	else {
+		assert(tok == MDOC_Vt);
+		blk_part_imp(mdoc, tok, line, ppos, pos, buf);
+	}
 }
 
 /*
  * Phrases occur within `Bl -column' entries, separated by `Ta' or tabs.
  * They're unusual because they're basically free-form text until a
  * macro is encountered.
  */
-static int
-phrase(struct mdoc *mdoc, int line, int ppos, char *buf)
-{
-	int		 la, pos;
-	enum margserr	 ac;
-	enum mdoct	 ntok;
-	char		*p;
-
-	for (pos = ppos; ; ) {
-		la = pos;
-
-		ac = mdoc_zargs(mdoc, line, &pos, buf, &p);
-
-		if (ARGS_ERROR == ac)
-			return(0);
-		if (ARGS_EOLN == ac)
-			break;
-
-		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);
-
-		if (MDOC_MAX == ntok) {
-			if ( ! dword(mdoc, line, la, p, DELIM_MAX, 1))
-				return(0);
-			continue;
-		}
-
-		if ( ! mdoc_macro(mdoc, ntok, line, la, &pos, buf))
-			return(0);
-		return(append_delims(mdoc, line, &pos, buf));
-	}
-
-	return(1);
-}
-
-static int
+static void
 phrase_ta(MACRO_PROT_ARGS)
 {
 	struct mdoc_node *n;
-	int		  la;
-	enum mdoct	  ntok;
-	enum margserr	  ac;
-	char		 *p;
 
 	/* Make sure we are in a column list or ignore this macro. */
+
 	n = mdoc->last;
-	while (NULL != n && MDOC_Bl != n->tok)
+	while (n != NULL && n->tok != MDOC_Bl)
 		n = n->parent;
-	if (NULL == n || LIST_column != n->norm->Bl.type) {
+	if (n == NULL || n->norm->Bl.type != LIST_column) {
 		mandoc_msg(MANDOCERR_TA_STRAY, mdoc->parse,
 		    line, ppos, "Ta");
-		return(1);
+		return;
 	}
 
 	/* Advance to the next column. */
-	if ( ! rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos))
-		return(0);
-	if ( ! mdoc_body_alloc(mdoc, line, ppos, MDOC_It))
-		return(0);
 
-	for (;;) {
-		la = *pos;
-		ac = mdoc_zargs(mdoc, line, pos, buf, &p);
-
-		if (ARGS_ERROR == ac)
-			return(0);
-		if (ARGS_EOLN == ac)
-			break;
-
-		ntok = ARGS_QWORD == ac ? MDOC_MAX : lookup_raw(p);
-
-		if (MDOC_MAX == ntok) {
-			if ( ! dword(mdoc, line, la, p, DELIM_MAX,
-			    MDOC_JOIN & mdoc_macros[tok].flags))
-				return(0);
-			continue;
-		}
-
-		if ( ! mdoc_macro(mdoc, ntok, line, la, pos, buf))
-			return(0);
-		return(append_delims(mdoc, line, pos, buf));
-	}
-
-	return(1);
+	rew_sub(MDOC_BODY, mdoc, MDOC_It, line, ppos);
+	mdoc_body_alloc(mdoc, line, ppos, MDOC_It);
+	parse_rest(mdoc, MDOC_MAX, line, pos, buf);
 }
Index: vendor/mdocml/dist/mdoc_man.c
===================================================================
--- vendor/mdocml/dist/mdoc_man.c	(revision 275396)
+++ vendor/mdocml/dist/mdoc_man.c	(revision 275397)
@@ -1,1753 +1,1790 @@
-/*	$Id: mdoc_man.c,v 1.68 2014/08/06 15:09:05 schwarze Exp $ */
+/*	$Id: mdoc_man.c,v 1.76 2014/11/27 22:27:56 schwarze Exp $ */
 /*
  * Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "out.h"
 #include "man.h"
 #include "mdoc.h"
 #include "main.h"
 
 #define	DECL_ARGS const struct mdoc_meta *meta, \
 		  const struct mdoc_node *n
 
 struct	manact {
 	int		(*cond)(DECL_ARGS); /* DON'T run actions */
 	int		(*pre)(DECL_ARGS); /* pre-node action */
 	void		(*post)(DECL_ARGS); /* post-node action */
 	const char	 *prefix; /* pre-node string constant */
 	const char	 *suffix; /* post-node string constant */
 };
 
 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_aq(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_en(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_aq(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_en(DECL_ARGS);
 static	int	  pre_enc(DECL_ARGS);
 static	int	  pre_em(DECL_ARGS);
-static	int	  pre_es(DECL_ARGS);
+static	int	  pre_skip(DECL_ARGS);
+static	int	  pre_eo(DECL_ARGS);
 static	int	  pre_ex(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_ll(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_rv(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(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_offs(const char *, int);
 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] = {
 	{ NULL, pre_ap, NULL, NULL, NULL }, /* Ap */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Dd */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Dt */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Os */
 	{ NULL, pre_sect, post_sect, ".SH", NULL }, /* Sh */
 	{ NULL, pre_sect, post_sect, ".SS", NULL }, /* Ss */
 	{ NULL, pre_pp, NULL, NULL, NULL }, /* Pp */
 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* D1 */
 	{ cond_body, pre_dl, post_dl, NULL, NULL }, /* Dl */
 	{ cond_body, pre_bd, post_bd, NULL, NULL }, /* Bd */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Ed */
 	{ cond_body, pre_bl, post_bl, NULL, NULL }, /* Bl */
 	{ NULL, NULL, NULL, NULL, NULL }, /* El */
 	{ 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_ex, NULL, NULL, NULL }, /* Ex */
 	{ 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, pre_ft, post_font, NULL, NULL }, /* Ot */
 	{ NULL, pre_em, post_font, NULL, NULL }, /* Pa */
 	{ NULL, pre_rv, NULL, NULL, NULL }, /* Rv */
 	{ NULL, NULL, NULL, NULL, NULL }, /* St */
 	{ 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, 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 */
+	{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Ao */
+	{ cond_body, pre_aq, post_aq, NULL, NULL }, /* Aq */
 	{ NULL, NULL, NULL, NULL, NULL }, /* At */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Bc */
 	{ NULL, pre_bf, post_bf, NULL, NULL }, /* Bf */
 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bo */
 	{ cond_body, pre_enc, post_enc, "[", "]" }, /* Bq */
 	{ NULL, pre_ux, NULL, "BSD/OS", NULL }, /* Bsx */
 	{ NULL, pre_bx, NULL, NULL, NULL }, /* Bx */
-	{ NULL, NULL, NULL, NULL, NULL }, /* Db */
+	{ NULL, pre_skip, NULL, NULL, NULL }, /* Db */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Dc */
 	{ 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 */
+	{ cond_body, pre_eo, post_eo, NULL, NULL }, /* Eo */
 	{ NULL, pre_ux, NULL, "FreeBSD", NULL }, /* Fx */
 	{ 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 */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Pc */
 	{ NULL, NULL, post_pf, NULL, NULL }, /* Pf */
 	{ 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, "\\(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_rs, NULL, NULL, NULL }, /* Rs */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Sc */
 	{ 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, 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, 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, pre_em, post_font, NULL, NULL }, /* Fr */
 	{ NULL, pre_ux, NULL, "currently under development.", NULL }, /* Ud */
 	{ NULL, NULL, post_lb, NULL, NULL }, /* Lb */
 	{ NULL, pre_pp, NULL, NULL, NULL }, /* Lp */
 	{ 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, post_percent, NULL, NULL }, /* %C */
-	{ NULL, pre_es, NULL, NULL, NULL }, /* Es */
+	{ NULL, pre_skip, NULL, NULL, NULL }, /* Es */
 	{ cond_body, pre_en, post_en, NULL, NULL }, /* En */
 	{ NULL, pre_ux, NULL, "DragonFly", NULL }, /* Dx */
 	{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
 	{ NULL, pre_br, NULL, NULL, NULL }, /* br */
 	{ NULL, pre_sp, post_sp, NULL, NULL }, /* sp */
 	{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
 	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
 	{ NULL, pre_ll, post_sp, NULL, NULL }, /* ll */
 	{ 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
 font_push(char newfont)
 {
 
 	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.
 		 */
 		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 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 (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.
 	 */
 	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("\\ ");
 			break;
 		case ASCII_HYPH:
 			putchar('-');
 			break;
 		case ASCII_BREAK:
 			printf("\\:");
 			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)
+print_offs(const char *v, int keywords)
 {
 	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"))
+	if (NULL == v || '\0' == *v || (keywords && !strcmp(v, "left")))
 		sz = 0;
-	else if (0 == strcmp(v, "indent"))
+	else if (keywords && !strcmp(v, "indent"))
 		sz = 6;
-	else if (0 == strcmp(v, "indent-two"))
+	else if (keywords && !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];
 
 	(void)snprintf(buf, sizeof(buf), "%zun", sz);
 	print_word(buf);
 	outflags |= MMAN_nl;
 }
 
 /*
  * Set up the indentation for a list item; used from pre_it().
  */
 static 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) {
 		(void)snprintf(buf, sizeof(buf), "%zun", sz + 2);
 		print_word(buf);
 	} else
 		print_word(v);
 	TPremain = remain;
 }
 
 static void
 print_count(int *count)
 {
 	char		  buf[24];
 
 	(void)snprintf(buf, sizeof(buf), "%d.", ++*count);
 	print_word(buf);
 }
 
 void
 man_man(void *arg, const struct man *man)
 {
 
 	/*
 	 * Dump the keep buffer.
 	 * We're guaranteed by now that this exists (is non-NULL).
 	 * Flush stdout afterward, just in case.
 	 */
 	fputs(mparse_getkeep(man_mparse(man)), stdout);
 	fflush(stdout);
 }
 
 void
 man_mdoc(void *arg, const struct mdoc *mdoc)
 {
 	const struct mdoc_meta *meta;
 	const struct mdoc_node *n;
 
 	meta = mdoc_meta(mdoc);
 	n = mdoc_node(mdoc);
 
 	printf(".TH \"%s\" \"%s\" \"%s\" \"%s\" \"%s\"\n",
 	    meta->title,
 	    (meta->msec == NULL ? "" : meta->msec),
 	    meta->date, meta->os, meta->vol);
 
 	/* Disable hyphenation and if nroff, disable justification. */
 	printf(".nh\n.if n .ad l");
 
 	outflags = MMAN_nl | MMAN_Sm;
 	if (0 == fontqueue.size) {
 		fontqueue.size = 8;
 		fontqueue.head = fontqueue.tail = mandoc_malloc(8);
 		*fontqueue.tail = 'R';
 	}
 	print_node(meta, n);
 	putchar('\n');
 }
 
 static void
 print_node(DECL_ARGS)
 {
 	const struct mdoc_node	*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.
 	 */
 	if (MMAN_spc & outflags && MDOC_LINE & n->flags)
 		outflags |= MMAN_nl;
 
 	act = NULL;
 	cond = 0;
 	do_sub = 1;
 
 	if (MDOC_TEXT == n->type) {
 		/*
 		 * Make sure that we don't happen to start with a
 		 * control character at the start of a line.
 		 */
 		if (MMAN_nl & outflags &&
 		    ('.' == *n->string || '\'' == *n->string)) {
 			print_word("");
 			printf("\\&");
 			outflags &= ~MMAN_spc;
 		}
+		if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMC))
+			outflags |= MMAN_spc_force;
 		print_word(n->string);
+		if (outflags & MMAN_Sm && ! (n->flags & MDOC_DELIMO))
+			outflags |= MMAN_spc;
 	} else {
 		/*
 		 * Conditionally run the pre-node action handler for a
 		 * node.
 		 */
 		act = manacts + n->tok;
-		cond = NULL == act->cond || (*act->cond)(meta, n);
-		if (cond && act->pre && ENDBODY_NOT == n->end)
+		cond = act->cond == NULL || (*act->cond)(meta, n);
+		if (cond && act->pre && (n->end == ENDBODY_NOT || n->nchild))
 			do_sub = (*act->pre)(meta, n);
 	}
 
 	/*
 	 * Conditionally run all child nodes.
 	 * Note that this iterates over children instead of using
 	 * recursion.  This prevents unnecessary depth in the stack.
 	 */
 	if (do_sub)
 		for (sub = n->child; sub; sub = sub->next)
 			print_node(meta, sub);
 
 	/*
 	 * Lastly, conditionally run the post-node handler.
 	 */
 	if (MDOC_ENDED & n->flags)
 		return;
 
 	if (cond && act->post)
 		(*act->post)(meta, n);
 
 	if (ENDBODY_NOT != n->end)
 		n->pending->flags |= MDOC_ENDED;
 
 	if (ENDBODY_NOSPACE == n->end)
 		outflags &= ~(MMAN_spc | MMAN_nl);
 }
 
 static int
 cond_head(DECL_ARGS)
 {
 
 	return(MDOC_HEAD == n->type);
 }
 
 static int
 cond_body(DECL_ARGS)
 {
 
 	return(MDOC_BODY == n->type);
 }
 
 static int
 pre_enc(DECL_ARGS)
 {
 	const char	*prefix;
 
 	prefix = manacts[n->tok].prefix;
 	if (NULL == prefix)
 		return(1);
 	print_word(prefix);
 	outflags &= ~MMAN_spc;
 	return(1);
 }
 
 static void
 post_enc(DECL_ARGS)
 {
 	const char *suffix;
 
 	suffix = manacts[n->tok].suffix;
 	if (NULL == suffix)
 		return;
 	outflags &= ~(MMAN_spc | MMAN_nl);
 	print_word(suffix);
 }
 
 static int
 pre_ex(DECL_ARGS)
 {
 	int	 nchild;
 
 	outflags |= MMAN_br | MMAN_nl;
 
 	print_word("The");
 
 	nchild = n->nchild;
 	for (n = n->child; n; n = n->next) {
 		font_push('B');
 		print_word(n->string);
 		font_pop();
 
 		if (n->next == NULL)
 			continue;
 
 		if (nchild > 2) {
 			outflags &= ~MMAN_spc;
 			print_word(",");
 		}
 		if (n->next->next == NULL)
 			print_word("and");
 	}
 
 	if (nchild > 1)
 		print_word("utilities exit\\~0");
 	else
 		print_word("utility exits\\~0");
 
 	print_word("on success, and\\~>0 if an error occurs.");
 	outflags |= MMAN_nl;
 	return(0);
 }
 
 static void
 post_font(DECL_ARGS)
 {
 
 	font_pop();
 }
 
 static void
 post_percent(DECL_ARGS)
 {
 
 	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.
  */
 static int
 pre_sect(DECL_ARGS)
 {
 
 	if (MDOC_HEAD == n->type) {
 		outflags |= MMAN_sp;
 		print_block(manacts[n->tok].prefix, 0);
 		print_word("");
 		putchar('\"');
 		outflags &= ~MMAN_spc;
 	}
 	return(1);
 }
 
 /*
  * Print subsequent a section header.
  */
 static void
 post_sect(DECL_ARGS)
 {
 
 	if (MDOC_HEAD != n->type)
 		return;
 	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)
 {
 
 	outflags &= ~MMAN_spc;
 	print_word("'");
 	outflags &= ~MMAN_spc;
 	return(0);
 }
 
 static int
+pre_aq(DECL_ARGS)
+{
+
+	print_word(n->parent->prev != NULL &&
+	    n->parent->prev->tok == MDOC_An ?  "<" : "\\(la");
+	outflags &= ~MMAN_spc;
+	return(1);
+}
+
+static void
+post_aq(DECL_ARGS)
+{
+
+	outflags &= ~(MMAN_spc | MMAN_nl);
+	print_word(n->parent->prev != NULL &&
+	    n->parent->prev->tok == MDOC_An ?  ">" : "\\(ra");
+}
+
+static int
 pre_bd(DECL_ARGS)
 {
 
 	outflags &= ~(MMAN_PP | MMAN_sp | MMAN_br);
 
 	if (DISP_unfilled == n->norm->Bd.type ||
 	    DISP_literal  == n->norm->Bd.type)
 		print_line(".nf", 0);
 	if (0 == n->norm->Bd.comp && NULL != n->parent->prev)
 		outflags |= MMAN_sp;
-	print_offs(n->norm->Bd.offs);
+	print_offs(n->norm->Bd.offs, 1);
 	return(1);
 }
 
 static void
 post_bd(DECL_ARGS)
 {
 
 	/* Close out this display. */
 	print_line(".RE", MMAN_nl);
 	if (DISP_unfilled == n->norm->Bd.type ||
 	    DISP_literal  == n->norm->Bd.type)
 		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);
 	}
 	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);
+		print_offs(n->norm->Bl.offs, 0);
 		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)
 {
 
 	outflags |= MMAN_br;
 	return(0);
 }
 
 static int
 pre_bx(DECL_ARGS)
 {
 
 	n = n->child;
 	if (n) {
 		print_word(n->string);
 		outflags &= ~MMAN_spc;
 		n = n->next;
 	}
 	print_word("BSD");
 	if (NULL == n)
 		return(0);
 	outflags &= ~MMAN_spc;
 	print_word("-");
 	outflags &= ~MMAN_spc;
 	print_word(n->string);
 	return(0);
 }
 
 static int
 pre_dl(DECL_ARGS)
 {
 
-	print_offs("6n");
+	print_offs("6n", 0);
 	return(1);
 }
 
 static void
 post_dl(DECL_ARGS)
 {
 
 	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 int
 pre_en(DECL_ARGS)
 {
 
 	if (NULL == n->norm->Es ||
 	    NULL == n->norm->Es->child)
 		return(1);
 
 	print_word(n->norm->Es->child->string);
 	outflags &= ~MMAN_spc;
 	return(1);
 }
 
 static void
 post_en(DECL_ARGS)
 {
 
 	if (NULL == n->norm->Es ||
 	    NULL == n->norm->Es->child ||
 	    NULL == n->norm->Es->child->next)
 		return;
 
 	outflags &= ~MMAN_spc;
 	print_word(n->norm->Es->child->next->string);
 	return;
 }
 
-static void
-post_eo(DECL_ARGS)
+static int
+pre_eo(DECL_ARGS)
 {
 
-	if (MDOC_HEAD == n->type || MDOC_BODY == n->type)
-		outflags &= ~MMAN_spc;
+	outflags &= ~(MMAN_spc | MMAN_nl);
+	return(1);
 }
 
-static int
-pre_es(DECL_ARGS)
+static void
+post_eo(DECL_ARGS)
 {
 
-	return(0);
+	if (n->end != ENDBODY_SPACE)
+		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;
+	if (n->nchild)
+		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)
+	if ( ! (n->nchild ||
+	    n->next == NULL ||
+	    n->next->type == MDOC_TEXT ||
+	    n->next->flags & MDOC_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;
 
 	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:
 			/* 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");
+				print_word("\\(bu");
 			else
 				print_word("-");
 			font_pop();
 			outflags |= MMAN_nl;
 			return(0);
 		case LIST_enum:
 			print_width(bln->norm->Bl.width, NULL, 0);
 			TPremain = 0;
 			outflags |= MMAN_nl;
 			print_count(&bln->norm->Bl.count);
 			outflags |= MMAN_nl;
 			return(0);
 		case LIST_hang:
 			print_width(bln->norm->Bl.width, n->child, 6);
 			TPremain = 0;
 			outflags |= MMAN_nl;
 			return(1);
 		case LIST_tag:
 			print_width(bln->norm->Bl.width, n->child, 0);
 			putchar('\n');
 			outflags &= ~MMAN_spc;
 			return(1);
 		default:
 			return(1);
 		}
 	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);
 	(void)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;
 		}
 		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_ll(DECL_ARGS)
 {
 
 	print_line(".ll", 0);
 	return(1);
 }
 
 static int
 pre_li(DECL_ARGS)
 {
 
 	font_push('R');
 	return(1);
 }
 
 static int
 pre_nm(DECL_ARGS)
 {
 	char	*name;
 
 	if (MDOC_BLOCK == n->type) {
 		outflags |= MMAN_Bk;
 		pre_syn(n);
 	}
 	if (MDOC_ELEM != n->type && MDOC_HEAD != n->type)
 		return(1);
 	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(meta->name);
 	return(1);
 }
 
 static void
 post_nm(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MDOC_BLOCK:
 		outflags &= ~MMAN_Bk;
 		break;
 	case MDOC_HEAD:
 		/* FALLTHROUGH */
 	case MDOC_ELEM:
 		if (n->child != NULL || meta->name != NULL)
 			font_pop();
 		break;
 	default:
 		break;
 	}
 }
 
 static int
 pre_no(DECL_ARGS)
 {
 
 	outflags |= MMAN_spc_force;
 	return(1);
 }
 
 static int
 pre_ns(DECL_ARGS)
 {
 
 	outflags &= ~MMAN_spc;
 	return(0);
 }
 
 static void
 post_pf(DECL_ARGS)
 {
 
 	outflags &= ~MMAN_spc;
 }
 
 static int
 pre_pp(DECL_ARGS)
 {
 
 	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_rv(DECL_ARGS)
 {
 	int	 nchild;
 
 	outflags |= MMAN_br | MMAN_nl;
 
 	nchild = n->nchild;
 	if (nchild > 0) {
 		print_word("The");
 
 		for (n = n->child; n; n = n->next) {
 			font_push('B');
 			print_word(n->string);
 			font_pop();
 
 			outflags &= ~MMAN_spc;
 			print_word("()");
 
 			if (n->next == NULL)
 				continue;
 
 			if (nchild > 2) {
 				outflags &= ~MMAN_spc;
 				print_word(",");
 			}
 			if (n->next->next == NULL)
 				print_word("and");
 		}
 
 		if (nchild > 1)
 			print_word("functions return");
 		else
 			print_word("function returns");
 
 		print_word("the value\\~0 if successful;");
 	} else
 		print_word("Upon successful completion, "
 		    "the value\\~0 is returned;");
 
 	print_word("otherwise the value\\~\\-1 is returned"
 	    " and the global variable");
 
 	font_push('I');
 	print_word("errno");
 	font_pop();
 
 	print_word("is set to indicate the error.");
 	outflags |= MMAN_nl;
+	return(0);
+}
+
+static int
+pre_skip(DECL_ARGS)
+{
+
 	return(0);
 }
 
 static int
 pre_sm(DECL_ARGS)
 {
 
 	if (NULL == n->child)
 		outflags ^= MMAN_Sm;
 	else if (0 == strcmp("on", n->child->string))
 		outflags |= MMAN_Sm;
 	else
 		outflags &= ~MMAN_Sm;
 
 	if (MMAN_Sm & outflags)
 		outflags |= MMAN_spc;
 
 	return(0);
 }
 
 static int
 pre_sp(DECL_ARGS)
 {
 
 	if (MMAN_PP & outflags) {
 		outflags &= ~MMAN_PP;
 		print_line(".PP", 0);
 	} else
 		print_line(".sp", 0);
 	return(1);
 }
 
 static void
 post_sp(DECL_ARGS)
 {
 
 	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)
 {
 
 	n = n->child;
 	if (NULL == n)
 		return(0);
 	print_node(meta, n);
 	n = n->next;
 	if (NULL == n)
 		return(0);
 	outflags &= ~MMAN_spc;
 	print_word("(");
 	print_node(meta, n);
 	print_word(")");
 	return(0);
 }
 
 static int
 pre_ux(DECL_ARGS)
 {
 
 	print_word(manacts[n->tok].prefix);
 	if (NULL == n->child)
 		return(0);
 	outflags &= ~MMAN_spc;
 	print_word("\\ ");
 	outflags &= ~MMAN_spc;
 	return(1);
 }
Index: vendor/mdocml/dist/mdoc_term.c
===================================================================
--- vendor/mdocml/dist/mdoc_term.c	(revision 275396)
+++ vendor/mdocml/dist/mdoc_term.c	(revision 275397)
@@ -1,2239 +1,2219 @@
-/*	$Id: mdoc_term.c,v 1.275 2014/08/06 15:09:05 schwarze Exp $ */
+/*	$Id: mdoc_term.c,v 1.297 2014/11/28 16:54:23 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2010, 2012, 2013, 2014 Ingo Schwarze 
  * Copyright (c) 2013 Franco Fichtner 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "out.h"
 #include "term.h"
 #include "mdoc.h"
 #include "main.h"
 
 struct	termpair {
 	struct termpair	 *ppair;
 	int		  count;
 };
 
 #define	DECL_ARGS struct termp *p, \
 		  struct termpair *pair, \
 		  const struct mdoc_meta *meta, \
 		  struct mdoc_node *n
 
 struct	termact {
 	int	(*pre)(DECL_ARGS);
 	void	(*post)(DECL_ARGS);
 };
 
 static	size_t	  a2width(const struct termp *, const char *);
 static	size_t	  a2height(const struct termp *, const char *);
-static	size_t	  a2offs(const struct termp *, const char *);
 
 static	void	  print_bvspace(struct termp *,
 			const struct mdoc_node *,
 			const struct mdoc_node *);
 static	void	  print_mdoc_node(DECL_ARGS);
 static	void	  print_mdoc_nodelist(DECL_ARGS);
 static	void	  print_mdoc_head(struct termp *, const void *);
 static	void	  print_mdoc_foot(struct termp *, const void *);
 static	void	  synopsis_pre(struct termp *,
 			const struct mdoc_node *);
 
 static	void	  termp____post(DECL_ARGS);
 static	void	  termp__t_post(DECL_ARGS);
-static	void	  termp_an_post(DECL_ARGS);
 static	void	  termp_bd_post(DECL_ARGS);
 static	void	  termp_bk_post(DECL_ARGS);
 static	void	  termp_bl_post(DECL_ARGS);
 static	void	  termp_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);
 static	void	  termp_lb_post(DECL_ARGS);
 static	void	  termp_nm_post(DECL_ARGS);
 static	void	  termp_pf_post(DECL_ARGS);
 static	void	  termp_quote_post(DECL_ARGS);
 static	void	  termp_sh_post(DECL_ARGS);
 static	void	  termp_ss_post(DECL_ARGS);
 
 static	int	  termp__a_pre(DECL_ARGS);
 static	int	  termp__t_pre(DECL_ARGS);
 static	int	  termp_an_pre(DECL_ARGS);
 static	int	  termp_ap_pre(DECL_ARGS);
 static	int	  termp_bd_pre(DECL_ARGS);
 static	int	  termp_bf_pre(DECL_ARGS);
 static	int	  termp_bk_pre(DECL_ARGS);
 static	int	  termp_bl_pre(DECL_ARGS);
 static	int	  termp_bold_pre(DECL_ARGS);
 static	int	  termp_bt_pre(DECL_ARGS);
 static	int	  termp_bx_pre(DECL_ARGS);
 static	int	  termp_cd_pre(DECL_ARGS);
 static	int	  termp_d1_pre(DECL_ARGS);
-static	int	  termp_es_pre(DECL_ARGS);
 static	int	  termp_ex_pre(DECL_ARGS);
 static	int	  termp_fa_pre(DECL_ARGS);
 static	int	  termp_fd_pre(DECL_ARGS);
 static	int	  termp_fl_pre(DECL_ARGS);
 static	int	  termp_fn_pre(DECL_ARGS);
 static	int	  termp_fo_pre(DECL_ARGS);
 static	int	  termp_ft_pre(DECL_ARGS);
 static	int	  termp_in_pre(DECL_ARGS);
 static	int	  termp_it_pre(DECL_ARGS);
 static	int	  termp_li_pre(DECL_ARGS);
 static	int	  termp_ll_pre(DECL_ARGS);
 static	int	  termp_lk_pre(DECL_ARGS);
 static	int	  termp_nd_pre(DECL_ARGS);
 static	int	  termp_nm_pre(DECL_ARGS);
 static	int	  termp_ns_pre(DECL_ARGS);
 static	int	  termp_quote_pre(DECL_ARGS);
 static	int	  termp_rs_pre(DECL_ARGS);
 static	int	  termp_rv_pre(DECL_ARGS);
 static	int	  termp_sh_pre(DECL_ARGS);
+static	int	  termp_skip_pre(DECL_ARGS);
 static	int	  termp_sm_pre(DECL_ARGS);
 static	int	  termp_sp_pre(DECL_ARGS);
 static	int	  termp_ss_pre(DECL_ARGS);
 static	int	  termp_under_pre(DECL_ARGS);
 static	int	  termp_ud_pre(DECL_ARGS);
 static	int	  termp_vt_pre(DECL_ARGS);
 static	int	  termp_xr_pre(DECL_ARGS);
 static	int	  termp_xx_pre(DECL_ARGS);
 
 static	const struct termact termacts[MDOC_MAX] = {
 	{ termp_ap_pre, NULL }, /* Ap */
 	{ NULL, NULL }, /* Dd */
 	{ NULL, NULL }, /* Dt */
 	{ NULL, NULL }, /* Os */
 	{ termp_sh_pre, termp_sh_post }, /* Sh */
 	{ termp_ss_pre, termp_ss_post }, /* Ss */
 	{ termp_sp_pre, NULL }, /* Pp */
 	{ 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 */
 	{ NULL, NULL }, /* El */
 	{ termp_it_pre, termp_it_post }, /* It */
 	{ termp_under_pre, NULL }, /* Ad */
-	{ termp_an_pre, termp_an_post }, /* An */
+	{ termp_an_pre, NULL }, /* An */
 	{ termp_under_pre, NULL }, /* Ar */
 	{ termp_cd_pre, NULL }, /* Cd */
 	{ termp_bold_pre, NULL }, /* Cm */
 	{ NULL, NULL }, /* Dv */
 	{ NULL, NULL }, /* Er */
 	{ NULL, NULL }, /* Ev */
 	{ termp_ex_pre, NULL }, /* Ex */
 	{ termp_fa_pre, NULL }, /* Fa */
 	{ termp_fd_pre, termp_fd_post }, /* Fd */
 	{ termp_fl_pre, NULL }, /* Fl */
 	{ termp_fn_pre, NULL }, /* Fn */
 	{ termp_ft_pre, NULL }, /* Ft */
 	{ termp_bold_pre, NULL }, /* Ic */
 	{ termp_in_pre, termp_in_post }, /* In */
 	{ termp_li_pre, NULL }, /* Li */
 	{ termp_nd_pre, NULL }, /* Nd */
 	{ termp_nm_pre, termp_nm_post }, /* Nm */
 	{ termp_quote_pre, termp_quote_post }, /* Op */
 	{ termp_ft_pre, NULL }, /* Ot */
 	{ termp_under_pre, NULL }, /* Pa */
 	{ termp_rv_pre, NULL }, /* Rv */
 	{ NULL, NULL }, /* St */
 	{ termp_under_pre, NULL }, /* Va */
 	{ termp_vt_pre, NULL }, /* Vt */
 	{ termp_xr_pre, NULL }, /* Xr */
 	{ termp__a_pre, termp____post }, /* %A */
 	{ termp_under_pre, termp____post }, /* %B */
 	{ NULL, termp____post }, /* %D */
 	{ termp_under_pre, termp____post }, /* %I */
 	{ termp_under_pre, termp____post }, /* %J */
 	{ NULL, termp____post }, /* %N */
 	{ NULL, termp____post }, /* %O */
 	{ NULL, termp____post }, /* %P */
 	{ NULL, termp____post }, /* %R */
 	{ termp__t_pre, termp__t_post }, /* %T */
 	{ NULL, termp____post }, /* %V */
 	{ NULL, NULL }, /* Ac */
 	{ termp_quote_pre, termp_quote_post }, /* Ao */
 	{ termp_quote_pre, termp_quote_post }, /* Aq */
 	{ NULL, NULL }, /* At */
 	{ NULL, NULL }, /* Bc */
 	{ termp_bf_pre, NULL }, /* Bf */
 	{ termp_quote_pre, termp_quote_post }, /* Bo */
 	{ termp_quote_pre, termp_quote_post }, /* Bq */
 	{ termp_xx_pre, NULL }, /* Bsx */
 	{ termp_bx_pre, NULL }, /* Bx */
-	{ NULL, NULL }, /* Db */
+	{ termp_skip_pre, NULL }, /* Db */
 	{ NULL, NULL }, /* Dc */
 	{ termp_quote_pre, termp_quote_post }, /* Do */
 	{ termp_quote_pre, termp_quote_post }, /* Dq */
 	{ NULL, NULL }, /* Ec */ /* FIXME: no space */
 	{ NULL, NULL }, /* Ef */
 	{ termp_under_pre, NULL }, /* Em */
 	{ termp_quote_pre, termp_quote_post }, /* Eo */
 	{ termp_xx_pre, NULL }, /* Fx */
 	{ termp_bold_pre, NULL }, /* Ms */
-	{ NULL, NULL }, /* No */
+	{ termp_li_pre, NULL }, /* No */
 	{ termp_ns_pre, NULL }, /* Ns */
 	{ termp_xx_pre, NULL }, /* Nx */
 	{ termp_xx_pre, NULL }, /* Ox */
 	{ NULL, NULL }, /* Pc */
 	{ NULL, termp_pf_post }, /* Pf */
 	{ termp_quote_pre, termp_quote_post }, /* Po */
 	{ termp_quote_pre, termp_quote_post }, /* Pq */
 	{ NULL, NULL }, /* Qc */
 	{ termp_quote_pre, termp_quote_post }, /* Ql */
 	{ termp_quote_pre, termp_quote_post }, /* Qo */
 	{ termp_quote_pre, termp_quote_post }, /* Qq */
 	{ NULL, NULL }, /* Re */
 	{ termp_rs_pre, NULL }, /* Rs */
 	{ NULL, NULL }, /* Sc */
 	{ termp_quote_pre, termp_quote_post }, /* So */
 	{ termp_quote_pre, termp_quote_post }, /* Sq */
 	{ termp_sm_pre, NULL }, /* Sm */
 	{ termp_under_pre, NULL }, /* Sx */
 	{ termp_bold_pre, NULL }, /* Sy */
 	{ NULL, NULL }, /* Tn */
 	{ termp_xx_pre, NULL }, /* Ux */
 	{ NULL, NULL }, /* Xc */
 	{ NULL, NULL }, /* Xo */
 	{ termp_fo_pre, termp_fo_post }, /* Fo */
 	{ NULL, NULL }, /* Fc */
 	{ termp_quote_pre, termp_quote_post }, /* Oo */
 	{ NULL, NULL }, /* Oc */
 	{ termp_bk_pre, termp_bk_post }, /* Bk */
 	{ NULL, NULL }, /* Ek */
 	{ termp_bt_pre, NULL }, /* Bt */
 	{ NULL, NULL }, /* Hf */
 	{ termp_under_pre, NULL }, /* Fr */
 	{ termp_ud_pre, NULL }, /* Ud */
 	{ NULL, termp_lb_post }, /* Lb */
 	{ termp_sp_pre, NULL }, /* Lp */
 	{ termp_lk_pre, NULL }, /* Lk */
 	{ termp_under_pre, NULL }, /* Mt */
 	{ termp_quote_pre, termp_quote_post }, /* Brq */
 	{ termp_quote_pre, termp_quote_post }, /* Bro */
 	{ NULL, NULL }, /* Brc */
 	{ NULL, termp____post }, /* %C */
-	{ termp_es_pre, NULL }, /* Es */
+	{ termp_skip_pre, NULL }, /* Es */
 	{ termp_quote_pre, termp_quote_post }, /* En */
 	{ termp_xx_pre, NULL }, /* Dx */
 	{ NULL, termp____post }, /* %Q */
 	{ termp_sp_pre, NULL }, /* br */
 	{ termp_sp_pre, NULL }, /* sp */
 	{ NULL, termp____post }, /* %U */
 	{ NULL, NULL }, /* Ta */
 	{ termp_ll_pre, NULL }, /* ll */
 };
 
 
 void
 terminal_mdoc(void *arg, const struct mdoc *mdoc)
 {
-	const struct mdoc_node	*n;
 	const struct mdoc_meta	*meta;
+	struct mdoc_node	*n;
 	struct termp		*p;
 
 	p = (struct termp *)arg;
 
-	if (0 == p->defindent)
-		p->defindent = 5;
-
 	p->overstep = 0;
-	p->maxrmargin = p->defrmargin;
+	p->rmargin = p->maxrmargin = p->defrmargin;
 	p->tabwidth = term_len(p, 5);
 
-	if (NULL == p->symtab)
-		p->symtab = mchars_alloc();
-
-	n = mdoc_node(mdoc);
+	n = mdoc_node(mdoc)->child;
 	meta = mdoc_meta(mdoc);
 
-	term_begin(p, print_mdoc_head, print_mdoc_foot, meta);
-
-	if (n->child) {
-		if (MDOC_Sh != n->child->tok)
-			term_vspace(p);
-		print_mdoc_nodelist(p, NULL, meta, n->child);
+	if (p->synopsisonly) {
+		while (n != NULL) {
+			if (n->tok == MDOC_Sh && n->sec == SEC_SYNOPSIS) {
+				if (n->child->next->child != NULL)
+					print_mdoc_nodelist(p, NULL,
+					    meta, n->child->next->child);
+				term_newln(p);
+				break;
+			}
+			n = n->next;
+		}
+	} else {
+		if (p->defindent == 0)
+			p->defindent = 5;
+		term_begin(p, print_mdoc_head, print_mdoc_foot, meta);
+		if (n != NULL) {
+			if (n->tok != MDOC_Sh)
+				term_vspace(p);
+			print_mdoc_nodelist(p, NULL, meta, n);
+		}
+		term_end(p);
 	}
-
-	term_end(p);
 }
 
 static void
 print_mdoc_nodelist(DECL_ARGS)
 {
 
 	print_mdoc_node(p, pair, meta, n);
 	if (n->next)
 		print_mdoc_nodelist(p, pair, meta, n->next);
 }
 
 static void
 print_mdoc_node(DECL_ARGS)
 {
 	int		 chld;
 	struct termpair	 npair;
 	size_t		 offset, rmargin;
 
 	chld = 1;
 	offset = p->offset;
 	rmargin = p->rmargin;
 	n->prev_font = term_fontq(p);
 
 	memset(&npair, 0, sizeof(struct termpair));
 	npair.ppair = pair;
 
 	/*
 	 * Keeps only work until the end of a line.  If a keep was
 	 * invoked in a prior line, revert it to PREKEEP.
 	 */
 
 	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;
 		}
 	}
 
 	/*
 	 * After the keep flags have been set up, we may now
 	 * produce output.  Note that some pre-handlers do so.
 	 */
 
 	switch (n->type) {
 	case MDOC_TEXT:
 		if (' ' == *n->string && MDOC_LINE & n->flags)
 			term_newln(p);
 		if (MDOC_DELIMC & n->flags)
 			p->flags |= TERMP_NOSPACE;
 		term_word(p, n->string);
 		if (MDOC_DELIMO & n->flags)
 			p->flags |= TERMP_NOSPACE;
 		break;
 	case MDOC_EQN:
+		if ( ! (n->flags & MDOC_LINE))
+			p->flags |= TERMP_NOSPACE;
 		term_eqn(p, n->eqn);
+		if (n->next != NULL && ! (n->next->flags & MDOC_LINE))
+			p->flags |= TERMP_NOSPACE;
 		break;
 	case MDOC_TBL:
 		term_tbl(p, n->span);
 		break;
 	default:
-		if (termacts[n->tok].pre && ENDBODY_NOT == n->end)
+		if (termacts[n->tok].pre &&
+		    (n->end == ENDBODY_NOT || n->nchild))
 			chld = (*termacts[n->tok].pre)
 				(p, &npair, meta, n);
 		break;
 	}
 
 	if (chld && n->child)
 		print_mdoc_nodelist(p, &npair, meta, n->child);
 
 	term_fontpopq(p,
 	    (ENDBODY_NOT == n->end ? n : n->pending)->prev_font);
 
 	switch (n->type) {
 	case MDOC_TEXT:
 		break;
 	case MDOC_TBL:
 		break;
 	case MDOC_EQN:
 		break;
 	default:
 		if ( ! termacts[n->tok].post || MDOC_ENDED & n->flags)
 			break;
 		(void)(*termacts[n->tok].post)(p, &npair, meta, n);
 
 		/*
 		 * Explicit end tokens not only call the post
 		 * handler, but also tell the respective block
 		 * that it must not call the post handler again.
 		 */
 		if (ENDBODY_NOT != n->end)
 			n->pending->flags |= MDOC_ENDED;
 
 		/*
 		 * End of line terminating an implicit block
 		 * while an explicit block is still open.
 		 * Continue the explicit block without spacing.
 		 */
 		if (ENDBODY_NOSPACE == n->end)
 			p->flags |= TERMP_NOSPACE;
 		break;
 	}
 
 	if (MDOC_EOS & n->flags)
 		p->flags |= TERMP_SENTENCE;
 
 	if (MDOC_ll != n->tok) {
 		p->offset = offset;
 		p->rmargin = rmargin;
 	}
 }
 
 static void
 print_mdoc_foot(struct termp *p, const void *arg)
 {
 	const struct mdoc_meta *meta;
+	size_t sz;
 
 	meta = (const struct mdoc_meta *)arg;
 
 	term_fontrepl(p, TERMFONT_NONE);
 
 	/*
 	 * Output the footer in new-groff style, that is, three columns
 	 * with the middle being the manual date and flanking columns
 	 * being the operating system:
 	 *
 	 * SYSTEM                  DATE                    SYSTEM
 	 */
 
 	term_vspace(p);
 
 	p->offset = 0;
-	p->rmargin = (p->maxrmargin -
-	    term_strlen(p, meta->date) + term_len(p, 1)) / 2;
+	sz = term_strlen(p, meta->date);
+	p->rmargin = p->maxrmargin > sz ?
+	    (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
 	p->trailspace = 1;
 	p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
 
 	term_word(p, meta->os);
 	term_flushln(p);
 
 	p->offset = p->rmargin;
-	p->rmargin = p->maxrmargin - term_strlen(p, meta->os);
+	sz = term_strlen(p, meta->os);
+	p->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
 	p->flags |= TERMP_NOSPACE;
 
 	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, meta->os);
 	term_flushln(p);
 
 	p->offset = 0;
 	p->rmargin = p->maxrmargin;
 	p->flags = 0;
 }
 
 static void
 print_mdoc_head(struct termp *p, const void *arg)
 {
 	const struct mdoc_meta	*meta;
 	char			*volume, *title;
 	size_t			 vollen, titlen;
 
 	meta = (const struct mdoc_meta *)arg;
 
 	/*
 	 * The header is strange.  It has three components, which are
 	 * really two with the first duplicated.  It goes like this:
 	 *
 	 * IDENTIFIER              TITLE                   IDENTIFIER
 	 *
 	 * The IDENTIFIER is NAME(SECTION), which is the command-name
 	 * (if given, or "unknown" if not) followed by the manual page
 	 * section.  These are given in `Dt'.  The TITLE is a free-form
 	 * string depending on the manual volume.  If not specified, it
 	 * switches on the manual section.
 	 */
 
-	p->offset = 0;
-	p->rmargin = p->maxrmargin;
-
 	assert(meta->vol);
 	if (NULL == meta->arch)
 		volume = mandoc_strdup(meta->vol);
 	else
 		mandoc_asprintf(&volume, "%s (%s)",
 		    meta->vol, meta->arch);
 	vollen = term_strlen(p, volume);
 
 	if (NULL == meta->msec)
 		title = mandoc_strdup(meta->title);
 	else
 		mandoc_asprintf(&title, "%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) + vollen < p->maxrmargin ?
 	    (p->maxrmargin - vollen + term_len(p, 1)) / 2 :
-	    p->maxrmargin - vollen;
+	    vollen < p->maxrmargin ?  p->maxrmargin - vollen : 0;
 
 	term_word(p, title);
 	term_flushln(p);
 
 	p->flags |= TERMP_NOSPACE;
 	p->offset = p->rmargin;
 	p->rmargin = p->offset + vollen + titlen < p->maxrmargin ?
 	    p->maxrmargin - titlen : p->maxrmargin;
 
 	term_word(p, volume);
 	term_flushln(p);
 
 	p->flags &= ~TERMP_NOBREAK;
 	p->trailspace = 0;
 	if (p->rmargin + titlen <= p->maxrmargin) {
 		p->flags |= TERMP_NOSPACE;
 		p->offset = p->rmargin;
 		p->rmargin = p->maxrmargin;
 		term_word(p, title);
 		term_flushln(p);
 	}
 
 	p->flags &= ~TERMP_NOSPACE;
 	p->offset = 0;
 	p->rmargin = p->maxrmargin;
 	free(title);
 	free(volume);
 }
 
 static size_t
 a2height(const struct termp *p, const char *v)
 {
 	struct roffsu	 su;
 
 
 	assert(v);
 	if ( ! a2roffsu(v, &su, SCALE_VS))
 		SCALE_VS_INIT(&su, atoi(v));
 
 	return(term_vspan(p, &su));
 }
 
 static size_t
 a2width(const struct termp *p, const char *v)
 {
 	struct roffsu	 su;
 
 	assert(v);
-	if ( ! a2roffsu(v, &su, SCALE_MAX))
+	if ( ! a2roffsu(v, &su, SCALE_MAX)) {
 		SCALE_HS_INIT(&su, term_strlen(p, v));
+		su.scale /= term_strlen(p, "0");
+	}
 
 	return(term_hspan(p, &su));
 }
 
-static size_t
-a2offs(const struct termp *p, const char *v)
-{
-	struct roffsu	 su;
-
-	if ('\0' == *v)
-		return(0);
-	else if (0 == strcmp(v, "left"))
-		return(0);
-	else if (0 == strcmp(v, "indent"))
-		return(term_len(p, p->defindent + 1));
-	else if (0 == strcmp(v, "indent-two"))
-		return(term_len(p, (p->defindent + 1) * 2));
-	else if ( ! a2roffsu(v, &su, SCALE_MAX))
-		SCALE_HS_INIT(&su, term_strlen(p, v));
-
-	return(term_hspan(p, &su));
-}
-
 /*
  * Determine how much space to print out before block elements of `It'
  * (and thus `Bl') and `Bd'.  And then go ahead and print that space,
  * too.
  */
 static void
 print_bvspace(struct termp *p,
 	const struct mdoc_node *bl,
 	const struct mdoc_node *n)
 {
 	const struct mdoc_node	*nn;
 
 	assert(n);
 
 	term_newln(p);
 
 	if (MDOC_Bd == bl->tok && bl->norm->Bd.comp)
 		return;
 	if (MDOC_Bl == bl->tok && bl->norm->Bl.comp)
 		return;
 
 	/* Do not vspace directly after Ss/Sh. */
 
-	for (nn = n; nn; nn = nn->parent) {
-		if (MDOC_BLOCK != nn->type)
-			continue;
-		if (MDOC_Ss == nn->tok)
+	nn = n;
+	while (nn->prev == NULL) {
+		do {
+			nn = nn->parent;
+			if (nn->type == MDOC_ROOT)
+				return;
+		} while (nn->type != MDOC_BLOCK);
+		if (nn->tok == MDOC_Sh || nn->tok == MDOC_Ss)
 			return;
-		if (MDOC_Sh == nn->tok)
-			return;
-		if (NULL == nn->prev)
-			continue;
-		break;
+		if (nn->tok == MDOC_It &&
+		    nn->parent->parent->norm->Bl.type != LIST_item)
+			break;
 	}
 
 	/* A `-column' does not assert vspace within the list. */
 
 	if (MDOC_Bl == bl->tok && LIST_column == bl->norm->Bl.type)
 		if (n->prev && MDOC_It == n->prev->tok)
 			return;
 
 	/* A `-diag' without body does not vspace. */
 
 	if (MDOC_Bl == bl->tok && LIST_diag == bl->norm->Bl.type)
 		if (n->prev && MDOC_It == n->prev->tok) {
 			assert(n->prev->body);
 			if (NULL == n->prev->body->child)
 				return;
 		}
 
 	term_vspace(p);
 }
 
 
 static int
 termp_ll_pre(DECL_ARGS)
 {
 
 	term_setwidth(p, n->nchild ? n->child->string : NULL);
 	return(0);
 }
 
 static int
 termp_it_pre(DECL_ARGS)
 {
 	const struct mdoc_node *bl, *nn;
 	char			buf[24];
 	int			i;
 	size_t			width, offset, ncols, dcol;
 	enum mdoc_list		type;
 
 	if (MDOC_BLOCK == n->type) {
 		print_bvspace(p, n->parent->parent, n);
 		return(1);
 	}
 
 	bl = n->parent->parent->parent;
 	type = bl->norm->Bl.type;
 
 	/*
 	 * First calculate width and offset.  This is pretty easy unless
 	 * we're a -column list, in which case all prior columns must
 	 * be accounted for.
 	 */
 
 	width = offset = 0;
 
 	if (bl->norm->Bl.offs)
-		offset = a2offs(p, bl->norm->Bl.offs);
+		offset = a2width(p, bl->norm->Bl.offs);
 
 	switch (type) {
 	case LIST_column:
 		if (MDOC_HEAD == n->type)
 			break;
 
 		/*
 		 * Imitate groff's column handling:
 		 * - For each earlier column, add its width.
 		 * - For less than 5 columns, add four more blanks per
 		 *   column.
 		 * - For exactly 5 columns, add three more blank per
 		 *   column.
 		 * - For more than 5 columns, add only one column.
 		 */
 		ncols = bl->norm->Bl.ncols;
 		dcol = ncols < 5 ? term_len(p, 4) :
 		    ncols == 5 ? term_len(p, 3) : term_len(p, 1);
 
 		/*
 		 * Calculate the offset by applying all prior MDOC_BODY,
 		 * so we stop at the MDOC_HEAD (NULL == nn->prev).
 		 */
 
 		for (i = 0, nn = n->prev;
 		    nn->prev && i < (int)ncols;
 		    nn = nn->prev, i++)
 			offset += dcol + a2width(p,
 			    bl->norm->Bl.cols[i]);
 
 		/*
 		 * When exceeding the declared number of columns, leave
 		 * the remaining widths at 0.  This will later be
 		 * adjusted to the default width of 10, or, for the last
 		 * column, stretched to the right margin.
 		 */
 		if (i >= (int)ncols)
 			break;
 
 		/*
 		 * Use the declared column widths, extended as explained
 		 * in the preceding paragraph.
 		 */
 		width = a2width(p, bl->norm->Bl.cols[i]) + dcol;
 		break;
 	default:
 		if (NULL == bl->norm->Bl.width)
 			break;
 
 		/*
 		 * Note: buffer the width by 2, which is groff's magic
 		 * number for buffering single arguments.  See the above
 		 * handling for column for how this changes.
 		 */
 		assert(bl->norm->Bl.width);
 		width = a2width(p, bl->norm->Bl.width) + term_len(p, 2);
 		break;
 	}
 
 	/*
 	 * List-type can override the width in the case of fixed-head
 	 * values (bullet, dash/hyphen, enum).  Tags need a non-zero
 	 * offset.
 	 */
 
 	switch (type) {
 	case LIST_bullet:
 		/* FALLTHROUGH */
 	case LIST_dash:
 		/* FALLTHROUGH */
 	case LIST_hyphen:
 		/* FALLTHROUGH */
 	case LIST_enum:
 		if (width < term_len(p, 2))
 			width = term_len(p, 2);
 		break;
 	case LIST_hang:
 		if (0 == width)
 			width = term_len(p, 8);
 		break;
 	case LIST_column:
 		/* FALLTHROUGH */
 	case LIST_tag:
 		if (0 == width)
 			width = term_len(p, 10);
 		break;
 	default:
 		break;
 	}
 
 	/*
 	 * Whitespace control.  Inset bodies need an initial space,
 	 * while diagonal bodies need two.
 	 */
 
 	p->flags |= TERMP_NOSPACE;
 
 	switch (type) {
 	case LIST_diag:
 		if (MDOC_BODY == n->type)
 			term_word(p, "\\ \\ ");
 		break;
 	case LIST_inset:
 		if (MDOC_BODY == n->type && n->parent->head->nchild)
 			term_word(p, "\\ ");
 		break;
 	default:
 		break;
 	}
 
 	p->flags |= TERMP_NOSPACE;
 
 	switch (type) {
 	case LIST_diag:
 		if (MDOC_HEAD == n->type)
 			term_fontpush(p, TERMFONT_BOLD);
 		break;
 	default:
 		break;
 	}
 
 	/*
 	 * Pad and break control.  This is the tricky part.  These flags
 	 * are documented in term_flushln() in term.c.  Note that we're
 	 * going to unset all of these flags in termp_it_post() when we
 	 * exit.
 	 */
 
 	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_hyphen:
 		if (MDOC_HEAD != n->type)
 			break;
 		p->flags |= TERMP_NOBREAK;
 		p->trailspace = 1;
 		break;
 	case LIST_hang:
 		if (MDOC_HEAD != n->type)
 			break;
 
 		/*
 		 * This is ugly.  If `-hang' is specified and the body
 		 * is a `Bl' or `Bd', then we want basically to nullify
 		 * the "overstep" effect in term_flushln() and treat
 		 * this as a `-ohang' list instead.
 		 */
-		if (n->next->child &&
+		if (NULL != n->next &&
+		    NULL != n->next->child &&
 		    (MDOC_Bl == n->next->child->tok ||
 		     MDOC_Bd == n->next->child->tok))
 			break;
 
 		p->flags |= TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG;
 		p->trailspace = 1;
 		break;
 	case LIST_tag:
 		if (MDOC_HEAD != n->type)
 			break;
 
 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
 		p->trailspace = 2;
 
 		if (NULL == n->next || NULL == n->next->child)
 			p->flags |= TERMP_DANGLE;
 		break;
 	case LIST_column:
 		if (MDOC_HEAD == n->type)
 			break;
 
 		if (NULL == n->next) {
 			p->flags &= ~TERMP_NOBREAK;
 			p->trailspace = 0;
 		} else {
 			p->flags |= TERMP_NOBREAK;
 			p->trailspace = 1;
 		}
 
 		break;
 	case LIST_diag:
 		if (MDOC_HEAD != n->type)
 			break;
 		p->flags |= TERMP_NOBREAK | TERMP_BRIND;
 		p->trailspace = 1;
 		break;
 	default:
 		break;
 	}
 
 	/*
 	 * Margin control.  Set-head-width lists have their right
 	 * margins shortened.  The body for these lists has the offset
 	 * necessarily lengthened.  Everybody gets the offset.
 	 */
 
 	p->offset += offset;
 
 	switch (type) {
 	case LIST_hang:
 		/*
 		 * Same stipulation as above, regarding `-hang'.  We
 		 * don't want to recalculate rmargin and offsets when
 		 * using `Bd' or `Bl' within `-hang' overstep lists.
 		 */
-		if (MDOC_HEAD == n->type && n->next->child &&
+		if (MDOC_HEAD == n->type &&
+		    NULL != n->next &&
+		    NULL != n->next->child &&
 		    (MDOC_Bl == n->next->child->tok ||
 		     MDOC_Bd == n->next->child->tok))
 			break;
 		/* FALLTHROUGH */
 	case LIST_bullet:
 		/* FALLTHROUGH */
 	case LIST_dash:
 		/* FALLTHROUGH */
 	case LIST_enum:
 		/* FALLTHROUGH */
 	case LIST_hyphen:
 		/* FALLTHROUGH */
 	case LIST_tag:
 		assert(width);
 		if (MDOC_HEAD == n->type)
 			p->rmargin = p->offset + width;
-		else {
+		else
 			p->offset += width;
-			if (p->rmargin < p->offset)
-				p->rmargin = p->offset;
-		}
 		break;
 	case LIST_column:
 		assert(width);
 		p->rmargin = p->offset + width;
 		/*
 		 * XXX - this behaviour is not documented: the
 		 * right-most column is filled to the right margin.
 		 */
 		if (MDOC_HEAD == n->type)
 			break;
 		if (NULL == n->next && p->rmargin < p->maxrmargin)
 			p->rmargin = p->maxrmargin;
 		break;
 	default:
 		break;
 	}
 
 	/*
 	 * The dash, hyphen, bullet and enum lists all have a special
 	 * HEAD character (temporarily bold, in some cases).
 	 */
 
 	if (MDOC_HEAD == n->type)
 		switch (type) {
 		case LIST_bullet:
 			term_fontpush(p, TERMFONT_BOLD);
 			term_word(p, "\\[bu]");
 			term_fontpop(p);
 			break;
 		case LIST_dash:
 			/* FALLTHROUGH */
 		case LIST_hyphen:
 			term_fontpush(p, TERMFONT_BOLD);
 			term_word(p, "\\(hy");
 			term_fontpop(p);
 			break;
 		case LIST_enum:
 			(pair->ppair->ppair->count)++;
 			(void)snprintf(buf, sizeof(buf), "%d.",
 			    pair->ppair->ppair->count);
 			term_word(p, buf);
 			break;
 		default:
 			break;
 		}
 
 	/*
 	 * If we're not going to process our children, indicate so here.
 	 */
 
 	switch (type) {
 	case LIST_bullet:
 		/* FALLTHROUGH */
 	case LIST_item:
 		/* FALLTHROUGH */
 	case LIST_dash:
 		/* FALLTHROUGH */
 	case LIST_hyphen:
 		/* FALLTHROUGH */
 	case LIST_enum:
 		if (MDOC_HEAD == n->type)
 			return(0);
 		break;
 	case LIST_column:
 		if (MDOC_HEAD == n->type)
 			return(0);
 		break;
 	default:
 		break;
 	}
 
 	return(1);
 }
 
 static void
 termp_it_post(DECL_ARGS)
 {
 	enum mdoc_list	   type;
 
 	if (MDOC_BLOCK == n->type)
 		return;
 
 	type = n->parent->parent->parent->norm->Bl.type;
 
 	switch (type) {
 	case LIST_item:
 		/* FALLTHROUGH */
 	case LIST_diag:
 		/* FALLTHROUGH */
 	case LIST_inset:
 		if (MDOC_BODY == n->type)
 			term_newln(p);
 		break;
 	case LIST_column:
 		if (MDOC_BODY == n->type)
 			term_flushln(p);
 		break;
 	default:
 		term_newln(p);
 		break;
 	}
 
 	/*
 	 * Now that our output is flushed, we can reset our tags.  Since
 	 * only `It' sets these flags, we're free to assume that nobody
 	 * has munged them in the meanwhile.
 	 */
 
 	p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
 			TERMP_DANGLE | TERMP_HANG);
 	p->trailspace = 0;
 }
 
 static int
 termp_nm_pre(DECL_ARGS)
 {
+	const char	*cp;
 
 	if (MDOC_BLOCK == n->type) {
 		p->flags |= TERMP_PREKEEP;
 		return(1);
 	}
 
 	if (MDOC_BODY == n->type) {
 		if (NULL == n->child)
 			return(0);
 		p->flags |= TERMP_NOSPACE;
-		p->offset += term_len(p, 1) +
-		    (NULL == n->prev->child ?
-		     term_strlen(p, meta->name) :
-		     MDOC_TEXT == n->prev->child->type ?
-		     term_strlen(p, n->prev->child->string) :
-		     term_len(p, 5));
-		if (p->rmargin < p->offset)
-			p->rmargin = p->offset;
+		cp = NULL;
+		if (n->prev->child != NULL)
+		    cp = n->prev->child->string;
+		if (cp == NULL)
+			cp = meta->name;
+		if (cp == NULL)
+			p->offset += term_len(p, 6);
+		else
+			p->offset += term_len(p, 1) + term_strlen(p, cp);
 		return(1);
 	}
 
 	if (NULL == n->child && NULL == meta->name)
 		return(0);
 
 	if (MDOC_HEAD == n->type)
 		synopsis_pre(p, n->parent);
 
-	if (MDOC_HEAD == n->type && n->next->child) {
+	if (MDOC_HEAD == n->type &&
+	    NULL != n->next && NULL != n->next->child) {
 		p->flags |= TERMP_NOSPACE | TERMP_NOBREAK | TERMP_BRIND;
 		p->trailspace = 1;
 		p->rmargin = p->offset + term_len(p, 1);
 		if (NULL == n->child) {
 			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)
 				p->flags |= TERMP_HANG;
 		} else {
 			p->rmargin += term_len(p, 5);
 			p->flags |= TERMP_HANG;
 		}
 	}
 
 	term_fontpush(p, TERMFONT_BOLD);
 	if (NULL == n->child)
 		term_word(p, meta->name);
 	return(1);
 }
 
 static void
 termp_nm_post(DECL_ARGS)
 {
 
 	if (MDOC_BLOCK == n->type) {
 		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
-	} else if (MDOC_HEAD == n->type && n->next->child) {
+	} else if (MDOC_HEAD == n->type &&
+	    NULL != n->next && NULL != n->next->child) {
 		term_flushln(p);
 		p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND | TERMP_HANG);
 		p->trailspace = 0;
 	} else if (MDOC_BODY == n->type && n->child)
 		term_flushln(p);
 }
 
 static int
 termp_fl_pre(DECL_ARGS)
 {
 
 	term_fontpush(p, TERMFONT_BOLD);
 	term_word(p, "\\-");
 
-	if (n->child)
+	if ( ! (n->nchild == 0 &&
+	    (n->next == NULL ||
+	     n->next->type == MDOC_TEXT ||
+	     n->next->flags & MDOC_LINE)))
 		p->flags |= TERMP_NOSPACE;
-	else if (n->next && n->next->line == n->line)
-		p->flags |= TERMP_NOSPACE;
 
 	return(1);
 }
 
 static int
 termp__a_pre(DECL_ARGS)
 {
 
 	if (n->prev && MDOC__A == n->prev->tok)
 		if (NULL == n->next || MDOC__A != n->next->tok)
 			term_word(p, "and");
 
 	return(1);
 }
 
 static int
 termp_an_pre(DECL_ARGS)
 {
 
-	if (NULL == n->child)
-		return(1);
+	if (n->norm->An.auth == AUTH_split) {
+		p->flags &= ~TERMP_NOSPLIT;
+		p->flags |= TERMP_SPLIT;
+		return(0);
+	}
+	if (n->norm->An.auth == AUTH_nosplit) {
+		p->flags &= ~TERMP_SPLIT;
+		p->flags |= TERMP_NOSPLIT;
+		return(0);
+	}
 
-	/*
-	 * If not in the AUTHORS section, `An -split' will cause
-	 * newlines to occur before the author name.  If in the AUTHORS
-	 * section, by default, the first `An' invocation is nosplit,
-	 * then all subsequent ones, regardless of whether interspersed
-	 * with other macros/text, are split.  -split, in this case,
-	 * will override the condition of the implied first -nosplit.
-	 */
+	if (n->child == NULL)
+		return(0);
 
-	if (n->sec == SEC_AUTHORS) {
-		if ( ! (TERMP_ANPREC & p->flags)) {
-			if (TERMP_SPLIT & p->flags)
-				term_newln(p);
-			return(1);
-		}
-		if (TERMP_NOSPLIT & p->flags)
-			return(1);
+	if (p->flags & TERMP_SPLIT)
 		term_newln(p);
-		return(1);
-	}
 
-	if (TERMP_SPLIT & p->flags)
-		term_newln(p);
+	if (n->sec == SEC_AUTHORS && ! (p->flags & TERMP_NOSPLIT))
+		p->flags |= TERMP_SPLIT;
 
 	return(1);
 }
 
-static void
-termp_an_post(DECL_ARGS)
-{
-
-	if (n->child) {
-		if (SEC_AUTHORS == n->sec)
-			p->flags |= TERMP_ANPREC;
-		return;
-	}
-
-	if (AUTH_split == n->norm->An.auth) {
-		p->flags &= ~TERMP_NOSPLIT;
-		p->flags |= TERMP_SPLIT;
-	} else if (AUTH_nosplit == n->norm->An.auth) {
-		p->flags &= ~TERMP_SPLIT;
-		p->flags |= TERMP_NOSPLIT;
-	}
-
-}
-
 static int
 termp_ns_pre(DECL_ARGS)
 {
 
 	if ( ! (MDOC_LINE & n->flags))
 		p->flags |= TERMP_NOSPACE;
 	return(1);
 }
 
 static int
 termp_rs_pre(DECL_ARGS)
 {
 
 	if (SEC_SEE_ALSO != n->sec)
 		return(1);
 	if (MDOC_BLOCK == n->type && n->prev)
 		term_vspace(p);
 	return(1);
 }
 
 static int
 termp_rv_pre(DECL_ARGS)
 {
 	int		 nchild;
 
 	term_newln(p);
 
 	nchild = n->nchild;
 	if (nchild > 0) {
 		term_word(p, "The");
 
 		for (n = n->child; n; n = n->next) {
 			term_fontpush(p, TERMFONT_BOLD);
 			term_word(p, n->string);
 			term_fontpop(p);
 
 			p->flags |= TERMP_NOSPACE;
 			term_word(p, "()");
 
 			if (n->next == NULL)
 				continue;
 
 			if (nchild > 2) {
 				p->flags |= TERMP_NOSPACE;
 				term_word(p, ",");
 			}
 			if (n->next->next == NULL)
 				term_word(p, "and");
 		}
 
 		if (nchild > 1)
 			term_word(p, "functions return");
 		else
 			term_word(p, "function returns");
 
 		term_word(p, "the value\\~0 if successful;");
 	} else
 		term_word(p, "Upon successful completion,"
 		    " the value\\~0 is returned;");
 
 	term_word(p, "otherwise the value\\~\\-1 is returned"
 	    " and the global variable");
 
 	term_fontpush(p, TERMFONT_UNDER);
 	term_word(p, "errno");
 	term_fontpop(p);
 
 	term_word(p, "is set to indicate the error.");
 	p->flags |= TERMP_SENTENCE;
 
 	return(0);
 }
 
 static int
 termp_ex_pre(DECL_ARGS)
 {
 	int		 nchild;
 
 	term_newln(p);
 	term_word(p, "The");
 
 	nchild = n->nchild;
 	for (n = n->child; n; n = n->next) {
 		term_fontpush(p, TERMFONT_BOLD);
 		term_word(p, n->string);
 		term_fontpop(p);
 
 		if (nchild > 2 && n->next) {
 			p->flags |= TERMP_NOSPACE;
 			term_word(p, ",");
 		}
 
 		if (n->next && NULL == n->next->next)
 			term_word(p, "and");
 	}
 
 	if (nchild > 1)
 		term_word(p, "utilities exit\\~0");
 	else
 		term_word(p, "utility exits\\~0");
 
 	term_word(p, "on success, and\\~>0 if an error occurs.");
 
 	p->flags |= TERMP_SENTENCE;
 	return(0);
 }
 
 static int
 termp_nd_pre(DECL_ARGS)
 {
 
-	if (MDOC_BODY != n->type)
-		return(1);
-
-#if defined(__OpenBSD__) || defined(__linux__)
-	term_word(p, "\\(en");
-#else
-	term_word(p, "\\(em");
-#endif
+	if (n->type == MDOC_BODY)
+		term_word(p, "\\(en");
 	return(1);
 }
 
 static int
 termp_bl_pre(DECL_ARGS)
 {
 
 	return(MDOC_HEAD != n->type);
 }
 
 static void
 termp_bl_post(DECL_ARGS)
 {
 
 	if (MDOC_BLOCK == n->type)
 		term_newln(p);
 }
 
 static int
 termp_xr_pre(DECL_ARGS)
 {
 
 	if (NULL == (n = n->child))
 		return(0);
 
 	assert(MDOC_TEXT == n->type);
 	term_word(p, n->string);
 
 	if (NULL == (n = n->next))
 		return(0);
 
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, "(");
 	p->flags |= TERMP_NOSPACE;
 
 	assert(MDOC_TEXT == n->type);
 	term_word(p, n->string);
 
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, ")");
 
 	return(0);
 }
 
 /*
  * This decides how to assert whitespace before any of the SYNOPSIS set
  * of macros (which, as in the case of Ft/Fo and Ft/Fn, may contain
  * macro combos).
  */
 static void
 synopsis_pre(struct termp *p, const struct mdoc_node *n)
 {
 	/*
 	 * Obviously, if we're not in a SYNOPSIS or no prior macros
 	 * exist, do nothing.
 	 */
 	if (NULL == n->prev || ! (MDOC_SYNPRETTY & n->flags))
 		return;
 
 	/*
 	 * If we're the second in a pair of like elements, emit our
 	 * newline and return.  UNLESS we're `Fo', `Fn', `Fn', in which
 	 * case we soldier on.
 	 */
 	if (n->prev->tok == n->tok &&
 	    MDOC_Ft != n->tok &&
 	    MDOC_Fo != n->tok &&
 	    MDOC_Fn != n->tok) {
 		term_newln(p);
 		return;
 	}
 
 	/*
 	 * If we're one of the SYNOPSIS set and non-like pair-wise after
 	 * another (or Fn/Fo, which we've let slip through) then assert
 	 * vertical space, else only newline and move on.
 	 */
 	switch (n->prev->tok) {
 	case MDOC_Fd:
 		/* FALLTHROUGH */
 	case MDOC_Fn:
 		/* FALLTHROUGH */
 	case MDOC_Fo:
 		/* FALLTHROUGH */
 	case MDOC_In:
 		/* FALLTHROUGH */
 	case MDOC_Vt:
 		term_vspace(p);
 		break;
 	case MDOC_Ft:
 		if (MDOC_Fn != n->tok && MDOC_Fo != n->tok) {
 			term_vspace(p);
 			break;
 		}
 		/* FALLTHROUGH */
 	default:
 		term_newln(p);
 		break;
 	}
 }
 
 static int
 termp_vt_pre(DECL_ARGS)
 {
 
 	if (MDOC_ELEM == n->type) {
 		synopsis_pre(p, n);
 		return(termp_under_pre(p, pair, meta, n));
 	} else if (MDOC_BLOCK == n->type) {
 		synopsis_pre(p, n);
 		return(1);
 	} else if (MDOC_HEAD == n->type)
 		return(0);
 
 	return(termp_under_pre(p, pair, meta, n));
 }
 
 static int
 termp_bold_pre(DECL_ARGS)
 {
 
 	term_fontpush(p, TERMFONT_BOLD);
 	return(1);
 }
 
 static int
 termp_fd_pre(DECL_ARGS)
 {
 
 	synopsis_pre(p, n);
 	return(termp_bold_pre(p, pair, meta, n));
 }
 
 static void
 termp_fd_post(DECL_ARGS)
 {
 
 	term_newln(p);
 }
 
 static int
 termp_sh_pre(DECL_ARGS)
 {
 
-	/* No vspace between consecutive `Sh' calls. */
-
 	switch (n->type) {
 	case MDOC_BLOCK:
-		if (n->prev && MDOC_Sh == n->prev->tok)
-			if (NULL == n->prev->body->child)
-				break;
-		term_vspace(p);
+		/*
+		 * Vertical space before sections, except
+		 * when the previous section was empty.
+		 */
+		if (n->prev == NULL ||
+		    MDOC_Sh != n->prev->tok ||
+		    (n->prev->body != NULL &&
+		     n->prev->body->child != NULL))
+			term_vspace(p);
 		break;
 	case MDOC_HEAD:
 		term_fontpush(p, TERMFONT_BOLD);
 		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;
 	}
 	return(1);
 }
 
 static void
 termp_sh_post(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MDOC_HEAD:
 		term_newln(p);
 		break;
 	case MDOC_BODY:
 		term_newln(p);
 		p->offset = 0;
 		break;
 	default:
 		break;
 	}
 }
 
 static int
 termp_bt_pre(DECL_ARGS)
 {
 
 	term_word(p, "is currently in beta test.");
 	p->flags |= TERMP_SENTENCE;
 	return(0);
 }
 
 static void
 termp_lb_post(DECL_ARGS)
 {
 
 	if (SEC_LIBRARY == n->sec && MDOC_LINE & n->flags)
 		term_newln(p);
 }
 
 static int
 termp_ud_pre(DECL_ARGS)
 {
 
 	term_word(p, "currently under development.");
 	p->flags |= TERMP_SENTENCE;
 	return(0);
 }
 
 static int
 termp_d1_pre(DECL_ARGS)
 {
 
 	if (MDOC_BLOCK != n->type)
 		return(1);
 	term_newln(p);
 	p->offset += term_len(p, p->defindent + 1);
 	return(1);
 }
 
 static int
 termp_ft_pre(DECL_ARGS)
 {
 
 	/* NB: MDOC_LINE does not effect this! */
 	synopsis_pre(p, n);
 	term_fontpush(p, TERMFONT_UNDER);
 	return(1);
 }
 
 static int
 termp_fn_pre(DECL_ARGS)
 {
 	size_t		 rmargin = 0;
 	int		 pretty;
 
 	pretty = MDOC_SYNPRETTY & n->flags;
 
 	synopsis_pre(p, n);
 
 	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_BRIND | 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_BRIND | TERMP_HANG);
 		p->offset = p->rmargin;
 		p->rmargin = rmargin;
 	}
 
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, "(");
 	p->flags |= TERMP_NOSPACE;
 
 	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);
 
 		if (n->next) {
 			p->flags |= TERMP_NOSPACE;
 			term_word(p, ",");
 		}
 	}
 
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, ")");
 
 	if (pretty) {
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, ";");
 		term_flushln(p);
 	}
 
 	return(0);
 }
 
 static int
 termp_fa_pre(DECL_ARGS)
 {
 	const struct mdoc_node	*nn;
 
 	if (n->parent->tok != MDOC_Fo) {
 		term_fontpush(p, TERMFONT_UNDER);
 		return(1);
 	}
 
 	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 || (n->next && n->next->tok == MDOC_Fa)) {
 			p->flags |= TERMP_NOSPACE;
 			term_word(p, ",");
 		}
 	}
 
 	return(0);
 }
 
 static int
 termp_bd_pre(DECL_ARGS)
 {
 	size_t			 tabwidth, lm, len, rm, rmax;
 	struct mdoc_node	*nn;
 
 	if (MDOC_BLOCK == n->type) {
 		print_bvspace(p, n, n);
 		return(1);
 	} else if (MDOC_HEAD == n->type)
 		return(0);
 
-	if (n->norm->Bd.offs)
-		p->offset += a2offs(p, n->norm->Bd.offs);
+	/* Handle the -offset argument. */
 
+	if (n->norm->Bd.offs == NULL ||
+	    ! strcmp(n->norm->Bd.offs, "left"))
+		/* nothing */;
+	else if ( ! strcmp(n->norm->Bd.offs, "indent"))
+		p->offset += term_len(p, p->defindent + 1);
+	else if ( ! strcmp(n->norm->Bd.offs, "indent-two"))
+		p->offset += term_len(p, (p->defindent + 1) * 2);
+	else
+		p->offset += a2width(p, n->norm->Bd.offs);
+
 	/*
 	 * If -ragged or -filled are specified, the block does nothing
 	 * but change the indentation.  If -unfilled or -literal are
 	 * specified, text is printed exactly as entered in the display:
 	 * for macro lines, a newline is appended to the line.  Blank
 	 * lines are allowed.
 	 */
 
 	if (DISP_literal != n->norm->Bd.type &&
 	    DISP_unfilled != n->norm->Bd.type &&
 	    DISP_centered != n->norm->Bd.type)
 		return(1);
 
 	tabwidth = p->tabwidth;
 	if (DISP_literal == n->norm->Bd.type)
 		p->tabwidth = term_len(p, 8);
 
 	lm = p->offset;
 	rm = p->rmargin;
 	rmax = p->maxrmargin;
 	p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
 
 	for (nn = n->child; nn; nn = nn->next) {
 		if (DISP_centered == n->norm->Bd.type) {
 			if (MDOC_TEXT == nn->type) {
 				len = term_strlen(p, nn->string);
 				p->offset = len >= rm ? 0 :
 				    lm + len >= rm ? rm - len :
 				    (lm + rm - len) / 2;
 			} else
 				p->offset = lm;
 		}
 		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
 		 * notion of selective eoln whitespace is pretty dumb
 		 * anyway, so don't sweat it.
 		 */
 		switch (nn->tok) {
 		case MDOC_Sm:
 			/* FALLTHROUGH */
 		case MDOC_br:
 			/* FALLTHROUGH */
 		case MDOC_sp:
 			/* FALLTHROUGH */
 		case MDOC_Bl:
 			/* FALLTHROUGH */
 		case MDOC_D1:
 			/* FALLTHROUGH */
 		case MDOC_Dl:
 			/* FALLTHROUGH */
 		case MDOC_Lp:
 			/* FALLTHROUGH */
 		case MDOC_Pp:
 			continue;
 		default:
 			break;
 		}
 		if (nn->next && nn->next->line == nn->line)
 			continue;
 		term_flushln(p);
 		p->flags |= TERMP_NOSPACE;
 	}
 
 	p->tabwidth = tabwidth;
 	p->rmargin = rm;
 	p->maxrmargin = rmax;
 	return(0);
 }
 
 static void
 termp_bd_post(DECL_ARGS)
 {
 	size_t		 rm, rmax;
 
 	if (MDOC_BODY != n->type)
 		return;
 
 	rm = p->rmargin;
 	rmax = p->maxrmargin;
 
 	if (DISP_literal == n->norm->Bd.type ||
 	    DISP_unfilled == n->norm->Bd.type)
 		p->rmargin = p->maxrmargin = TERM_MAXMARGIN;
 
 	p->flags |= TERMP_NOSPACE;
 	term_newln(p);
 
 	p->rmargin = rm;
 	p->maxrmargin = rmax;
 }
 
 static int
 termp_bx_pre(DECL_ARGS)
 {
 
 	if (NULL != (n = n->child)) {
 		term_word(p, n->string);
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, "BSD");
 	} else {
 		term_word(p, "BSD");
 		return(0);
 	}
 
 	if (NULL != (n = n->next)) {
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, "-");
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, n->string);
 	}
 
 	return(0);
 }
 
 static int
 termp_xx_pre(DECL_ARGS)
 {
 	const char	*pp;
 	int		 flags;
 
 	pp = NULL;
 	switch (n->tok) {
 	case MDOC_Bsx:
 		pp = "BSD/OS";
 		break;
 	case MDOC_Dx:
 		pp = "DragonFly";
 		break;
 	case MDOC_Fx:
 		pp = "FreeBSD";
 		break;
 	case MDOC_Nx:
 		pp = "NetBSD";
 		break;
 	case MDOC_Ox:
 		pp = "OpenBSD";
 		break;
 	case MDOC_Ux:
 		pp = "UNIX";
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	term_word(p, pp);
 	if (n->child) {
 		flags = p->flags;
 		p->flags |= TERMP_KEEP;
 		term_word(p, n->child->string);
 		p->flags = flags;
 	}
 	return(0);
 }
 
 static void
 termp_pf_post(DECL_ARGS)
 {
 
 	p->flags |= TERMP_NOSPACE;
 }
 
 static int
 termp_ss_pre(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MDOC_BLOCK:
 		term_newln(p);
 		if (n->prev)
 			term_vspace(p);
 		break;
 	case MDOC_HEAD:
 		term_fontpush(p, TERMFONT_BOLD);
 		p->offset = term_len(p, (p->defindent+1)/2);
 		break;
 	case MDOC_BODY:
 		p->offset = term_len(p, p->defindent);
 		break;
 	default:
 		break;
 	}
 
 	return(1);
 }
 
 static void
 termp_ss_post(DECL_ARGS)
 {
 
 	if (n->type == MDOC_HEAD || n->type == MDOC_BODY)
 		term_newln(p);
 }
 
 static int
 termp_cd_pre(DECL_ARGS)
 {
 
 	synopsis_pre(p, n);
 	term_fontpush(p, TERMFONT_BOLD);
 	return(1);
 }
 
 static int
 termp_in_pre(DECL_ARGS)
 {
 
 	synopsis_pre(p, n);
 
 	if (MDOC_SYNPRETTY & n->flags && MDOC_LINE & n->flags) {
 		term_fontpush(p, TERMFONT_BOLD);
 		term_word(p, "#include");
 		term_word(p, "<");
 	} else {
 		term_word(p, "<");
 		term_fontpush(p, TERMFONT_UNDER);
 	}
 
 	p->flags |= TERMP_NOSPACE;
 	return(1);
 }
 
 static void
 termp_in_post(DECL_ARGS)
 {
 
 	if (MDOC_SYNPRETTY & n->flags)
 		term_fontpush(p, TERMFONT_BOLD);
 
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, ">");
 
 	if (MDOC_SYNPRETTY & n->flags)
 		term_fontpop(p);
 }
 
 static int
 termp_sp_pre(DECL_ARGS)
 {
 	size_t		 i, len;
 
 	switch (n->tok) {
 	case MDOC_sp:
 		len = n->child ? a2height(p, n->child->string) : 1;
 		break;
 	case MDOC_br:
 		len = 0;
 		break;
 	default:
 		len = 1;
 		break;
 	}
 
 	if (0 == len)
 		term_newln(p);
 	for (i = 0; i < len; i++)
 		term_vspace(p);
 
 	return(0);
 }
 
 static int
-termp_es_pre(DECL_ARGS)
+termp_skip_pre(DECL_ARGS)
 {
 
 	return(0);
 }
 
 static int
 termp_quote_pre(DECL_ARGS)
 {
 
 	if (MDOC_BODY != n->type && MDOC_ELEM != n->type)
 		return(1);
 
 	switch (n->tok) {
 	case MDOC_Ao:
 		/* FALLTHROUGH */
 	case MDOC_Aq:
-		term_word(p, "<");
+		term_word(p, n->parent->prev != NULL &&
+		    n->parent->prev->tok == MDOC_An ?  "<" : "\\(la");
 		break;
 	case MDOC_Bro:
 		/* FALLTHROUGH */
 	case MDOC_Brq:
 		term_word(p, "{");
 		break;
 	case MDOC_Oo:
 		/* FALLTHROUGH */
 	case MDOC_Op:
 		/* FALLTHROUGH */
 	case MDOC_Bo:
 		/* FALLTHROUGH */
 	case MDOC_Bq:
 		term_word(p, "[");
 		break;
 	case MDOC_Do:
 		/* FALLTHROUGH */
 	case MDOC_Dq:
 		term_word(p, "\\(lq");
 		break;
 	case MDOC_En:
 		if (NULL == n->norm->Es ||
 		    NULL == n->norm->Es->child)
 			return(1);
 		term_word(p, n->norm->Es->child->string);
 		break;
 	case MDOC_Eo:
 		break;
 	case MDOC_Po:
 		/* FALLTHROUGH */
 	case MDOC_Pq:
 		term_word(p, "(");
 		break;
 	case MDOC__T:
 		/* FALLTHROUGH */
 	case MDOC_Qo:
 		/* FALLTHROUGH */
 	case MDOC_Qq:
 		term_word(p, "\"");
 		break;
 	case MDOC_Ql:
 		/* FALLTHROUGH */
 	case MDOC_So:
 		/* FALLTHROUGH */
 	case MDOC_Sq:
 		term_word(p, "\\(oq");
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	p->flags |= TERMP_NOSPACE;
 	return(1);
 }
 
 static void
 termp_quote_post(DECL_ARGS)
 {
 
-	if (MDOC_BODY != n->type && MDOC_ELEM != n->type)
+	if (n->type != MDOC_BODY && n->type != MDOC_ELEM)
 		return;
 
-	if (MDOC_En != n->tok)
+	if ( ! (n->tok == MDOC_En ||
+	    (n->tok == MDOC_Eo && n->end == ENDBODY_SPACE)))
 		p->flags |= TERMP_NOSPACE;
 
 	switch (n->tok) {
 	case MDOC_Ao:
 		/* FALLTHROUGH */
 	case MDOC_Aq:
-		term_word(p, ">");
+		term_word(p, n->parent->prev != NULL &&
+		    n->parent->prev->tok == MDOC_An ?  ">" : "\\(ra");
 		break;
 	case MDOC_Bro:
 		/* FALLTHROUGH */
 	case MDOC_Brq:
 		term_word(p, "}");
 		break;
 	case MDOC_Oo:
 		/* FALLTHROUGH */
 	case MDOC_Op:
 		/* FALLTHROUGH */
 	case MDOC_Bo:
 		/* FALLTHROUGH */
 	case MDOC_Bq:
 		term_word(p, "]");
 		break;
 	case MDOC_Do:
 		/* FALLTHROUGH */
 	case MDOC_Dq:
 		term_word(p, "\\(rq");
 		break;
 	case MDOC_En:
 		if (NULL != n->norm->Es &&
 		    NULL != n->norm->Es->child &&
 		    NULL != n->norm->Es->child->next) {
 			p->flags |= TERMP_NOSPACE;
 			term_word(p, n->norm->Es->child->next->string);
 		}
 		break;
 	case MDOC_Eo:
 		break;
 	case MDOC_Po:
 		/* FALLTHROUGH */
 	case MDOC_Pq:
 		term_word(p, ")");
 		break;
 	case MDOC__T:
 		/* FALLTHROUGH */
 	case MDOC_Qo:
 		/* FALLTHROUGH */
 	case MDOC_Qq:
 		term_word(p, "\"");
 		break;
 	case MDOC_Ql:
 		/* FALLTHROUGH */
 	case MDOC_So:
 		/* FALLTHROUGH */
 	case MDOC_Sq:
 		term_word(p, "\\(cq");
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 }
 
 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_BRIND |
 					TERMP_HANG;
 		}
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, "(");
 		p->flags |= TERMP_NOSPACE;
 		if (pretty) {
 			term_flushln(p);
 			p->flags &= ~(TERMP_NOBREAK | TERMP_BRIND |
 					TERMP_HANG);
 			p->offset = p->rmargin;
 			p->rmargin = rmargin;
 		}
 		return(1);
 	}
 
 	if (NULL == n->child)
 		return(0);
 
 	/* XXX: we drop non-initial arguments as per groff. */
 
 	assert(n->child->string);
 	term_fontpush(p, TERMFONT_BOLD);
 	term_word(p, n->child->string);
 	return(0);
 }
 
 static void
 termp_fo_post(DECL_ARGS)
 {
 
 	if (MDOC_BODY != n->type)
 		return;
 
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, ")");
 
 	if (MDOC_SYNPRETTY & n->flags) {
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, ";");
 		term_flushln(p);
 	}
 }
 
 static int
 termp_bf_pre(DECL_ARGS)
 {
 
 	if (MDOC_HEAD == n->type)
 		return(0);
 	else if (MDOC_BODY != n->type)
 		return(1);
 
 	if (FONT_Em == n->norm->Bf.font)
 		term_fontpush(p, TERMFONT_UNDER);
 	else if (FONT_Sy == n->norm->Bf.font)
 		term_fontpush(p, TERMFONT_BOLD);
 	else
 		term_fontpush(p, TERMFONT_NONE);
 
 	return(1);
 }
 
 static int
 termp_sm_pre(DECL_ARGS)
 {
 
 	if (NULL == n->child)
 		p->flags ^= TERMP_NONOSPACE;
 	else if (0 == strcmp("on", n->child->string))
 		p->flags &= ~TERMP_NONOSPACE;
 	else
 		p->flags |= TERMP_NONOSPACE;
 
 	if (p->col && ! (TERMP_NONOSPACE & p->flags))
 		p->flags &= ~TERMP_NOSPACE;
 
 	return(0);
 }
 
 static int
 termp_ap_pre(DECL_ARGS)
 {
 
 	p->flags |= TERMP_NOSPACE;
 	term_word(p, "'");
 	p->flags |= TERMP_NOSPACE;
 	return(1);
 }
 
 static void
 termp____post(DECL_ARGS)
 {
 
 	/*
 	 * Handle lists of authors.  In general, print each followed by
 	 * a comma.  Don't print the comma if there are only two
 	 * authors.
 	 */
 	if (MDOC__A == n->tok && n->next && MDOC__A == n->next->tok)
 		if (NULL == n->next->next || MDOC__A != n->next->next->tok)
 			if (NULL == n->prev || MDOC__A != n->prev->tok)
 				return;
 
 	/* TODO: %U. */
 
 	if (NULL == n->parent || MDOC_Rs != n->parent->tok)
 		return;
 
 	p->flags |= TERMP_NOSPACE;
 	if (NULL == n->next) {
 		term_word(p, ".");
 		p->flags |= TERMP_SENTENCE;
 	} else
 		term_word(p, ",");
 }
 
 static int
 termp_li_pre(DECL_ARGS)
 {
 
 	term_fontpush(p, TERMFONT_NONE);
 	return(1);
 }
 
 static int
 termp_lk_pre(DECL_ARGS)
 {
 	const struct mdoc_node *link, *descr;
 
 	if (NULL == (link = n->child))
 		return(0);
 
 	if (NULL != (descr = link->next)) {
 		term_fontpush(p, TERMFONT_UNDER);
 		while (NULL != descr) {
 			term_word(p, descr->string);
 			descr = descr->next;
 		}
 		p->flags |= TERMP_NOSPACE;
 		term_word(p, ":");
 		term_fontpop(p);
 	}
 
 	term_fontpush(p, TERMFONT_BOLD);
 	term_word(p, link->string);
 	term_fontpop(p);
 
 	return(0);
 }
 
 static int
 termp_bk_pre(DECL_ARGS)
 {
 
 	switch (n->type) {
 	case MDOC_BLOCK:
 		break;
 	case MDOC_HEAD:
 		return(0);
 	case MDOC_BODY:
 		if (n->parent->args || 0 == n->prev->nchild)
 			p->flags |= TERMP_PREKEEP;
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	return(1);
 }
 
 static void
 termp_bk_post(DECL_ARGS)
 {
 
 	if (MDOC_BODY == n->type)
 		p->flags &= ~(TERMP_KEEP | TERMP_PREKEEP);
 }
 
 static void
 termp__t_post(DECL_ARGS)
 {
 
 	/*
 	 * If we're in an `Rs' and there's a journal present, then quote
 	 * us instead of underlining us (for disambiguation).
 	 */
 	if (n->parent && MDOC_Rs == n->parent->tok &&
 	    n->parent->norm->Rs.quote_T)
 		termp_quote_post(p, pair, meta, n);
 
 	termp____post(p, pair, meta, n);
 }
 
 static int
 termp__t_pre(DECL_ARGS)
 {
 
 	/*
 	 * If we're in an `Rs' and there's a journal present, then quote
 	 * us instead of underlining us (for disambiguation).
 	 */
 	if (n->parent && MDOC_Rs == n->parent->tok &&
 	    n->parent->norm->Rs.quote_T)
 		return(termp_quote_pre(p, pair, meta, n));
 
 	term_fontpush(p, TERMFONT_UNDER);
 	return(1);
 }
 
 static int
 termp_under_pre(DECL_ARGS)
 {
 
 	term_fontpush(p, TERMFONT_UNDER);
 	return(1);
 }
Index: vendor/mdocml/dist/mdoc_validate.c
===================================================================
--- vendor/mdocml/dist/mdoc_validate.c	(revision 275396)
+++ vendor/mdocml/dist/mdoc_validate.c	(revision 275397)
@@ -1,2488 +1,2491 @@
-/*	$Id: mdoc_validate.c,v 1.243 2014/08/06 15:09:05 schwarze Exp $ */
+/*	$Id: mdoc_validate.c,v 1.262 2014/11/28 18:36:35 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons 
  * Copyright (c) 2010-2014 Ingo Schwarze 
  * Copyright (c) 2010 Joerg Sonnenberger 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
 #ifndef OSNAME
 #include 
 #endif
 
-#include 
-
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mdoc.h"
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "libmdoc.h"
 #include "libmandoc.h"
 
 /* FIXME: .Bl -diag can't have non-text children in HEAD. */
 
 #define	PRE_ARGS  struct mdoc *mdoc, struct mdoc_node *n
 #define	POST_ARGS struct mdoc *mdoc
 
 enum	check_ineq {
 	CHECK_LT,
 	CHECK_GT,
 	CHECK_EQ
 };
 
 enum	check_lvl {
 	CHECK_WARN,
 	CHECK_ERROR,
 };
 
-typedef	int	(*v_pre)(PRE_ARGS);
-typedef	int	(*v_post)(POST_ARGS);
+typedef	void	(*v_pre)(PRE_ARGS);
+typedef	void	(*v_post)(POST_ARGS);
 
 struct	valids {
 	v_pre	 pre;
 	v_post	 post;
 };
 
-static	int	 check_count(struct mdoc *, enum mdoc_type,
+static	void	 check_count(struct mdoc *, enum mdoc_type,
 			enum check_lvl, enum check_ineq, int);
 static	void	 check_text(struct mdoc *, int, int, char *);
 static	void	 check_argv(struct mdoc *,
 			struct mdoc_node *, struct mdoc_argv *);
 static	void	 check_args(struct mdoc *, struct mdoc_node *);
+static	int	 child_an(const struct mdoc_node *);
 static	enum mdoc_sec	a2sec(const char *);
 static	size_t		macro2len(enum mdoct);
+static	void	 rewrite_macro2len(char **);
 
-static	int	 ebool(POST_ARGS);
-static	int	 berr_ge1(POST_ARGS);
-static	int	 bwarn_ge1(POST_ARGS);
-static	int	 ewarn_eq0(POST_ARGS);
-static	int	 ewarn_eq1(POST_ARGS);
-static	int	 ewarn_ge1(POST_ARGS);
-static	int	 ewarn_le1(POST_ARGS);
-static	int	 hwarn_eq0(POST_ARGS);
-static	int	 hwarn_eq1(POST_ARGS);
-static	int	 hwarn_ge1(POST_ARGS);
+static	void	 bwarn_ge1(POST_ARGS);
+static	void	 ewarn_eq1(POST_ARGS);
+static	void	 ewarn_ge1(POST_ARGS);
+static	void	 hwarn_eq0(POST_ARGS);
 
-static	int	 post_an(POST_ARGS);
-static	int	 post_at(POST_ARGS);
-static	int	 post_bf(POST_ARGS);
-static	int	 post_bk(POST_ARGS);
-static	int	 post_bl(POST_ARGS);
-static	int	 post_bl_block(POST_ARGS);
-static	int	 post_bl_block_width(POST_ARGS);
-static	int	 post_bl_block_tag(POST_ARGS);
-static	int	 post_bl_head(POST_ARGS);
-static	int	 post_bx(POST_ARGS);
-static	int	 post_d1(POST_ARGS);
-static	int	 post_defaults(POST_ARGS);
-static	int	 post_dd(POST_ARGS);
-static	int	 post_dt(POST_ARGS);
-static	int	 post_en(POST_ARGS);
-static	int	 post_es(POST_ARGS);
-static	int	 post_eoln(POST_ARGS);
-static	int	 post_ex(POST_ARGS);
-static	int	 post_fo(POST_ARGS);
-static	int	 post_hyph(POST_ARGS);
-static	int	 post_hyphtext(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_nd(POST_ARGS);
-static	int	 post_nm(POST_ARGS);
-static	int	 post_ns(POST_ARGS);
-static	int	 post_os(POST_ARGS);
-static	int	 post_par(POST_ARGS);
-static	int	 post_root(POST_ARGS);
-static	int	 post_rs(POST_ARGS);
-static	int	 post_sh(POST_ARGS);
-static	int	 post_sh_body(POST_ARGS);
-static	int	 post_sh_head(POST_ARGS);
-static	int	 post_st(POST_ARGS);
-static	int	 post_vt(POST_ARGS);
-static	int	 pre_an(PRE_ARGS);
-static	int	 pre_bd(PRE_ARGS);
-static	int	 pre_bl(PRE_ARGS);
-static	int	 pre_dd(PRE_ARGS);
-static	int	 pre_display(PRE_ARGS);
-static	int	 pre_dt(PRE_ARGS);
-static	int	 pre_literal(PRE_ARGS);
-static	int	 pre_obsolete(PRE_ARGS);
-static	int	 pre_os(PRE_ARGS);
-static	int	 pre_par(PRE_ARGS);
-static	int	 pre_std(PRE_ARGS);
+static	void	 post_an(POST_ARGS);
+static	void	 post_at(POST_ARGS);
+static	void	 post_bf(POST_ARGS);
+static	void	 post_bk(POST_ARGS);
+static	void	 post_bl(POST_ARGS);
+static	void	 post_bl_block(POST_ARGS);
+static	void	 post_bl_block_tag(POST_ARGS);
+static	void	 post_bl_head(POST_ARGS);
+static	void	 post_bx(POST_ARGS);
+static	void	 post_d1(POST_ARGS);
+static	void	 post_defaults(POST_ARGS);
+static	void	 post_dd(POST_ARGS);
+static	void	 post_dt(POST_ARGS);
+static	void	 post_en(POST_ARGS);
+static	void	 post_es(POST_ARGS);
+static	void	 post_eoln(POST_ARGS);
+static	void	 post_ex(POST_ARGS);
+static	void	 post_fa(POST_ARGS);
+static	void	 post_fn(POST_ARGS);
+static	void	 post_fname(POST_ARGS);
+static	void	 post_fo(POST_ARGS);
+static	void	 post_hyph(POST_ARGS);
+static	void	 post_hyphtext(POST_ARGS);
+static	void	 post_ignpar(POST_ARGS);
+static	void	 post_it(POST_ARGS);
+static	void	 post_lb(POST_ARGS);
+static	void	 post_literal(POST_ARGS);
+static	void	 post_nd(POST_ARGS);
+static	void	 post_nm(POST_ARGS);
+static	void	 post_ns(POST_ARGS);
+static	void	 post_os(POST_ARGS);
+static	void	 post_par(POST_ARGS);
+static	void	 post_root(POST_ARGS);
+static	void	 post_rs(POST_ARGS);
+static	void	 post_sh(POST_ARGS);
+static	void	 post_sh_head(POST_ARGS);
+static	void	 post_sh_name(POST_ARGS);
+static	void	 post_sh_see_also(POST_ARGS);
+static	void	 post_sh_authors(POST_ARGS);
+static	void	 post_sm(POST_ARGS);
+static	void	 post_st(POST_ARGS);
+static	void	 post_vt(POST_ARGS);
 
+static	void	 pre_an(PRE_ARGS);
+static	void	 pre_bd(PRE_ARGS);
+static	void	 pre_bl(PRE_ARGS);
+static	void	 pre_dd(PRE_ARGS);
+static	void	 pre_display(PRE_ARGS);
+static	void	 pre_dt(PRE_ARGS);
+static	void	 pre_literal(PRE_ARGS);
+static	void	 pre_obsolete(PRE_ARGS);
+static	void	 pre_os(PRE_ARGS);
+static	void	 pre_par(PRE_ARGS);
+static	void	 pre_std(PRE_ARGS);
+
 static	const struct valids mdoc_valids[MDOC_MAX] = {
 	{ NULL, NULL },				/* Ap */
 	{ pre_dd, post_dd },			/* Dd */
 	{ pre_dt, post_dt },			/* Dt */
 	{ pre_os, post_os },			/* Os */
 	{ NULL, post_sh },			/* Sh */
 	{ NULL, post_ignpar },			/* Ss */
 	{ pre_par, post_par },			/* Pp */
 	{ pre_display, post_d1 },		/* D1 */
 	{ pre_literal, post_literal },		/* Dl */
 	{ pre_bd, post_literal },		/* Bd */
 	{ NULL, NULL },				/* Ed */
 	{ pre_bl, post_bl },			/* Bl */
 	{ NULL, NULL },				/* El */
 	{ pre_par, post_it },			/* It */
 	{ NULL, NULL },				/* Ad */
 	{ pre_an, post_an },			/* An */
 	{ NULL, post_defaults },		/* Ar */
 	{ NULL, NULL },				/* Cd */
 	{ NULL, NULL },				/* Cm */
 	{ NULL, NULL },				/* Dv */
 	{ NULL, NULL },				/* Er */
 	{ NULL, NULL },				/* Ev */
 	{ pre_std, post_ex },			/* Ex */
-	{ NULL, NULL },				/* Fa */
+	{ NULL, post_fa },			/* Fa */
 	{ NULL, ewarn_ge1 },			/* Fd */
 	{ NULL, NULL },				/* Fl */
-	{ NULL, NULL },				/* Fn */
+	{ NULL, post_fn },			/* Fn */
 	{ NULL, NULL },				/* Ft */
 	{ NULL, NULL },				/* Ic */
 	{ NULL, ewarn_eq1 },			/* In */
 	{ NULL, post_defaults },		/* Li */
 	{ NULL, post_nd },			/* Nd */
 	{ NULL, post_nm },			/* Nm */
 	{ NULL, NULL },				/* Op */
 	{ pre_obsolete, NULL },			/* Ot */
 	{ NULL, post_defaults },		/* Pa */
 	{ pre_std, NULL },			/* Rv */
 	{ NULL, post_st },			/* St */
 	{ NULL, NULL },				/* Va */
 	{ NULL, post_vt },			/* Vt */
 	{ NULL, ewarn_ge1 },			/* Xr */
 	{ NULL, ewarn_ge1 },			/* %A */
 	{ NULL, post_hyphtext },		/* %B */ /* FIXME: can be used outside Rs/Re. */
 	{ NULL, ewarn_ge1 },			/* %D */
 	{ NULL, ewarn_ge1 },			/* %I */
 	{ NULL, ewarn_ge1 },			/* %J */
 	{ NULL, post_hyphtext },		/* %N */
 	{ NULL, post_hyphtext },		/* %O */
 	{ NULL, ewarn_ge1 },			/* %P */
 	{ NULL, post_hyphtext },		/* %R */
 	{ NULL, post_hyphtext },		/* %T */ /* FIXME: can be used outside Rs/Re. */
 	{ NULL, ewarn_ge1 },			/* %V */
 	{ NULL, NULL },				/* Ac */
 	{ NULL, NULL },				/* Ao */
 	{ NULL, NULL },				/* Aq */
 	{ NULL, post_at },			/* At */
 	{ NULL, NULL },				/* Bc */
 	{ NULL, post_bf },			/* Bf */
 	{ NULL, NULL },				/* Bo */
 	{ NULL, NULL },				/* Bq */
 	{ NULL, NULL },				/* Bsx */
 	{ NULL, post_bx },			/* Bx */
-	{ NULL, ebool },			/* Db */
+	{ pre_obsolete, NULL },			/* Db */
 	{ NULL, NULL },				/* Dc */
 	{ NULL, NULL },				/* Do */
 	{ NULL, NULL },				/* Dq */
 	{ NULL, NULL },				/* Ec */
 	{ NULL, NULL },				/* Ef */
 	{ NULL, NULL },				/* Em */
 	{ NULL, NULL },				/* Eo */
 	{ NULL, NULL },				/* Fx */
 	{ NULL, NULL },				/* Ms */
-	{ NULL, ewarn_eq0 },			/* No */
+	{ NULL, NULL },				/* No */
 	{ NULL, post_ns },			/* Ns */
 	{ NULL, NULL },				/* Nx */
 	{ NULL, NULL },				/* Ox */
 	{ NULL, NULL },				/* Pc */
 	{ NULL, ewarn_eq1 },			/* Pf */
 	{ NULL, NULL },				/* Po */
 	{ NULL, NULL },				/* Pq */
 	{ NULL, NULL },				/* Qc */
 	{ NULL, NULL },				/* Ql */
 	{ NULL, NULL },				/* Qo */
 	{ NULL, NULL },				/* Qq */
 	{ NULL, NULL },				/* Re */
 	{ NULL, post_rs },			/* Rs */
 	{ NULL, NULL },				/* Sc */
 	{ NULL, NULL },				/* So */
 	{ NULL, NULL },				/* Sq */
-	{ NULL, ebool },			/* Sm */
+	{ NULL, post_sm },			/* Sm */
 	{ NULL, post_hyph },			/* Sx */
 	{ NULL, NULL },				/* Sy */
 	{ NULL, NULL },				/* Tn */
 	{ NULL, NULL },				/* Ux */
 	{ NULL, NULL },				/* Xc */
 	{ NULL, NULL },				/* Xo */
 	{ NULL, post_fo },			/* Fo */
 	{ NULL, NULL },				/* Fc */
 	{ NULL, NULL },				/* Oo */
 	{ NULL, NULL },				/* Oc */
 	{ NULL, post_bk },			/* Bk */
 	{ NULL, NULL },				/* Ek */
 	{ NULL, post_eoln },			/* Bt */
 	{ NULL, NULL },				/* Hf */
 	{ pre_obsolete, NULL },			/* Fr */
 	{ NULL, post_eoln },			/* Ud */
 	{ NULL, post_lb },			/* Lb */
 	{ pre_par, post_par },			/* Lp */
 	{ NULL, NULL },				/* Lk */
 	{ NULL, post_defaults },		/* Mt */
 	{ NULL, NULL },				/* Brq */
 	{ NULL, NULL },				/* Bro */
 	{ NULL, NULL },				/* Brc */
 	{ NULL, ewarn_ge1 },			/* %C */
 	{ pre_obsolete, post_es },		/* Es */
 	{ pre_obsolete, post_en },		/* En */
 	{ NULL, NULL },				/* Dx */
 	{ NULL, ewarn_ge1 },			/* %Q */
 	{ NULL, post_par },			/* br */
 	{ NULL, post_par },			/* sp */
 	{ NULL, ewarn_eq1 },			/* %U */
 	{ NULL, NULL },				/* Ta */
 	{ NULL, NULL },				/* ll */
 };
 
 #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
 
 static	const enum mdoct rsord[RSORD_MAX] = {
 	MDOC__A,
 	MDOC__T,
 	MDOC__B,
 	MDOC__I,
 	MDOC__J,
 	MDOC__R,
 	MDOC__N,
 	MDOC__V,
 	MDOC__U,
 	MDOC__P,
 	MDOC__Q,
 	MDOC__C,
 	MDOC__D,
 	MDOC__O
 };
 
 static	const char * const secnames[SEC__MAX] = {
 	NULL,
 	"NAME",
 	"LIBRARY",
 	"SYNOPSIS",
 	"DESCRIPTION",
 	"CONTEXT",
 	"IMPLEMENTATION NOTES",
 	"RETURN VALUES",
 	"ENVIRONMENT",
 	"FILES",
 	"EXIT STATUS",
 	"EXAMPLES",
 	"DIAGNOSTICS",
 	"COMPATIBILITY",
 	"ERRORS",
 	"SEE ALSO",
 	"STANDARDS",
 	"HISTORY",
 	"AUTHORS",
 	"CAVEATS",
 	"BUGS",
 	"SECURITY CONSIDERATIONS",
 	NULL
 };
 
 
-int
+void
 mdoc_valid_pre(struct mdoc *mdoc, struct mdoc_node *n)
 {
 	v_pre	 p;
 
 	switch (n->type) {
 	case MDOC_TEXT:
 		check_text(mdoc, n->line, n->pos, n->string);
 		/* FALLTHROUGH */
 	case MDOC_TBL:
 		/* FALLTHROUGH */
 	case MDOC_EQN:
 		/* FALLTHROUGH */
 	case MDOC_ROOT:
-		return(1);
+		return;
 	default:
 		break;
 	}
 
 	check_args(mdoc, n);
 	p = mdoc_valids[n->tok].pre;
-	return(*p ? (*p)(mdoc, n) : 1);
+	if (*p)
+		(*p)(mdoc, n);
 }
 
-int
+void
 mdoc_valid_post(struct mdoc *mdoc)
 {
 	struct mdoc_node *n;
 	v_post p;
 
 	n = mdoc->last;
 	if (n->flags & MDOC_VALID)
-		return(1);
+		return;
 	n->flags |= MDOC_VALID;
 
 	switch (n->type) {
 	case MDOC_TEXT:
 		/* FALLTHROUGH */
 	case MDOC_EQN:
 		/* FALLTHROUGH */
 	case MDOC_TBL:
-		return(1);
+		break;
 	case MDOC_ROOT:
-		return(post_root(mdoc));
+		post_root(mdoc);
+		break;
 	default:
+
+		/*
+		 * Closing delimiters are not special at the
+		 * beginning of a block, opening delimiters
+		 * are not special at the end.
+		 */
+
+		if (n->child != NULL)
+			n->child->flags &= ~MDOC_DELIMC;
+		if (n->last != NULL)
+			n->last->flags &= ~MDOC_DELIMO;
+
+		/* Call the macro's postprocessor. */
+
 		p = mdoc_valids[n->tok].post;
-		return(*p ? (*p)(mdoc) : 1);
+		if (*p)
+			(*p)(mdoc);
+		break;
 	}
 }
 
-static int
+static void
 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 (mdoc->last->type != type)
-		return(1);
+		return;
 
 	switch (ineq) {
 	case CHECK_LT:
 		p = "less than ";
 		if (mdoc->last->nchild < val)
-			return(1);
+			return;
 		break;
 	case CHECK_GT:
 		p = "more than ";
 		if (mdoc->last->nchild > val)
-			return(1);
+			return;
 		break;
 	case CHECK_EQ:
 		p = "";
 		if (val == mdoc->last->nchild)
-			return(1);
+			return;
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	t = lvl == CHECK_WARN ? MANDOCERR_ARGCWARN : MANDOCERR_ARGCOUNT;
 	mandoc_vmsg(t, mdoc->parse, mdoc->last->line,
 	    mdoc->last->pos, "want %s%d children (have %d)",
 	    p, val, mdoc->last->nchild);
-	return(1);
 }
 
-static int
-berr_ge1(POST_ARGS)
-{
-
-	return(check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0));
-}
-
-static int
+static void
 bwarn_ge1(POST_ARGS)
 {
-	return(check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0));
+	check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
 }
 
-static int
-ewarn_eq0(POST_ARGS)
-{
-	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0));
-}
-
-static int
+static void
 ewarn_eq1(POST_ARGS)
 {
-	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1));
+	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
 }
 
-static int
+static void
 ewarn_ge1(POST_ARGS)
 {
-	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0));
+	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
 }
 
-static int
-ewarn_le1(POST_ARGS)
-{
-	return(check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2));
-}
-
-static int
+static void
 hwarn_eq0(POST_ARGS)
 {
-	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0));
+	check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
 }
 
-static int
-hwarn_eq1(POST_ARGS)
-{
-	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1));
-}
-
-static int
-hwarn_ge1(POST_ARGS)
-{
-	return(check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0));
-}
-
 static void
 check_args(struct mdoc *mdoc, struct mdoc_node *n)
 {
 	int		 i;
 
 	if (NULL == n->args)
 		return;
 
 	assert(n->args->argc);
 	for (i = 0; i < (int)n->args->argc; i++)
 		check_argv(mdoc, n, &n->args->argv[i]);
 }
 
 static void
 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(mdoc, v->line, v->pos, v->value[i]);
 }
 
 static void
 check_text(struct mdoc *mdoc, int ln, int pos, char *p)
 {
 	char		*cp;
 
 	if (MDOC_LITERAL & mdoc->flags)
 		return;
 
 	for (cp = p; NULL != (p = strchr(p, '\t')); p++)
 		mandoc_msg(MANDOCERR_FI_TAB, mdoc->parse,
 		    ln, pos + (int)(p - cp), NULL);
 }
 
-static int
+static void
 pre_display(PRE_ARGS)
 {
 	struct mdoc_node *node;
 
 	if (MDOC_BLOCK != n->type)
-		return(1);
+		return;
 
 	for (node = mdoc->last->parent; node; node = node->parent)
 		if (MDOC_BLOCK == node->type)
 			if (MDOC_Bd == node->tok)
 				break;
 
 	if (node)
 		mandoc_vmsg(MANDOCERR_BD_NEST,
 		    mdoc->parse, n->line, n->pos,
 		    "%s in Bd", mdoc_macronames[n->tok]);
-
-	return(1);
 }
 
-static int
+static void
 pre_bl(PRE_ARGS)
 {
 	struct mdoc_node *np;
 	struct mdoc_argv *argv, *wa;
 	int		  i;
 	enum mdocargt	  mdoclt;
 	enum mdoc_list	  lt;
 
 	if (MDOC_BLOCK != n->type) {
 		if (ENDBODY_NOT != n->end) {
 			assert(n->pending);
 			np = n->pending->parent;
 		} else
 			np = n->parent;
 
 		assert(np);
 		assert(MDOC_BLOCK == np->type);
 		assert(MDOC_Bl == np->tok);
-		return(1);
+		return;
 	}
 
 	/*
 	 * First figure out which kind of list to use: bind ourselves to
 	 * the first mentioned list type and warn about any remaining
 	 * ones.  If we find no list type, we default to LIST_item.
 	 */
 
-	wa = n->args->argv;
+	wa = (n->args == NULL) ? NULL : n->args->argv;
 	mdoclt = MDOC_ARG_MAX;
 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
 		argv = n->args->argv + i;
 		lt = LIST__NONE;
 		switch (argv->arg) {
 		/* Set list types. */
 		case MDOC_Bullet:
 			lt = LIST_bullet;
 			break;
 		case MDOC_Dash:
 			lt = LIST_dash;
 			break;
 		case MDOC_Enum:
 			lt = LIST_enum;
 			break;
 		case MDOC_Hyphen:
 			lt = LIST_hyphen;
 			break;
 		case MDOC_Item:
 			lt = LIST_item;
 			break;
 		case MDOC_Tag:
 			lt = LIST_tag;
 			break;
 		case MDOC_Diag:
 			lt = LIST_diag;
 			break;
 		case MDOC_Hang:
 			lt = LIST_hang;
 			break;
 		case MDOC_Ohang:
 			lt = LIST_ohang;
 			break;
 		case MDOC_Inset:
 			lt = LIST_inset;
 			break;
 		case MDOC_Column:
 			lt = LIST_column;
 			break;
 		/* Set list arguments. */
 		case MDOC_Compact:
 			if (n->norm->Bl.comp)
 				mandoc_msg(MANDOCERR_ARG_REP,
 				    mdoc->parse, argv->line,
 				    argv->pos, "Bl -compact");
 			n->norm->Bl.comp = 1;
 			break;
 		case MDOC_Width:
 			wa = argv;
 			if (0 == argv->sz) {
 				mandoc_msg(MANDOCERR_ARG_EMPTY,
 				    mdoc->parse, argv->line,
 				    argv->pos, "Bl -width");
 				n->norm->Bl.width = "0n";
 				break;
 			}
 			if (NULL != n->norm->Bl.width)
 				mandoc_vmsg(MANDOCERR_ARG_REP,
 				    mdoc->parse, argv->line,
 				    argv->pos, "Bl -width %s",
 				    argv->value[0]);
+			rewrite_macro2len(argv->value);
 			n->norm->Bl.width = argv->value[0];
 			break;
 		case MDOC_Offset:
 			if (0 == argv->sz) {
 				mandoc_msg(MANDOCERR_ARG_EMPTY,
 				    mdoc->parse, argv->line,
 				    argv->pos, "Bl -offset");
 				break;
 			}
 			if (NULL != n->norm->Bl.offs)
 				mandoc_vmsg(MANDOCERR_ARG_REP,
 				    mdoc->parse, argv->line,
 				    argv->pos, "Bl -offset %s",
 				    argv->value[0]);
+			rewrite_macro2len(argv->value);
 			n->norm->Bl.offs = argv->value[0];
 			break;
 		default:
 			continue;
 		}
 		if (LIST__NONE == lt)
 			continue;
 		mdoclt = argv->arg;
 
 		/* Check: multiple list types. */
 
 		if (LIST__NONE != n->norm->Bl.type) {
 			mandoc_vmsg(MANDOCERR_BL_REP,
 			    mdoc->parse, n->line, n->pos,
 			    "Bl -%s", mdoc_argnames[argv->arg]);
 			continue;
 		}
 
 		/* The list type should come first. */
 
 		if (n->norm->Bl.width ||
 		    n->norm->Bl.offs ||
 		    n->norm->Bl.comp)
 			mandoc_vmsg(MANDOCERR_BL_LATETYPE,
 			    mdoc->parse, n->line, n->pos, "Bl -%s",
 			    mdoc_argnames[n->args->argv[0].arg]);
 
 		n->norm->Bl.type = lt;
 		if (LIST_column == lt) {
 			n->norm->Bl.ncols = argv->sz;
 			n->norm->Bl.cols = (void *)argv->value;
 		}
 	}
 
 	/* Allow lists to default to LIST_item. */
 
 	if (LIST__NONE == n->norm->Bl.type) {
 		mandoc_msg(MANDOCERR_BL_NOTYPE, mdoc->parse,
 		    n->line, n->pos, "Bl");
 		n->norm->Bl.type = LIST_item;
 	}
 
 	/*
 	 * 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.  Yet others have a default and need
 	 * no warning.
 	 */
 
 	switch (n->norm->Bl.type) {
 	case LIST_tag:
 		if (NULL == n->norm->Bl.width)
 			mandoc_msg(MANDOCERR_BL_NOWIDTH, mdoc->parse,
 			    n->line, n->pos, "Bl -tag");
 		break;
 	case LIST_column:
 		/* FALLTHROUGH */
 	case LIST_diag:
 		/* FALLTHROUGH */
 	case LIST_ohang:
 		/* FALLTHROUGH */
 	case LIST_inset:
 		/* FALLTHROUGH */
 	case LIST_item:
 		if (n->norm->Bl.width)
 			mandoc_vmsg(MANDOCERR_BL_SKIPW, mdoc->parse,
 			    wa->line, wa->pos, "Bl -%s",
 			    mdoc_argnames[mdoclt]);
 		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;
 	}
-
-	return(pre_par(mdoc, n));
+	pre_par(mdoc, n);
 }
 
-static int
+static void
 pre_bd(PRE_ARGS)
 {
 	struct mdoc_node *np;
 	struct mdoc_argv *argv;
 	int		  i;
 	enum mdoc_disp	  dt;
 
 	pre_literal(mdoc, n);
 
 	if (MDOC_BLOCK != n->type) {
 		if (ENDBODY_NOT != n->end) {
 			assert(n->pending);
 			np = n->pending->parent;
 		} else
 			np = n->parent;
 
 		assert(np);
 		assert(MDOC_BLOCK == np->type);
 		assert(MDOC_Bd == np->tok);
-		return(1);
+		return;
 	}
 
 	for (i = 0; n->args && i < (int)n->args->argc; i++) {
 		argv = n->args->argv + i;
 		dt = DISP__NONE;
 
 		switch (argv->arg) {
 		case MDOC_Centred:
 			dt = DISP_centered;
 			break;
 		case MDOC_Ragged:
 			dt = DISP_ragged;
 			break;
 		case MDOC_Unfilled:
 			dt = DISP_unfilled;
 			break;
 		case MDOC_Filled:
 			dt = DISP_filled;
 			break;
 		case MDOC_Literal:
 			dt = DISP_literal;
 			break;
 		case MDOC_File:
 			mandoc_msg(MANDOCERR_BD_FILE, mdoc->parse,
 			    n->line, n->pos, NULL);
-			return(0);
+			break;
 		case MDOC_Offset:
 			if (0 == argv->sz) {
 				mandoc_msg(MANDOCERR_ARG_EMPTY,
 				    mdoc->parse, argv->line,
 				    argv->pos, "Bd -offset");
 				break;
 			}
 			if (NULL != n->norm->Bd.offs)
 				mandoc_vmsg(MANDOCERR_ARG_REP,
 				    mdoc->parse, argv->line,
 				    argv->pos, "Bd -offset %s",
 				    argv->value[0]);
+			rewrite_macro2len(argv->value);
 			n->norm->Bd.offs = argv->value[0];
 			break;
 		case MDOC_Compact:
 			if (n->norm->Bd.comp)
 				mandoc_msg(MANDOCERR_ARG_REP,
 				    mdoc->parse, argv->line,
 				    argv->pos, "Bd -compact");
 			n->norm->Bd.comp = 1;
 			break;
 		default:
 			abort();
 			/* NOTREACHED */
 		}
 		if (DISP__NONE == dt)
 			continue;
 
 		if (DISP__NONE == n->norm->Bd.type)
 			n->norm->Bd.type = dt;
 		else
 			mandoc_vmsg(MANDOCERR_BD_REP,
 			    mdoc->parse, n->line, n->pos,
 			    "Bd -%s", mdoc_argnames[argv->arg]);
 	}
 
 	if (DISP__NONE == n->norm->Bd.type) {
 		mandoc_msg(MANDOCERR_BD_NOTYPE, mdoc->parse,
 		    n->line, n->pos, "Bd");
 		n->norm->Bd.type = DISP_ragged;
 	}
-
-	return(pre_par(mdoc, n));
+	pre_par(mdoc, n);
 }
 
-static int
+static void
 pre_an(PRE_ARGS)
 {
 	struct mdoc_argv *argv;
 	size_t	 i;
 
 	if (n->args == NULL)
-		return(1);
+		return;
 
 	for (i = 1; i < n->args->argc; i++) {
 		argv = n->args->argv + i;
 		mandoc_vmsg(MANDOCERR_AN_REP,
 		    mdoc->parse, argv->line, argv->pos,
 		    "An -%s", mdoc_argnames[argv->arg]);
 	}
 
 	argv = n->args->argv;
 	if (argv->arg == MDOC_Split)
 		n->norm->An.auth = AUTH_split;
 	else if (argv->arg == MDOC_Nosplit)
 		n->norm->An.auth = AUTH_nosplit;
 	else
 		abort();
-
-	return(1);
 }
 
-static int
+static void
 pre_std(PRE_ARGS)
 {
 
 	if (n->args && 1 == n->args->argc)
 		if (MDOC_Std == n->args->argv[0].arg)
-			return(1);
+			return;
 
 	mandoc_msg(MANDOCERR_ARG_STD, mdoc->parse,
 	    n->line, n->pos, mdoc_macronames[n->tok]);
-	return(1);
 }
 
-static int
+static void
 pre_obsolete(PRE_ARGS)
 {
 
 	if (MDOC_ELEM == n->type || MDOC_BLOCK == n->type)
 		mandoc_msg(MANDOCERR_MACRO_OBS, mdoc->parse,
 		    n->line, n->pos, mdoc_macronames[n->tok]);
-	return(1);
 }
 
-static int
+static void
 pre_dt(PRE_ARGS)
 {
 
 	if (mdoc->meta.title != NULL)
 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
 		    n->line, n->pos, "Dt");
 	else if (mdoc->meta.os != NULL)
 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
 		    n->line, n->pos, "Dt after Os");
-	return(1);
 }
 
-static int
+static void
 pre_os(PRE_ARGS)
 {
 
 	if (mdoc->meta.os != NULL)
 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
 		    n->line, n->pos, "Os");
 	else if (mdoc->flags & MDOC_PBODY)
 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
 		    n->line, n->pos, "Os");
-	return(1);
 }
 
-static int
+static void
 pre_dd(PRE_ARGS)
 {
 
 	if (mdoc->meta.date != NULL)
 		mandoc_msg(MANDOCERR_PROLOG_REP, mdoc->parse,
 		    n->line, n->pos, "Dd");
 	else if (mdoc->flags & MDOC_PBODY)
 		mandoc_msg(MANDOCERR_PROLOG_LATE, mdoc->parse,
 		    n->line, n->pos, "Dd");
 	else if (mdoc->meta.title != NULL)
 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
 		    n->line, n->pos, "Dd after Dt");
 	else if (mdoc->meta.os != NULL)
 		mandoc_msg(MANDOCERR_PROLOG_ORDER, mdoc->parse,
 		    n->line, n->pos, "Dd after Os");
-	return(1);
 }
 
-static int
+static void
 post_bf(POST_ARGS)
 {
 	struct mdoc_node *np, *nch;
 	enum mdocargt	  arg;
 
 	/*
 	 * Unlike other data pointers, these are "housed" by the HEAD
 	 * element, which contains the goods.
 	 */
 
 	if (MDOC_HEAD != mdoc->last->type) {
 		if (ENDBODY_NOT != mdoc->last->end) {
 			assert(mdoc->last->pending);
 			np = mdoc->last->pending->parent->head;
 		} else if (MDOC_BLOCK != mdoc->last->type) {
 			np = mdoc->last->parent->head;
 		} else
 			np = mdoc->last->head;
 
 		assert(np);
 		assert(MDOC_HEAD == np->type);
 		assert(MDOC_Bf == np->tok);
-		return(1);
+		return;
 	}
 
 	np = mdoc->last;
 	assert(MDOC_BLOCK == np->parent->type);
 	assert(MDOC_Bf == np->parent->tok);
 
 	/* Check the number of arguments. */
 
 	nch = np->child;
 	if (NULL == np->parent->args) {
 		if (NULL == nch) {
 			mandoc_msg(MANDOCERR_BF_NOFONT, mdoc->parse,
 			    np->line, np->pos, "Bf");
-			return(1);
+			return;
 		}
 		nch = nch->next;
 	}
 	if (NULL != nch)
 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, mdoc->parse,
 		    nch->line, nch->pos, "Bf ... %s", nch->string);
 
 	/* Extract argument into data. */
 
 	if (np->parent->args) {
 		arg = np->parent->args->argv[0].arg;
 		if (MDOC_Emphasis == arg)
 			np->norm->Bf.font = FONT_Em;
 		else if (MDOC_Literal == arg)
 			np->norm->Bf.font = FONT_Li;
 		else if (MDOC_Symbolic == arg)
 			np->norm->Bf.font = FONT_Sy;
 		else
 			abort();
-		return(1);
+		return;
 	}
 
 	/* Extract parameter into data. */
 
 	if (0 == strcmp(np->child->string, "Em"))
 		np->norm->Bf.font = FONT_Em;
 	else if (0 == strcmp(np->child->string, "Li"))
 		np->norm->Bf.font = FONT_Li;
 	else if (0 == strcmp(np->child->string, "Sy"))
 		np->norm->Bf.font = FONT_Sy;
 	else
 		mandoc_vmsg(MANDOCERR_BF_BADFONT, mdoc->parse,
 		    np->child->line, np->child->pos,
 		    "Bf %s", np->child->string);
-
-	return(1);
 }
 
-static int
+static void
 post_lb(POST_ARGS)
 {
 	struct mdoc_node	*n;
 	const char		*stdlibname;
 	char			*libname;
 
 	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 1);
-
 	n = mdoc->last->child;
-
-	assert(n);
 	assert(MDOC_TEXT == n->type);
 
 	if (NULL == (stdlibname = mdoc_a2lib(n->string)))
 		mandoc_asprintf(&libname,
 		    "library \\(lq%s\\(rq", n->string);
 	else
 		libname = mandoc_strdup(stdlibname);
 
 	free(n->string);
 	n->string = libname;
-	return(1);
 }
 
-static int
+static void
 post_eoln(POST_ARGS)
 {
 	const struct mdoc_node *n;
 
 	n = mdoc->last;
 	if (n->child)
 		mandoc_vmsg(MANDOCERR_ARG_SKIP,
 		    mdoc->parse, n->line, n->pos,
 		    "%s %s", mdoc_macronames[n->tok],
 		    n->child->string);
-	return(1);
 }
 
-static int
+static void
+post_fname(POST_ARGS)
+{
+	const struct mdoc_node	*n;
+	const char		*cp;
+	size_t			 pos;
+
+	n = mdoc->last->child;
+	pos = strcspn(n->string, "()");
+	cp = n->string + pos;
+	if ( ! (cp[0] == '\0' || (cp[0] == '(' && cp[1] == '*')))
+		mandoc_msg(MANDOCERR_FN_PAREN, mdoc->parse,
+		    n->line, n->pos + pos, n->string);
+}
+
+static void
+post_fn(POST_ARGS)
+{
+
+	post_fname(mdoc);
+	post_fa(mdoc);
+}
+
+static void
 post_fo(POST_ARGS)
 {
 
-	hwarn_eq1(mdoc);
+	check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 1);
 	bwarn_ge1(mdoc);
-	return(1);
+	if (mdoc->last->type == MDOC_HEAD && mdoc->last->nchild)
+		post_fname(mdoc);
 }
 
-static int
+static void
+post_fa(POST_ARGS)
+{
+	const struct mdoc_node *n;
+	const char *cp;
+
+	for (n = mdoc->last->child; n != NULL; n = n->next) {
+		for (cp = n->string; *cp != '\0'; cp++) {
+			/* Ignore callbacks and alterations. */
+			if (*cp == '(' || *cp == '{')
+				break;
+			if (*cp != ',')
+				continue;
+			mandoc_msg(MANDOCERR_FA_COMMA, mdoc->parse,
+			    n->line, n->pos + (cp - n->string),
+			    n->string);
+			break;
+		}
+	}
+}
+
+static void
 post_vt(POST_ARGS)
 {
 	const struct mdoc_node *n;
 
 	/*
 	 * The Vt macro comes in both ELEM and BLOCK form, both of which
 	 * have different syntaxes (yet more context-sensitive
 	 * behaviour).  ELEM types must have a child, which is already
 	 * guaranteed by the in_line parsing routine; BLOCK types,
 	 * specifically the BODY, should only have TEXT children.
 	 */
 
 	if (MDOC_BODY != mdoc->last->type)
-		return(1);
+		return;
 
 	for (n = mdoc->last->child; n; n = n->next)
 		if (MDOC_TEXT != n->type)
 			mandoc_msg(MANDOCERR_VT_CHILD, mdoc->parse,
 			    n->line, n->pos, mdoc_macronames[n->tok]);
-
-	return(1);
 }
 
-static int
+static void
 post_nm(POST_ARGS)
 {
 
 	if (NULL != mdoc->meta.name)
-		return(1);
+		return;
 
 	mdoc_deroff(&mdoc->meta.name, mdoc->last);
 
 	if (NULL == mdoc->meta.name)
 		mandoc_msg(MANDOCERR_NM_NONAME, mdoc->parse,
 		    mdoc->last->line, mdoc->last->pos, "Nm");
-	return(1);
 }
 
-static int
+static void
 post_nd(POST_ARGS)
 {
 
-	berr_ge1(mdoc);
-	return(post_hyph(mdoc));
+	check_count(mdoc, MDOC_BODY, CHECK_ERROR, CHECK_GT, 0);
+	post_hyph(mdoc);
 }
 
-static int
+static void
 post_d1(POST_ARGS)
 {
 
 	bwarn_ge1(mdoc);
-	return(post_hyph(mdoc));
+	post_hyph(mdoc);
 }
 
-static int
+static void
 post_literal(POST_ARGS)
 {
 
 	if (mdoc->last->tok == MDOC_Bd)
 		hwarn_eq0(mdoc);
 	bwarn_ge1(mdoc);
 
 	/*
 	 * The `Dl' (note "el" not "one") and `Bd' macros unset the
 	 * MDOC_LITERAL flag as they leave.  Note that `Bd' only sets
 	 * this in literal mode, but it doesn't hurt to just switch it
 	 * off in general since displays can't be nested.
 	 */
 
 	if (MDOC_BODY == mdoc->last->type)
 		mdoc->flags &= ~MDOC_LITERAL;
-
-	return(1);
 }
 
-static int
+static void
 post_defaults(POST_ARGS)
 {
 	struct mdoc_node *nn;
 
 	/*
 	 * The `Ar' defaults to "file ..." if no value is provided as an
 	 * argument; the `Mt' and `Pa' macros use "~"; the `Li' just
 	 * gets an empty string.
 	 */
 
 	if (mdoc->last->child)
-		return(1);
+		return;
 
 	nn = mdoc->last;
 	mdoc->next = MDOC_NEXT_CHILD;
 
 	switch (nn->tok) {
 	case MDOC_Ar:
-		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "file"))
-			return(0);
-		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "..."))
-			return(0);
+		mdoc_word_alloc(mdoc, nn->line, nn->pos, "file");
+		mdoc_word_alloc(mdoc, nn->line, nn->pos, "...");
 		break;
-	case MDOC_Li:
-		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, ""))
-			return(0);
-		break;
 	case MDOC_Pa:
 		/* FALLTHROUGH */
 	case MDOC_Mt:
-		if ( ! mdoc_word_alloc(mdoc, nn->line, nn->pos, "~"))
-			return(0);
+		mdoc_word_alloc(mdoc, nn->line, nn->pos, "~");
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
-
 	mdoc->last = nn;
-	return(1);
 }
 
-static int
+static void
 post_at(POST_ARGS)
 {
 	struct mdoc_node	*n;
 	const char		*std_att;
 	char			*att;
 
 	n = mdoc->last;
 	if (n->child == NULL) {
 		mdoc->next = MDOC_NEXT_CHILD;
-		if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX"))
-			return(0);
+		mdoc_word_alloc(mdoc, n->line, n->pos, "AT&T UNIX");
 		mdoc->last = n;
-		return(1);
+		return;
 	}
 
 	/*
 	 * If we have a child, look it up in the standard keys.  If a
 	 * key exist, use that instead of the child; if it doesn't,
 	 * prefix "AT&T UNIX " to the existing data.
 	 */
 
 	n = n->child;
 	assert(MDOC_TEXT == n->type);
 	if (NULL == (std_att = mdoc_a2att(n->string))) {
 		mandoc_vmsg(MANDOCERR_AT_BAD, mdoc->parse,
 		    n->line, n->pos, "At %s", n->string);
 		mandoc_asprintf(&att, "AT&T UNIX %s", n->string);
 	} else
 		att = mandoc_strdup(std_att);
 
 	free(n->string);
 	n->string = att;
-	return(1);
 }
 
-static int
+static void
 post_an(POST_ARGS)
 {
 	struct mdoc_node *np;
 
 	np = mdoc->last;
 	if (AUTH__NONE == np->norm->An.auth) {
 		if (0 == np->child)
 			check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_GT, 0);
 	} else if (np->child)
 		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
-
-	return(1);
 }
 
-static int
+static void
 post_en(POST_ARGS)
 {
 
 	if (MDOC_BLOCK == mdoc->last->type)
 		mdoc->last->norm->Es = mdoc->last_es;
-	return(1);
 }
 
-static int
+static void
 post_es(POST_ARGS)
 {
 
 	mdoc->last_es = mdoc->last;
-	return(1);
 }
 
-static int
+static void
 post_it(POST_ARGS)
 {
 	int		  i, cols;
 	enum mdoc_list	  lt;
 	struct mdoc_node *nbl, *nit, *nch;
 
 	nit = mdoc->last;
 	if (MDOC_BLOCK != nit->type)
-		return(1);
+		return;
 
 	nbl = nit->parent->parent;
 	lt = nbl->norm->Bl.type;
 
 	switch (lt) {
 	case LIST_tag:
 		/* FALLTHROUGH */
 	case LIST_hang:
 		/* FALLTHROUGH */
 	case LIST_ohang:
 		/* FALLTHROUGH */
 	case LIST_inset:
 		/* FALLTHROUGH */
 	case LIST_diag:
 		if (NULL == nit->head->child)
 			mandoc_vmsg(MANDOCERR_IT_NOHEAD,
 			    mdoc->parse, nit->line, nit->pos,
 			    "Bl -%s It",
 			    mdoc_argnames[nbl->args->argv[0].arg]);
 		break;
 	case LIST_bullet:
 		/* FALLTHROUGH */
 	case LIST_dash:
 		/* FALLTHROUGH */
 	case LIST_enum:
 		/* FALLTHROUGH */
 	case LIST_hyphen:
 		if (NULL == nit->body->child)
 			mandoc_vmsg(MANDOCERR_IT_NOBODY,
 			    mdoc->parse, nit->line, nit->pos,
 			    "Bl -%s It",
 			    mdoc_argnames[nbl->args->argv[0].arg]);
 		/* FALLTHROUGH */
 	case LIST_item:
 		if (NULL != nit->head->child)
 			mandoc_vmsg(MANDOCERR_ARG_SKIP,
 			    mdoc->parse, nit->line, nit->pos,
 			    "It %s", nit->head->child->string);
 		break;
 	case LIST_column:
 		cols = (int)nbl->norm->Bl.ncols;
 
 		assert(NULL == nit->head->child);
 
 		for (i = 0, nch = nit->child; nch; nch = nch->next)
 			if (MDOC_BODY == nch->type)
 				i++;
 
 		if (i < cols || i > cols + 1)
 			mandoc_vmsg(MANDOCERR_ARGCOUNT,
 			    mdoc->parse, nit->line, nit->pos,
 			    "columns == %d (have %d)", cols, i);
 		break;
 	default:
 		abort();
 	}
-
-	return(1);
 }
 
-static int
+static void
 post_bl_block(POST_ARGS)
 {
 	struct mdoc_node *n, *ni, *nc;
 
 	/*
 	 * These are fairly complicated, so we've broken them into two
 	 * functions.  post_bl_block_tag() is called when a -tag is
 	 * specified, but no -width (it must be guessed).  The second
 	 * when a -width is specified (macro indicators must be
 	 * rewritten into real lengths).
 	 */
 
 	n = mdoc->last;
 
 	if (LIST_tag == n->norm->Bl.type &&
 	    NULL == n->norm->Bl.width) {
-		if ( ! post_bl_block_tag(mdoc))
-			return(0);
+		post_bl_block_tag(mdoc);
 		assert(n->norm->Bl.width);
-	} else if (NULL != n->norm->Bl.width) {
-		if ( ! post_bl_block_width(mdoc))
-			return(0);
-		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) {
 				mandoc_msg(MANDOCERR_PAR_MOVE,
 				    mdoc->parse, nc->line, nc->pos,
 				    mdoc_macronames[nc->tok]);
-				if ( ! mdoc_node_relink(mdoc, nc))
-					return(0);
+				mdoc_node_relink(mdoc, nc);
 			} else if (0 == n->norm->Bl.comp &&
 			    LIST_column != n->norm->Bl.type) {
 				mandoc_vmsg(MANDOCERR_PAR_SKIP,
 				    mdoc->parse, nc->line, nc->pos,
 				    "%s before It",
 				    mdoc_macronames[nc->tok]);
 				mdoc_node_delete(mdoc, nc);
 			} else
 				break;
 			nc = ni->body->last;
 		}
 	}
-	return(1);
 }
 
-static int
-post_bl_block_width(POST_ARGS)
+/*
+ * If the argument of -offset or -width is a macro,
+ * replace it with the associated default width.
+ */
+void
+rewrite_macro2len(char **arg)
 {
 	size_t		  width;
-	int		  i;
 	enum mdoct	  tok;
-	struct mdoc_node *n;
-	char		  buf[24];
 
-	n = mdoc->last;
-
-	/*
-	 * Calculate the real width of a list from the -width string,
-	 * which may contain a macro (with a known default width), a
-	 * literal string, or a scaling width.
-	 *
-	 * If the value to -width is a macro, then we re-write it to be
-	 * the macro's width as set in share/tmac/mdoc/doc-common.
-	 */
-
-	if (0 == strcmp(n->norm->Bl.width, "Ds"))
+	if (*arg == NULL)
+		return;
+	else if ( ! strcmp(*arg, "Ds"))
 		width = 6;
-	else if (MDOC_MAX == (tok = mdoc_hash_find(n->norm->Bl.width)))
-		return(1);
+	else if ((tok = mdoc_hash_find(*arg)) == MDOC_MAX)
+		return;
 	else
 		width = macro2len(tok);
 
-	/* The value already exists: free and reallocate it. */
-
-	assert(n->args);
-
-	for (i = 0; i < (int)n->args->argc; i++)
-		if (MDOC_Width == n->args->argv[i].arg)
-			break;
-
-	assert(i < (int)n->args->argc);
-
-	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)width);
-	free(n->args->argv[i].value[0]);
-	n->args->argv[i].value[0] = mandoc_strdup(buf);
-
-	/* Set our width! */
-	n->norm->Bl.width = n->args->argv[i].value[0];
-	return(1);
+	free(*arg);
+	mandoc_asprintf(arg, "%zun", width);
 }
 
-static int
+static void
 post_bl_block_tag(POST_ARGS)
 {
 	struct mdoc_node *n, *nn;
 	size_t		  sz, ssz;
 	int		  i;
 	char		  buf[24];
 
 	/*
 	 * Calculate the -width for a `Bl -tag' list if it hasn't been
 	 * provided.  Uses the first head macro.  NOTE AGAIN: this is
 	 * ONLY if the -width argument has NOT been provided.  See
-	 * post_bl_block_width() for converting the -width string.
+	 * rewrite_macro2len() for converting the -width string.
 	 */
 
 	sz = 10;
 	n = mdoc->last;
 
 	for (nn = n->body->child; nn; nn = nn->next) {
 		if (MDOC_It != nn->tok)
 			continue;
 
 		assert(MDOC_BLOCK == nn->type);
 		nn = nn->head->child;
 
 		if (nn == NULL)
 			break;
 
 		if (MDOC_TEXT == nn->type) {
 			sz = strlen(nn->string) + 1;
 			break;
 		}
 
 		if (0 != (ssz = macro2len(nn->tok)))
 			sz = ssz;
 
 		break;
 	}
 
 	/* Defaults to ten ens. */
 
 	(void)snprintf(buf, sizeof(buf), "%un", (unsigned int)sz);
 
 	/*
 	 * We have to dynamically add this to the macro's argument list.
 	 * We're guaranteed that a MDOC_Width doesn't already exist.
 	 */
 
 	assert(n->args);
 	i = (int)(n->args->argc)++;
 
 	n->args->argv = mandoc_reallocarray(n->args->argv,
 	    n->args->argc, sizeof(struct mdoc_argv));
 
 	n->args->argv[i].arg = MDOC_Width;
 	n->args->argv[i].line = n->line;
 	n->args->argv[i].pos = n->pos;
 	n->args->argv[i].sz = 1;
 	n->args->argv[i].value = mandoc_malloc(sizeof(char *));
 	n->args->argv[i].value[0] = mandoc_strdup(buf);
 
 	/* Set our width! */
 	n->norm->Bl.width = n->args->argv[i].value[0];
-	return(1);
 }
 
-static int
+static void
 post_bl_head(POST_ARGS)
 {
 	struct mdoc_node *np, *nn, *nnp;
 	struct mdoc_argv *argv;
 	int		  i, j;
 
-	if (LIST_column != mdoc->last->norm->Bl.type)
+	if (LIST_column != mdoc->last->norm->Bl.type) {
 		/* FIXME: this should be ERROR class... */
-		return(hwarn_eq0(mdoc));
+		hwarn_eq0(mdoc);
+		return;
+	}
 
 	/*
 	 * Append old-style lists, where the column width specifiers
 	 * trail as macro parameters, to the new-style ("normal-form")
 	 * lists where they're argument values following -column.
 	 */
 
 	if (mdoc->last->child == NULL)
-		return(1);
+		return;
 
 	np = mdoc->last->parent;
 	assert(np->args);
 
 	for (j = 0; j < (int)np->args->argc; j++)
 		if (MDOC_Column == np->args->argv[j].arg)
 			break;
 
 	assert(j < (int)np->args->argc);
 
 	/*
 	 * Accommodate for new-style groff column syntax.  Shuffle the
 	 * child nodes, all of which must be TEXT, as arguments for the
 	 * column field.  Then, delete the head children.
 	 */
 
 	argv = np->args->argv + j;
 	i = argv->sz;
 	argv->sz += mdoc->last->nchild;
 	argv->value = mandoc_reallocarray(argv->value,
 	    argv->sz, sizeof(char *));
 
 	mdoc->last->norm->Bl.ncols = argv->sz;
 	mdoc->last->norm->Bl.cols = (void *)argv->value;
 
 	for (nn = mdoc->last->child; nn; i++) {
 		argv->value[i] = nn->string;
 		nn->string = NULL;
 		nnp = nn;
 		nn = nn->next;
 		mdoc_node_delete(NULL, nnp);
 	}
 
 	mdoc->last->nchild = 0;
 	mdoc->last->child = NULL;
-
-	return(1);
 }
 
-static int
+static void
 post_bl(POST_ARGS)
 {
 	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 */
 
 	nbody = mdoc->last;
 	switch (nbody->type) {
 	case MDOC_BLOCK:
-		return(post_bl_block(mdoc));
+		post_bl_block(mdoc);
+		return;
 	case MDOC_HEAD:
-		return(post_bl_head(mdoc));
+		post_bl_head(mdoc);
+		return;
 	case MDOC_BODY:
 		break;
 	default:
-		return(1);
+		return;
 	}
 
 	bwarn_ge1(mdoc);
 
 	nchild = nbody->child;
 	while (NULL != nchild) {
 		if (MDOC_It == nchild->tok || MDOC_Sm == nchild->tok) {
 			nchild = nchild->next;
 			continue;
 		}
 
 		mandoc_msg(MANDOCERR_BL_MOVE, mdoc->parse,
 		    nchild->line, nchild->pos,
 		    mdoc_macronames[nchild->tok]);
 
 		/*
 		 * 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);
 }
 
-static int
+static void
 post_bk(POST_ARGS)
 {
 
 	hwarn_eq0(mdoc);
 	bwarn_ge1(mdoc);
-	return(1);
 }
 
-static int
-ebool(struct mdoc *mdoc)
+static void
+post_sm(struct mdoc *mdoc)
 {
 	struct mdoc_node	*nch;
-	enum mdoct		 tok;
 
-	tok = mdoc->last->tok;
 	nch = mdoc->last->child;
 
-	if (NULL == nch) {
-		if (MDOC_Sm == tok)
-			mdoc->flags ^= MDOC_SMOFF;
-		return(1);
+	if (nch == NULL) {
+		mdoc->flags ^= MDOC_SMOFF;
+		return;
 	}
 
-	check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
+	assert(nch->type == MDOC_TEXT);
 
-	assert(MDOC_TEXT == nch->type);
-
-	if (0 == strcmp(nch->string, "on")) {
-		if (MDOC_Sm == tok)
-			mdoc->flags &= ~MDOC_SMOFF;
-		return(1);
+	if ( ! strcmp(nch->string, "on")) {
+		mdoc->flags &= ~MDOC_SMOFF;
+		return;
 	}
-	if (0 == strcmp(nch->string, "off")) {
-		if (MDOC_Sm == tok)
-			mdoc->flags |= MDOC_SMOFF;
-		return(1);
+	if ( ! strcmp(nch->string, "off")) {
+		mdoc->flags |= MDOC_SMOFF;
+		return;
 	}
 
 	mandoc_vmsg(MANDOCERR_SM_BAD,
 	    mdoc->parse, nch->line, nch->pos,
-	    "%s %s", mdoc_macronames[tok], nch->string);
-	return(mdoc_node_relink(mdoc, nch));
+	    "%s %s", mdoc_macronames[mdoc->last->tok], nch->string);
+	mdoc_node_relink(mdoc, nch);
+	return;
 }
 
-static int
+static void
 post_root(POST_ARGS)
 {
 	struct mdoc_node *n;
 
 	/* Add missing prologue data. */
 
 	if (mdoc->meta.date == NULL)
 		mdoc->meta.date = mdoc->quick ?
 		    mandoc_strdup("") :
 		    mandoc_normdate(mdoc->parse, NULL, 0, 0);
 
 	if (mdoc->meta.title == NULL) {
 		mandoc_msg(MANDOCERR_DT_NOTITLE,
 		    mdoc->parse, 0, 0, "EOF");
 		mdoc->meta.title = mandoc_strdup("UNTITLED");
 	}
 
 	if (mdoc->meta.vol == NULL)
 		mdoc->meta.vol = mandoc_strdup("LOCAL");
 
 	if (mdoc->meta.os == NULL) {
 		mandoc_msg(MANDOCERR_OS_MISSING,
 		    mdoc->parse, 0, 0, NULL);
 		mdoc->meta.os = mandoc_strdup("");
 	}
 
-	n = mdoc->first;
-	assert(n);
-
 	/* Check that we begin with a proper `Sh'. */
 
-	if (NULL == n->child)
-		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse,
-		    n->line, n->pos, NULL);
-	else if (MDOC_Sh != n->child->tok)
-		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
-		    n->child->line, n->child->pos,
-		    mdoc_macronames[n->child->tok]);
+	n = mdoc->first->child;
+	while (n != NULL && mdoc_macros[n->tok].flags & MDOC_PROLOGUE)
+		n = n->next;
 
-	return(1);
+	if (n == NULL)
+		mandoc_msg(MANDOCERR_DOC_EMPTY, mdoc->parse, 0, 0, NULL);
+	else if (n->tok != MDOC_Sh)
+		mandoc_msg(MANDOCERR_SEC_BEFORE, mdoc->parse,
+		    n->line, n->pos, mdoc_macronames[n->tok]);
 }
 
-static int
+static void
 post_st(POST_ARGS)
 {
 	struct mdoc_node	 *n, *nch;
 	const char		 *p;
 
 	n = mdoc->last;
 	nch = n->child;
 
 	if (NULL == nch) {
 		mandoc_msg(MANDOCERR_MACRO_EMPTY, mdoc->parse,
 		    n->line, n->pos, mdoc_macronames[n->tok]);
 		mdoc_node_delete(mdoc, n);
-		return(1);
+		return;
 	}
 
 	assert(MDOC_TEXT == nch->type);
 
 	if (NULL == (p = mdoc_a2st(nch->string))) {
 		mandoc_vmsg(MANDOCERR_ST_BAD, mdoc->parse,
 		    nch->line, nch->pos, "St %s", nch->string);
 		mdoc_node_delete(mdoc, n);
 	} else {
 		free(nch->string);
 		nch->string = mandoc_strdup(p);
 	}
-
-	return(1);
 }
 
-static int
+static void
 post_rs(POST_ARGS)
 {
 	struct mdoc_node *nn, *next, *prev;
 	int		  i, j;
 
 	switch (mdoc->last->type) {
 	case MDOC_HEAD:
 		check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_EQ, 0);
-		return(1);
+		return;
 	case MDOC_BODY:
 		if (mdoc->last->child)
 			break;
 		check_count(mdoc, MDOC_BODY, CHECK_WARN, CHECK_GT, 0);
-		return(1);
+		return;
 	default:
-		return(1);
+		return;
 	}
 
 	/*
 	 * The full `Rs' block needs special handling to order the
 	 * sub-elements according to `rsord'.  Pick through each element
 	 * and correctly order it.  This is an insertion sort.
 	 */
 
 	next = NULL;
 	for (nn = mdoc->last->child->next; nn; nn = next) {
 		/* Determine order of `nn'. */
 		for (i = 0; i < RSORD_MAX; i++)
 			if (rsord[i] == nn->tok)
 				break;
 
 		if (i == RSORD_MAX) {
 			mandoc_msg(MANDOCERR_RS_BAD,
 			    mdoc->parse, nn->line, nn->pos,
 			    mdoc_macronames[nn->tok]);
 			i = -1;
 		} else if (MDOC__J == nn->tok || MDOC__B == nn->tok)
 			mdoc->last->norm->Rs.quote_T++;
 
 		/*
 		 * Remove `nn' from the chain.  This somewhat
 		 * repeats mdoc_node_unlink(), but since we're
 		 * just re-ordering, there's no need for the
 		 * full unlink process.
 		 */
 
 		if (NULL != (next = nn->next))
 			next->prev = nn->prev;
 
 		if (NULL != (prev = nn->prev))
 			prev->next = nn->next;
 
 		nn->prev = nn->next = NULL;
 
 		/*
 		 * Scan back until we reach a node that's
 		 * ordered before `nn'.
 		 */
 
 		for ( ; prev ; prev = prev->prev) {
 			/* Determine order of `prev'. */
 			for (j = 0; j < RSORD_MAX; j++)
 				if (rsord[j] == prev->tok)
 					break;
 			if (j == RSORD_MAX)
 				j = -1;
 
 			if (j <= i)
 				break;
 		}
 
 		/*
 		 * Set `nn' back into its correct place in front
 		 * of the `prev' node.
 		 */
 
 		nn->prev = prev;
 
 		if (prev) {
 			if (prev->next)
 				prev->next->prev = nn;
 			nn->next = prev->next;
 			prev->next = nn;
 		} else {
 			mdoc->last->child->prev = nn;
 			nn->next = mdoc->last->child;
 			mdoc->last->child = nn;
 		}
 	}
-
-	return(1);
 }
 
 /*
  * For some arguments of some macros,
  * convert all breakable hyphens into ASCII_HYPH.
  */
-static int
+static void
 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);
+		return;
 	case MDOC_BODY:
 		if (MDOC_D1 == n->tok || MDOC_Nd == n->tok)
 			break;
-		return(1);
+		return;
 	case MDOC_ELEM:
 		break;
 	default:
-		return(1);
+		return;
 	}
 
 	for (nch = n->child; nch; nch = nch->next) {
 		if (MDOC_TEXT != nch->type)
 			continue;
 		cp = nch->string;
 		if ('\0' == *cp)
 			continue;
 		while ('\0' != *(++cp))
 			if ('-' == *cp &&
 			    isalpha((unsigned char)cp[-1]) &&
 			    isalpha((unsigned char)cp[1]))
 				*cp = ASCII_HYPH;
 	}
-	return(1);
 }
 
-static int
+static void
 post_hyphtext(POST_ARGS)
 {
 
 	ewarn_ge1(mdoc);
-	return(post_hyph(mdoc));
+	post_hyph(mdoc);
 }
 
-static int
+static void
 post_ns(POST_ARGS)
 {
 
 	if (MDOC_LINE & mdoc->last->flags)
 		mandoc_msg(MANDOCERR_NS_SKIP, mdoc->parse,
 		    mdoc->last->line, mdoc->last->pos, NULL);
-	return(1);
 }
 
-static int
+static void
 post_sh(POST_ARGS)
 {
 
 	post_ignpar(mdoc);
 
-	if (MDOC_HEAD == mdoc->last->type)
-		return(post_sh_head(mdoc));
-	if (MDOC_BODY == mdoc->last->type)
-		return(post_sh_body(mdoc));
-
-	return(1);
+	switch (mdoc->last->type) {
+	case MDOC_HEAD:
+		post_sh_head(mdoc);
+		break;
+	case MDOC_BODY:
+		switch (mdoc->lastsec)  {
+		case SEC_NAME:
+			post_sh_name(mdoc);
+			break;
+		case SEC_SEE_ALSO:
+			post_sh_see_also(mdoc);
+			break;
+		case SEC_AUTHORS:
+			post_sh_authors(mdoc);
+			break;
+		default:
+			break;
+		}
+		break;
+	default:
+		break;
+	}
 }
 
-static int
-post_sh_body(POST_ARGS)
+static void
+post_sh_name(POST_ARGS)
 {
 	struct mdoc_node *n;
 
-	if (SEC_NAME != mdoc->lastsec)
-		return(1);
-
 	/*
 	 * Warn if the NAME section doesn't contain the `Nm' and `Nd'
 	 * macros (can have multiple `Nm' and one `Nd').  Note that the
 	 * children of the BODY declaration can also be "text".
 	 */
 
 	if (NULL == (n = mdoc->last->child)) {
 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
 		    mdoc->last->line, mdoc->last->pos, "empty");
-		return(1);
+		return;
 	}
 
 	for ( ; n && n->next; n = n->next) {
 		if (MDOC_ELEM == n->type && MDOC_Nm == n->tok)
 			continue;
 		if (MDOC_TEXT == n->type)
 			continue;
 		mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
 		    n->line, n->pos, mdoc_macronames[n->tok]);
 	}
 
 	assert(n);
 	if (MDOC_BLOCK == n->type && MDOC_Nd == n->tok)
-		return(1);
+		return;
 
 	mandoc_msg(MANDOCERR_NAMESEC_BAD, mdoc->parse,
 	    n->line, n->pos, mdoc_macronames[n->tok]);
-	return(1);
 }
 
+static void
+post_sh_see_also(POST_ARGS)
+{
+	const struct mdoc_node	*n;
+	const char 		*name, *sec;
+	const char		*lastname, *lastsec, *lastpunct;
+	int			 cmp;
+
+	n = mdoc->last->child;
+	lastname = lastsec = lastpunct = NULL;
+	while (n != NULL) {
+		if (n->tok != MDOC_Xr || n->nchild < 2)
+			break;
+
+		/* Process one .Xr node. */
+
+		name = n->child->string;
+		sec = n->child->next->string;
+		if (lastsec != NULL) {
+			if (lastpunct[0] != ',' || lastpunct[1] != '\0')
+				mandoc_vmsg(MANDOCERR_XR_PUNCT,
+				    mdoc->parse, n->line, n->pos,
+				    "%s before %s(%s)", lastpunct,
+				    name, sec);
+			cmp = strcmp(lastsec, sec);
+			if (cmp > 0)
+				mandoc_vmsg(MANDOCERR_XR_ORDER,
+				    mdoc->parse, n->line, n->pos,
+				    "%s(%s) after %s(%s)", name,
+				    sec, lastname, lastsec);
+			else if (cmp == 0 &&
+			    strcasecmp(lastname, name) > 0)
+				mandoc_vmsg(MANDOCERR_XR_ORDER,
+				    mdoc->parse, n->line, n->pos,
+				    "%s after %s", name, lastname);
+		}
+		lastname = name;
+		lastsec = sec;
+
+		/* Process the following node. */
+
+		n = n->next;
+		if (n == NULL)
+			break;
+		if (n->tok == MDOC_Xr) {
+			lastpunct = "none";
+			continue;
+		}
+		if (n->type != MDOC_TEXT)
+			break;
+		for (name = n->string; *name != '\0'; name++)
+			if (isalpha((const unsigned char)*name))
+				return;
+		lastpunct = n->string;
+		if (n->next == NULL)
+			mandoc_vmsg(MANDOCERR_XR_PUNCT, mdoc->parse,
+			    n->line, n->pos, "%s after %s(%s)",
+			    lastpunct, lastname, lastsec);
+		n = n->next;
+	}
+}
+
 static int
+child_an(const struct mdoc_node *n)
+{
+
+	for (n = n->child; n != NULL; n = n->next)
+		if ((n->tok == MDOC_An && n->nchild) || child_an(n))
+			return(1);
+	return(0);
+}
+
+static void
+post_sh_authors(POST_ARGS)
+{
+
+	if ( ! child_an(mdoc->last))
+		mandoc_msg(MANDOCERR_AN_MISSING, mdoc->parse,
+		    mdoc->last->line, mdoc->last->pos, NULL);
+}
+
+static void
 post_sh_head(POST_ARGS)
 {
 	struct mdoc_node *n;
 	const char	*goodsec;
 	char		*secname;
 	enum mdoc_sec	 sec;
 
 	/*
 	 * Process a new section.  Sections are either "named" or
 	 * "custom".  Custom sections are user-defined, while named ones
 	 * follow a conventional order and may only appear in certain
 	 * manual sections.
 	 */
 
 	secname = NULL;
 	sec = SEC_CUSTOM;
 	mdoc_deroff(&secname, mdoc->last);
 	sec = NULL == secname ? SEC_CUSTOM : a2sec(secname);
 
 	/* The NAME should be first. */
 
 	if (SEC_NAME != sec && SEC_NONE == mdoc->lastnamed)
 		mandoc_vmsg(MANDOCERR_NAMESEC_FIRST, mdoc->parse,
 		    mdoc->last->line, mdoc->last->pos,
 		    "Sh %s", secname);
 
 	/* The SYNOPSIS gets special attention in other areas. */
 
 	if (SEC_SYNOPSIS == sec) {
 		roff_setreg(mdoc->roff, "nS", 1, '=');
 		mdoc->flags |= MDOC_SYNOPSIS;
 	} else {
 		roff_setreg(mdoc->roff, "nS", 0, '=');
 		mdoc->flags &= ~MDOC_SYNOPSIS;
 	}
 
 	/* Mark our last section. */
 
 	mdoc->lastsec = sec;
 
 	/*
 	 * Set the section attribute for the current HEAD, for its
 	 * parent BLOCK, and for the HEAD children; the latter can
 	 * only be TEXT nodes, so no recursion is needed.
 	 * For other blocks and elements, including .Sh BODY, this is
 	 * done when allocating the node data structures, but for .Sh
 	 * BLOCK and HEAD, the section is still unknown at that time.
 	 */
 
 	mdoc->last->parent->sec = sec;
 	mdoc->last->sec = sec;
 	for (n = mdoc->last->child; n; n = n->next)
 		n->sec = sec;
 
 	/* We don't care about custom sections after this. */
 
 	if (SEC_CUSTOM == sec) {
 		free(secname);
-		return(1);
+		return;
 	}
 
 	/*
 	 * Check whether our non-custom section is being repeated or is
 	 * out of order.
 	 */
 
 	if (sec == mdoc->lastnamed)
 		mandoc_vmsg(MANDOCERR_SEC_REP, mdoc->parse,
 		    mdoc->last->line, mdoc->last->pos,
 		    "Sh %s", secname);
 
 	if (sec < mdoc->lastnamed)
 		mandoc_vmsg(MANDOCERR_SEC_ORDER, mdoc->parse,
 		    mdoc->last->line, mdoc->last->pos,
 		    "Sh %s", secname);
 
 	/* Mark the last named section. */
 
 	mdoc->lastnamed = sec;
 
 	/* Check particular section/manual conventions. */
 
 	if (mdoc->meta.msec == NULL) {
 		free(secname);
-		return(1);
+		return;
 	}
 
 	goodsec = NULL;
 	switch (sec) {
 	case SEC_ERRORS:
 		if (*mdoc->meta.msec == '4')
 			break;
 		goodsec = "2, 3, 4, 9";
 		/* FALLTHROUGH */
 	case SEC_RETURN_VALUES:
 		/* FALLTHROUGH */
 	case SEC_LIBRARY:
 		if (*mdoc->meta.msec == '2')
 			break;
 		if (*mdoc->meta.msec == '3')
 			break;
 		if (NULL == goodsec)
 			goodsec = "2, 3, 9";
 		/* FALLTHROUGH */
 	case SEC_CONTEXT:
 		if (*mdoc->meta.msec == '9')
 			break;
 		if (NULL == goodsec)
 			goodsec = "9";
 		mandoc_vmsg(MANDOCERR_SEC_MSEC, mdoc->parse,
 		    mdoc->last->line, mdoc->last->pos,
 		    "Sh %s for %s only", secname, goodsec);
 		break;
 	default:
 		break;
 	}
-
 	free(secname);
-	return(1);
 }
 
-static int
+static void
 post_ignpar(POST_ARGS)
 {
 	struct mdoc_node *np;
 
-	hwarn_ge1(mdoc);
+	check_count(mdoc, MDOC_HEAD, CHECK_WARN, CHECK_GT, 0);
 	post_hyph(mdoc);
 
 	if (MDOC_BODY != mdoc->last->type)
-		return(1);
+		return;
 
 	if (NULL != (np = mdoc->last->child))
 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
 			mandoc_vmsg(MANDOCERR_PAR_SKIP,
 			    mdoc->parse, np->line, np->pos,
 			    "%s after %s", mdoc_macronames[np->tok],
 			    mdoc_macronames[mdoc->last->tok]);
 			mdoc_node_delete(mdoc, np);
 		}
 
 	if (NULL != (np = mdoc->last->last))
 		if (MDOC_Pp == np->tok || MDOC_Lp == np->tok) {
 			mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
 			    np->line, np->pos, "%s at the end of %s",
 			    mdoc_macronames[np->tok],
 			    mdoc_macronames[mdoc->last->tok]);
 			mdoc_node_delete(mdoc, np);
 		}
-
-	return(1);
 }
 
-static int
+static void
 pre_par(PRE_ARGS)
 {
 
 	if (NULL == mdoc->last)
-		return(1);
+		return;
 	if (MDOC_ELEM != n->type && MDOC_BLOCK != n->type)
-		return(1);
+		return;
 
 	/*
 	 * Don't allow prior `Lp' or `Pp' prior to a paragraph-type
 	 * block:  `Lp', `Pp', or non-compact `Bd' or `Bl'.
 	 */
 
 	if (MDOC_Pp != mdoc->last->tok &&
 	    MDOC_Lp != mdoc->last->tok &&
 	    MDOC_br != mdoc->last->tok)
-		return(1);
+		return;
 	if (MDOC_Bl == n->tok && n->norm->Bl.comp)
-		return(1);
+		return;
 	if (MDOC_Bd == n->tok && n->norm->Bd.comp)
-		return(1);
+		return;
 	if (MDOC_It == n->tok && n->parent->norm->Bl.comp)
-		return(1);
+		return;
 
 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
 	    mdoc->last->line, mdoc->last->pos,
 	    "%s before %s", mdoc_macronames[mdoc->last->tok],
 	    mdoc_macronames[n->tok]);
 	mdoc_node_delete(mdoc, mdoc->last);
-	return(1);
 }
 
-static int
+static void
 post_par(POST_ARGS)
 {
 	struct mdoc_node *np;
 
 	if (mdoc->last->tok == MDOC_sp)
-		ewarn_le1(mdoc);
+		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_LT, 2);
 	else
-		ewarn_eq0(mdoc);
+		check_count(mdoc, MDOC_ELEM, CHECK_WARN, CHECK_EQ, 0);
 
 	if (MDOC_ELEM != mdoc->last->type &&
 	    MDOC_BLOCK != mdoc->last->type)
-		return(1);
+		return;
 
 	if (NULL == (np = mdoc->last->prev)) {
 		np = mdoc->last->parent;
 		if (MDOC_Sh != np->tok && MDOC_Ss != np->tok)
-			return(1);
-	} else {
-		if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
-		    (MDOC_br != mdoc->last->tok ||
-		     (MDOC_sp != np->tok && MDOC_br != np->tok)))
-			return(1);
-	}
+			return;
+	} else if (MDOC_Pp != np->tok && MDOC_Lp != np->tok &&
+	    (MDOC_br != mdoc->last->tok ||
+	     (MDOC_sp != np->tok && MDOC_br != np->tok)))
+		return;
 
 	mandoc_vmsg(MANDOCERR_PAR_SKIP, mdoc->parse,
 	    mdoc->last->line, mdoc->last->pos,
 	    "%s after %s", mdoc_macronames[mdoc->last->tok],
 	    mdoc_macronames[np->tok]);
 	mdoc_node_delete(mdoc, mdoc->last);
-	return(1);
 }
 
-static int
+static void
 pre_literal(PRE_ARGS)
 {
 
 	pre_display(mdoc, n);
 
 	if (MDOC_BODY != n->type)
-		return(1);
+		return;
 
 	/*
 	 * The `Dl' (note "el" not "one") and `Bd -literal' and `Bd
 	 * -unfilled' macros set MDOC_LITERAL on entrance to the body.
 	 */
 
 	switch (n->tok) {
 	case MDOC_Dl:
 		mdoc->flags |= MDOC_LITERAL;
 		break;
 	case MDOC_Bd:
 		if (DISP_literal == n->norm->Bd.type)
 			mdoc->flags |= MDOC_LITERAL;
 		if (DISP_unfilled == n->norm->Bd.type)
 			mdoc->flags |= MDOC_LITERAL;
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
-
-	return(1);
 }
 
-static int
+static void
 post_dd(POST_ARGS)
 {
 	struct mdoc_node *n;
 	char		 *datestr;
 
 	if (mdoc->meta.date)
 		free(mdoc->meta.date);
 
 	n = mdoc->last;
 	if (NULL == n->child || '\0' == n->child->string[0]) {
 		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
 		    mandoc_normdate(mdoc->parse, NULL, n->line, n->pos);
 		goto out;
 	}
 
 	datestr = NULL;
 	mdoc_deroff(&datestr, n);
 	if (mdoc->quick)
 		mdoc->meta.date = datestr;
 	else {
 		mdoc->meta.date = mandoc_normdate(mdoc->parse,
 		    datestr, n->line, n->pos);
 		free(datestr);
 	}
 out:
 	mdoc_node_delete(mdoc, n);
-	return(1);
 }
 
-static int
+static void
 post_dt(POST_ARGS)
 {
 	struct mdoc_node *nn, *n;
 	const char	 *cp;
 	char		 *p;
 
 	n = mdoc->last;
 
 	free(mdoc->meta.title);
 	free(mdoc->meta.msec);
 	free(mdoc->meta.vol);
 	free(mdoc->meta.arch);
 
 	mdoc->meta.title = NULL;
 	mdoc->meta.msec = NULL;
 	mdoc->meta.vol = NULL;
 	mdoc->meta.arch = NULL;
 
 	/* First check that all characters are uppercase. */
 
 	if (NULL != (nn = n->child))
 		for (p = nn->string; *p; p++) {
 			if (toupper((unsigned char)*p) == *p)
 				continue;
 			mandoc_vmsg(MANDOCERR_TITLE_CASE,
 			    mdoc->parse, nn->line,
 			    nn->pos + (p - nn->string),
 			    "Dt %s", nn->string);
 			break;
 		}
 
 	/* No argument: msec and arch remain NULL. */
 
 	if (NULL == (nn = n->child)) {
 		mandoc_msg(MANDOCERR_DT_NOTITLE,
 		    mdoc->parse, n->line, n->pos, "Dt");
 		mdoc->meta.title = mandoc_strdup("UNTITLED");
 		mdoc->meta.vol = mandoc_strdup("LOCAL");
 		goto out;
 	}
 
 	/* One argument: msec and arch remain NULL. */
 
 	mdoc->meta.title = mandoc_strdup(
 	    '\0' == nn->string[0] ? "UNTITLED" : nn->string);
 
 	if (NULL == (nn = nn->next)) {
 		mandoc_vmsg(MANDOCERR_MSEC_MISSING,
 		    mdoc->parse, n->line, n->pos,
 		    "Dt %s", mdoc->meta.title);
 		mdoc->meta.vol = mandoc_strdup("LOCAL");
 		goto out;
 	}
 
 	/* Handles: `.Dt TITLE SEC'
 	 * title = TITLE,
 	 * volume = SEC is msec ? format(msec) : SEC,
 	 * msec = SEC is msec ? atoi(msec) : 0,
 	 * arch = NULL
 	 */
 
 	cp = mandoc_a2msec(nn->string);
 	if (cp) {
 		mdoc->meta.vol = mandoc_strdup(cp);
 		mdoc->meta.msec = mandoc_strdup(nn->string);
 	} else {
 		mandoc_vmsg(MANDOCERR_MSEC_BAD, mdoc->parse,
 		    nn->line, nn->pos, "Dt ... %s", nn->string);
 		mdoc->meta.vol = mandoc_strdup(nn->string);
 		mdoc->meta.msec = mandoc_strdup(nn->string);
 	}
 
-	if (NULL == (nn = nn->next))
-		goto out;
+	/* Handle an optional architecture */
 
-	/* Handles: `.Dt TITLE SEC VOL'
-	 * title = TITLE,
-	 * volume = VOL is vol ? format(VOL) :
-	 *	    VOL is arch ? format(arch) :
-	 *	    VOL
-	 */
-
-	cp = mdoc_a2vol(nn->string);
-	if (cp) {
-		free(mdoc->meta.vol);
-		mdoc->meta.vol = mandoc_strdup(cp);
-	} else {
-		cp = mdoc_a2arch(nn->string);
-		if (NULL == cp) {
-			mandoc_vmsg(MANDOCERR_ARCH_BAD, mdoc->parse,
-			    nn->line, nn->pos, "Dt ... %s", nn->string);
-			free(mdoc->meta.vol);
-			mdoc->meta.vol = mandoc_strdup(nn->string);
-		} else
-			mdoc->meta.arch = mandoc_strdup(cp);
+	if ((nn = nn->next) != NULL) {
+		for (p = nn->string; *p; p++)
+			*p = tolower((unsigned char)*p);
+		mdoc->meta.arch = mandoc_strdup(nn->string);
 	}
 
 	/* Ignore any subsequent parameters... */
 	/* FIXME: warn about subsequent parameters. */
 out:
 	mdoc_node_delete(mdoc, n);
-	return(1);
 }
 
-static int
+static void
 post_bx(POST_ARGS)
 {
 	struct mdoc_node	*n;
 
 	/*
 	 * Make `Bx's second argument always start with an uppercase
 	 * letter.  Groff checks if it's an "accepted" term, but we just
 	 * uppercase blindly.
 	 */
 
 	n = mdoc->last->child;
 	if (n && NULL != (n = n->next))
 		*n->string = (char)toupper((unsigned char)*n->string);
-
-	return(1);
 }
 
-static int
+static void
 post_os(POST_ARGS)
 {
 #ifndef OSNAME
 	struct utsname	  utsname;
 	static char	 *defbuf;
 #endif
 	struct mdoc_node *n;
 
 	n = mdoc->last;
 
 	/*
 	 * 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)
 	 */
 
 	free(mdoc->meta.os);
 	mdoc->meta.os = NULL;
 	mdoc_deroff(&mdoc->meta.os, n);
 	if (mdoc->meta.os)
 		goto out;
 
 	if (mdoc->defos) {
 		mdoc->meta.os = mandoc_strdup(mdoc->defos);
 		goto out;
 	}
 
 #ifdef OSNAME
 	mdoc->meta.os = mandoc_strdup(OSNAME);
 #else /*!OSNAME */
 	if (NULL == defbuf) {
 		if (-1 == uname(&utsname)) {
 			mandoc_msg(MANDOCERR_OS_UNAME, mdoc->parse,
 			    n->line, n->pos, "Os");
 			defbuf = mandoc_strdup("UNKNOWN");
 		} else
 			mandoc_asprintf(&defbuf, "%s %s",
 			    utsname.sysname, utsname.release);
 	}
 	mdoc->meta.os = mandoc_strdup(defbuf);
 #endif /*!OSNAME*/
 
 out:
 	mdoc_node_delete(mdoc, n);
-	return(1);
 }
 
 /*
  * If no argument is provided,
  * fill in the name of the current manual page.
  */
-static int
+static void
 post_ex(POST_ARGS)
 {
 	struct mdoc_node *n;
 
 	n = mdoc->last;
 
 	if (n->child)
-		return(1);
+		return;
 
 	if (mdoc->meta.name == NULL) {
 		mandoc_msg(MANDOCERR_EX_NONAME, mdoc->parse,
 		    n->line, n->pos, "Ex");
-		return(1);
+		return;
 	}
 
 	mdoc->next = MDOC_NEXT_CHILD;
-
-	if ( ! mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name))
-		return(0);
-
+	mdoc_word_alloc(mdoc, n->line, n->pos, mdoc->meta.name);
 	mdoc->last = n;
-	return(1);
 }
 
 static enum mdoc_sec
 a2sec(const char *p)
 {
 	int		 i;
 
 	for (i = 0; i < (int)SEC__MAX; i++)
 		if (secnames[i] && 0 == strcmp(p, secnames[i]))
 			return((enum mdoc_sec)i);
 
 	return(SEC_CUSTOM);
 }
 
 static size_t
 macro2len(enum mdoct macro)
 {
 
 	switch (macro) {
 	case MDOC_Ad:
 		return(12);
 	case MDOC_Ao:
 		return(12);
 	case MDOC_An:
 		return(12);
 	case MDOC_Aq:
 		return(12);
 	case MDOC_Ar:
 		return(12);
 	case MDOC_Bo:
 		return(12);
 	case MDOC_Bq:
 		return(12);
 	case MDOC_Cd:
 		return(12);
 	case MDOC_Cm:
 		return(10);
 	case MDOC_Do:
 		return(10);
 	case MDOC_Dq:
 		return(12);
 	case MDOC_Dv:
 		return(12);
 	case MDOC_Eo:
 		return(12);
 	case MDOC_Em:
 		return(10);
 	case MDOC_Er:
 		return(17);
 	case MDOC_Ev:
 		return(15);
 	case MDOC_Fa:
 		return(12);
 	case MDOC_Fl:
 		return(10);
 	case MDOC_Fo:
 		return(16);
 	case MDOC_Fn:
 		return(16);
 	case MDOC_Ic:
 		return(10);
 	case MDOC_Li:
 		return(16);
 	case MDOC_Ms:
 		return(6);
 	case MDOC_Nm:
 		return(10);
 	case MDOC_No:
 		return(12);
 	case MDOC_Oo:
 		return(10);
 	case MDOC_Op:
 		return(14);
 	case MDOC_Pa:
 		return(32);
 	case MDOC_Pf:
 		return(12);
 	case MDOC_Po:
 		return(12);
 	case MDOC_Pq:
 		return(12);
 	case MDOC_Ql:
 		return(16);
 	case MDOC_Qo:
 		return(12);
 	case MDOC_So:
 		return(12);
 	case MDOC_Sq:
 		return(12);
 	case MDOC_Sy:
 		return(6);
 	case MDOC_Sx:
 		return(16);
 	case MDOC_Tn:
 		return(10);
 	case MDOC_Va:
 		return(12);
 	case MDOC_Vt:
 		return(12);
 	case MDOC_Xr:
 		return(10);
 	default:
 		break;
 	};
 	return(0);
 }
Index: vendor/mdocml/dist/msec.c
===================================================================
--- vendor/mdocml/dist/msec.c	(revision 275396)
+++ vendor/mdocml/dist/msec.c	(revision 275397)
@@ -1,36 +1,36 @@
-/*	$Id: msec.c,v 1.11 2014/03/23 11:25:26 schwarze Exp $ */
+/*	$Id: msec.c,v 1.12 2014/08/10 23:54:41 schwarze Exp $ */
 /*
  * Copyright (c) 2009 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
+
+#include 
 
 #include 
 
 #include "mandoc.h"
 #include "libmandoc.h"
 
 #define LINE(x, y) \
 	if (0 == strcmp(p, x)) return(y);
 
 const char *
 mandoc_a2msec(const char *p)
 {
 
 #include "msec.in"
 
 	return(NULL);
 }
Index: vendor/mdocml/dist/msec.in
===================================================================
--- vendor/mdocml/dist/msec.in	(revision 275396)
+++ vendor/mdocml/dist/msec.in	(revision 275397)
@@ -1,40 +1,40 @@
-/*	$Id: msec.in,v 1.6 2010/06/19 20:46:28 kristaps Exp $ */
+/*	$Id: msec.in,v 1.7 2014/08/26 11:21:40 schwarze Exp $ */
 /*
  * Copyright (c) 2009 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 /*
  * These are all possible manual-section macros and what they correspond
  * to when rendered as the volume title.
  *
  * Be sure to escape strings.
  */
 
 LINE("1",		"General Commands Manual")
 LINE("2",		"System Calls Manual")
 LINE("3",		"Library Functions Manual")
-LINE("3p",		"Perl Library Functions Manual")
-LINE("4",		"Kernel Interfaces Manual")
+LINE("3p",		"Perl Library Manual")
+LINE("4",		"Device Drivers Manual")
 LINE("5",		"File Formats Manual")
 LINE("6",		"Games Manual")
 LINE("7",		"Miscellaneous Information Manual")
 LINE("8",		"System Manager\'s Manual")
 LINE("9",		"Kernel Developer\'s Manual")
 LINE("X11",		"X11 Developer\'s Manual")
 LINE("X11R6",		"X11 Developer\'s Manual")
 LINE("unass",		"Unassociated")
 LINE("local",		"Local")
 LINE("draft",		"Draft")
 LINE("paper",		"Paper")
Index: vendor/mdocml/dist/out.c
===================================================================
--- vendor/mdocml/dist/out.c	(revision 275396)
+++ vendor/mdocml/dist/out.c	(revision 275397)
@@ -1,283 +1,345 @@
-/*	$Id: out.c,v 1.49 2014/08/01 19:25:52 schwarze Exp $ */
+/*	$Id: out.c,v 1.53 2014/10/14 18:18:05 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
- * Copyright (c) 2011 Ingo Schwarze 
+ * Copyright (c) 2011, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc_aux.h"
 #include "mandoc.h"
 #include "out.h"
 
 static	void	tblcalc_data(struct rofftbl *, struct roffcol *,
 			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_opts *, const struct tbl_dat *);
 
 
 /*
  * Convert a `scaling unit' to a consistent form, or fail.  Scaling
  * units are documented in groff.7, mdoc.7, man.7.
  */
 int
 a2roffsu(const char *src, struct roffsu *dst, enum roffscale def)
 {
 	char		 buf[BUFSIZ], hasd;
 	int		 i;
 	enum roffscale	 unit;
 
 	if ('\0' == *src)
 		return(0);
 
 	i = hasd = 0;
 
 	switch (*src) {
 	case '+':
 		src++;
 		break;
 	case '-':
 		buf[i++] = *src++;
 		break;
 	default:
 		break;
 	}
 
 	if ('\0' == *src)
 		return(0);
 
 	while (i < BUFSIZ) {
 		if ( ! isdigit((unsigned char)*src)) {
 			if ('.' != *src)
 				break;
 			else if (hasd)
 				break;
 			else
 				hasd = 1;
 		}
 		buf[i++] = *src++;
 	}
 
 	if (BUFSIZ == i || (*src && *(src + 1)))
 		return(0);
 
 	buf[i] = '\0';
 
 	switch (*src) {
 	case 'c':
 		unit = SCALE_CM;
 		break;
 	case 'i':
 		unit = SCALE_IN;
 		break;
 	case 'P':
 		unit = SCALE_PC;
 		break;
 	case 'p':
 		unit = SCALE_PT;
 		break;
 	case 'f':
 		unit = SCALE_FS;
 		break;
 	case 'v':
 		unit = SCALE_VS;
 		break;
 	case 'm':
 		unit = SCALE_EM;
 		break;
 	case '\0':
 		if (SCALE_MAX == def)
 			return(0);
-		unit = SCALE_BU;
+		unit = SCALE_EN;
 		break;
 	case 'u':
 		unit = SCALE_BU;
 		break;
 	case 'M':
 		unit = SCALE_MM;
 		break;
 	case 'n':
 		unit = SCALE_EN;
 		break;
 	default:
 		return(0);
 	}
 
 	/* FIXME: do this in the caller. */
 	if ((dst->scale = atof(buf)) < 0.0)
 		dst->scale = 0.0;
 	dst->unit = unit;
 	return(1);
 }
 
 /*
  * Calculate the abstract widths and decimal positions of columns in a
  * table.  This routine allocates the columns structures then runs over
  * all rows and cells in the table.  The function pointers in "tbl" are
  * used for the actual width calculations.
  */
 void
-tblcalc(struct rofftbl *tbl, const struct tbl_span *sp)
+tblcalc(struct rofftbl *tbl, const struct tbl_span *sp,
+	size_t totalwidth)
 {
 	const struct tbl_dat	*dp;
 	struct roffcol		*col;
+	size_t			 ewidth, xwidth;
 	int			 spans;
+	int			 icol, maxcol, necol, nxcol;
 
 	/*
 	 * Allocate the master column specifiers.  These will hold the
 	 * widths and decimal positions for all cells in the column.  It
 	 * must be freed and nullified by the caller.
 	 */
 
 	assert(NULL == tbl->cols);
 	tbl->cols = mandoc_calloc((size_t)sp->opts->cols,
 	    sizeof(struct roffcol));
 
-	for ( ; sp; sp = sp->next) {
+	for (maxcol = -1; sp; sp = sp->next) {
 		if (TBL_SPAN_DATA != sp->pos)
 			continue;
 		spans = 1;
 		/*
 		 * Account for the data cells in the layout, matching it
 		 * to data cells in the data section.
 		 */
 		for (dp = sp->first; dp; dp = dp->next) {
 			/* Do not used spanned cells in the calculation. */
 			if (0 < --spans)
 				continue;
 			spans = dp->spans;
 			if (1 < spans)
 				continue;
-			assert(dp->layout);
-			col = &tbl->cols[dp->layout->head->ident];
+			icol = dp->layout->head->ident;
+			if (maxcol < icol)
+				maxcol = icol;
+			col = tbl->cols + icol;
+			col->flags |= dp->layout->flags;
+			if (dp->layout->flags & TBL_CELL_WIGN)
+				continue;
 			tblcalc_data(tbl, col, sp->opts, dp);
+		}
+	}
+
+	/*
+	 * Count columns to equalize and columns to maximize.
+	 * Find maximum width of the columns to equalize.
+	 * Find total width of the columns *not* to maximize.
+	 */
+
+	necol = nxcol = 0;
+	ewidth = xwidth = 0;
+	for (icol = 0; icol <= maxcol; icol++) {
+		col = tbl->cols + icol;
+		if (col->flags & TBL_CELL_EQUAL) {
+			necol++;
+			if (ewidth < col->width)
+				ewidth = col->width;
+		}
+		if (col->flags & TBL_CELL_WMAX)
+			nxcol++;
+		else
+			xwidth += col->width;
+	}
+
+	/*
+	 * Equalize columns, if requested for any of them.
+	 * Update total width of the columns not to maximize.
+	 */
+
+	if (necol) {
+		for (icol = 0; icol <= maxcol; icol++) {
+			col = tbl->cols + icol;
+			if ( ! (col->flags & TBL_CELL_EQUAL))
+				continue;
+			if (col->width == ewidth)
+				continue;
+			if (nxcol && totalwidth)
+				xwidth += ewidth - col->width;
+			col->width = ewidth;
+		}
+	}
+
+	/*
+	 * If there are any columns to maximize, find the total
+	 * available width, deducting 3n margins between columns.
+	 * Distribute the available width evenly.
+	 */
+
+	if (nxcol && totalwidth) {
+		xwidth = totalwidth - 3*maxcol - xwidth;
+		for (icol = 0; icol <= maxcol; icol++) {
+			col = tbl->cols + icol;
+			if ( ! (col->flags & TBL_CELL_WMAX))
+				continue;
+			col->width = xwidth / nxcol--;
+			xwidth -= col->width;
 		}
 	}
 }
 
 static void
 tblcalc_data(struct rofftbl *tbl, struct roffcol *col,
 		const struct tbl_opts *opts, const struct tbl_dat *dp)
 {
 	size_t		 sz;
 
 	/* Branch down into data sub-types. */
 
 	switch (dp->layout->pos) {
 	case TBL_CELL_HORIZ:
 		/* FALLTHROUGH */
 	case TBL_CELL_DHORIZ:
 		sz = (*tbl->len)(1, tbl->arg);
 		if (col->width < sz)
 			col->width = sz;
 		break;
 	case TBL_CELL_LONG:
 		/* FALLTHROUGH */
 	case TBL_CELL_CENTRE:
 		/* FALLTHROUGH */
 	case TBL_CELL_LEFT:
 		/* FALLTHROUGH */
 	case TBL_CELL_RIGHT:
 		tblcalc_literal(tbl, col, dp);
 		break;
 	case TBL_CELL_NUMBER:
 		tblcalc_number(tbl, col, opts, dp);
 		break;
 	case TBL_CELL_DOWN:
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 }
 
 static void
 tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
 		const struct tbl_dat *dp)
 {
 	size_t		 sz;
 	const char	*str;
 
 	str = dp->string ? dp->string : "";
 	sz = (*tbl->slen)(str, tbl->arg);
 
 	if (col->width < sz)
 		col->width = sz;
 }
 
 static void
 tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
 		const struct tbl_opts *opts, const struct tbl_dat *dp)
 {
 	int		 i;
 	size_t		 sz, psz, ssz, d;
 	const char	*str;
 	char		*cp;
 	char		 buf[2];
 
 	/*
 	 * First calculate number width and decimal place (last + 1 for
 	 * non-decimal numbers).  If the stored decimal is subsequent to
 	 * ours, make our size longer by that difference
 	 * (right-"shifting"); similarly, if ours is subsequent the
 	 * stored, then extend the stored size by the difference.
 	 * Finally, re-assign the stored values.
 	 */
 
 	str = dp->string ? dp->string : "";
 	sz = (*tbl->slen)(str, tbl->arg);
 
 	/* FIXME: TBL_DATA_HORIZ et al.? */
 
 	buf[0] = opts->decimal;
 	buf[1] = '\0';
 
 	psz = (*tbl->slen)(buf, tbl->arg);
 
 	if (NULL != (cp = strrchr(str, opts->decimal))) {
 		buf[1] = '\0';
 		for (ssz = 0, i = 0; cp != &str[i]; i++) {
 			buf[0] = str[i];
 			ssz += (*tbl->slen)(buf, tbl->arg);
 		}
 		d = ssz + psz;
 	} else
 		d = sz + psz;
 
 	/* Adjust the settings for this column. */
 
 	if (col->decimal > d) {
 		sz += col->decimal - d;
 		d = col->decimal;
 	} else
 		col->width += d - col->decimal;
 
 	if (sz > col->width)
 		col->width = sz;
 	if (d > col->decimal)
 		col->decimal = d;
 }
Index: vendor/mdocml/dist/out.h
===================================================================
--- vendor/mdocml/dist/out.h	(revision 275396)
+++ vendor/mdocml/dist/out.h	(revision 275397)
@@ -1,71 +1,73 @@
-/*	$Id: out.h,v 1.22 2014/04/20 16:46:05 schwarze Exp $ */
+/*	$Id: out.h,v 1.24 2014/10/14 02:16:06 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef OUT_H
 #define OUT_H
 
 enum	roffscale {
 	SCALE_CM, /* centimeters (c) */
 	SCALE_IN, /* inches (i) */
 	SCALE_PC, /* pica (P) */
 	SCALE_PT, /* points (p) */
 	SCALE_EM, /* ems (m) */
 	SCALE_MM, /* mini-ems (M) */
 	SCALE_EN, /* ens (n) */
 	SCALE_BU, /* default horizontal (u) */
 	SCALE_VS, /* default vertical (v) */
 	SCALE_FS, /* syn. for u (f) */
 	SCALE_MAX
 };
 
 struct	roffcol {
 	size_t		 width; /* width of cell */
 	size_t		 decimal; /* decimal position in cell */
+	int		 flags; /* layout flags, see tbl_cell */
 };
 
 struct	roffsu {
 	enum roffscale	  unit;
 	double		  scale;
 };
 
 typedef	size_t	(*tbl_strlen)(const char *, void *);
 typedef	size_t	(*tbl_len)(size_t, void *);
 
 struct	rofftbl {
 	tbl_strlen	 slen; /* calculate string length */
 	tbl_len		 len; /* produce width of empty space */
 	struct roffcol	*cols; /* master column specifiers */
 	void		*arg; /* passed to slen and len */
 };
 
 __BEGIN_DECLS
 
 #define	SCALE_VS_INIT(p, v) \
 	do { (p)->unit = SCALE_VS; \
 	     (p)->scale = (v); } \
 	while (/* CONSTCOND */ 0)
 
 #define	SCALE_HS_INIT(p, v) \
-	do { (p)->unit = SCALE_BU; \
+	do { (p)->unit = SCALE_EN; \
 	     (p)->scale = (v); } \
 	while (/* CONSTCOND */ 0)
 
 int		  a2roffsu(const char *, struct roffsu *, enum roffscale);
-void		  tblcalc(struct rofftbl *tbl, const struct tbl_span *);
+void		  tblcalc(struct rofftbl *tbl,
+			const struct tbl_span *, size_t);
 
 __END_DECLS
 
 #endif /*!OUT_H*/
Index: vendor/mdocml/dist/preconv.c
===================================================================
--- vendor/mdocml/dist/preconv.c	(revision 275396)
+++ vendor/mdocml/dist/preconv.c	(revision 275397)
@@ -1,523 +1,193 @@
-/*	$Id: preconv.c,v 1.6 2013/06/02 03:52:21 schwarze Exp $ */
+/*	$Id: preconv.c,v 1.12 2014/11/14 04:24:04 schwarze Exp $ */
 /*
  * Copyright (c) 2011 Kristaps Dzonsons 
+ * Copyright (c) 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
-#ifdef HAVE_MMAP
-#include 
-#include 
-#endif
+#include 
 
-#include 
-#include 
 #include 
-#include 
 #include 
-#include 
+#include "mandoc.h"
+#include "libmandoc.h"
 
-/* 
- * The read_whole_file() and resize_buf() functions are copied from
- * read.c, including all dependency code.
- */
-
-enum	enc {
-	ENC_UTF_8, /* UTF-8 */
-	ENC_US_ASCII, /* US-ASCII */
-	ENC_LATIN_1, /* Latin-1 */
-	ENC__MAX
-};
-
-struct	buf {
-	char		 *buf; /* binary input buffer */
-	size_t	 	  sz; /* size of binary buffer */
-	size_t		  offs; /* starting buffer offset */
-};
-
-struct	encode {
-	const char	 *name;
-	int		(*conv)(const struct buf *);
-};
-
-static	int	 cue_enc(const struct buf *, size_t *, enum enc *);
-static	int	 conv_latin_1(const struct buf *);
-static	int	 conv_us_ascii(const struct buf *);
-static	int	 conv_utf_8(const struct buf *);
-static	int	 read_whole_file(const char *, int, 
-			struct buf *, int *);
-static	void	 resize_buf(struct buf *, size_t);
-static	void	 usage(void);
-
-static	const struct encode encs[ENC__MAX] = {
-	{ "utf-8", conv_utf_8 }, /* ENC_UTF_8 */
-	{ "us-ascii", conv_us_ascii }, /* ENC_US_ASCII */
-	{ "latin-1", conv_latin_1 }, /* ENC_LATIN_1 */
-};
-
-static	const char	 *progname;
-
-static void
-usage(void)
+int
+preconv_encode(struct buf *ib, size_t *ii, struct buf *ob, size_t *oi,
+    int *filenc)
 {
-
-	fprintf(stderr, "usage: %s "
-			"[-D enc] "
-			"[-e ENC] "
-			"[file]\n", progname);
-}
-
-static int
-conv_latin_1(const struct buf *b)
-{
 	size_t		 i;
-	unsigned char	 cu;
-	const char	*cp;
-
-	cp = b->buf + (int)b->offs;
-
-	/*
-	 * Latin-1 falls into the first 256 code-points of Unicode, so
-	 * there's no need for any sort of translation.  Just make the
-	 * 8-bit characters use the Unicode escape.
-	 * Note that binary values 128 < v < 160 are passed through
-	 * unmodified to mandoc.
-	 */
-
-	for (i = b->offs; i < b->sz; i++) {
-		cu = (unsigned char)*cp++;
-		cu < 160U ? putchar(cu) : printf("\\[u%.4X]", cu);
-	}
-
-	return(1);
-}
-
-static int
-conv_us_ascii(const struct buf *b)
-{
-
-	/*
-	 * US-ASCII has no conversion since it falls into the first 128
-	 * bytes of Unicode.
-	 */
-
-	fwrite(b->buf, 1, b->sz, stdout);
-	return(1);
-}
-
-static int
-conv_utf_8(const struct buf *b)
-{
-	int		 state, be;
+	int		 state;
 	unsigned int	 accum;
-	size_t		 i;
 	unsigned char	 cu;
-	const char	*cp;
-	const long	 one = 1L;
 
-	cp = b->buf + (int)b->offs;
+	if ( ! (*filenc & MPARSE_UTF8))
+		goto latin;
+
 	state = 0;
 	accum = 0U;
-	be = 0;
 
-	/* Quick test for big-endian value. */
-
-	if ( ! (*((const char *)(&one))))
-		be = 1;
-
-	for (i = b->offs; i < b->sz; i++) {
-		cu = (unsigned char)*cp++;
+	for (i = *ii; i < ib->sz; i++) {
+		cu = ib->buf[i];
 		if (state) {
 			if ( ! (cu & 128) || (cu & 64)) {
 				/* Bad sequence header. */
-				return(0);
+				break;
 			}
 
 			/* Accept only legitimate bit patterns. */
 
 			if (cu > 191 || cu < 128) {
 				/* Bad in-sequence bits. */
-				return(0);
+				break;
 			}
 
 			accum |= (cu & 63) << --state * 6;
 
-			/*
-			 * Accum is held in little-endian order as
-			 * stipulated by the UTF-8 sequence coding.  We
-			 * need to convert to a native big-endian if our
-			 * architecture requires it.
-			 */
+			if (state)
+				continue;
 
-			if (0 == state && be) 
-				accum = (accum >> 24) | 
-					((accum << 8) & 0x00FF0000) |
-					((accum >> 8) & 0x0000FF00) |
-					(accum << 24);
-
-			if (0 == state) {
-				accum < 128U ? putchar(accum) : 
-					printf("\\[u%.4X]", accum);
-				accum = 0U;
-			}
-		} else if (cu & (1 << 7)) {
+			if (accum < 0x80)
+				ob->buf[(*oi)++] = accum;
+			else
+				*oi += snprintf(ob->buf + *oi,
+				    11, "\\[u%.4X]", accum);
+			*ii = i + 1;
+			*filenc &= ~MPARSE_LATIN1;
+			return(1);
+		} else {
 			/*
 			 * Entering a UTF-8 state:  if we encounter a
 			 * UTF-8 bitmask, calculate the expected UTF-8
 			 * state from it.
 			 */
-			for (state = 0; state < 7; state++) 
+			for (state = 0; state < 7; state++)
 				if ( ! (cu & (1 << (7 - state))))
 					break;
 
 			/* Accept only legitimate bit patterns. */
 
-			switch (state) {
+			switch (state--) {
 			case (4):
 				if (cu <= 244 && cu >= 240) {
 					accum = (cu & 7) << 18;
-					break;
+					continue;
 				}
 				/* Bad 4-sequence start bits. */
-				return(0);
+				break;
 			case (3):
 				if (cu <= 239 && cu >= 224) {
 					accum = (cu & 15) << 12;
-					break;
+					continue;
 				}
 				/* Bad 3-sequence start bits. */
-				return(0);
+				break;
 			case (2):
 				if (cu <= 223 && cu >= 194) {
 					accum = (cu & 31) << 6;
-					break;
+					continue;
 				}
 				/* Bad 2-sequence start bits. */
-				return(0);
+				break;
 			default:
 				/* Bad sequence bit mask. */
-				return(0);
+				break;
 			}
-			state--;
-		} else
-			putchar(cu);
+			break;
+		}
 	}
 
-	if (0 != state) {
-		/* Bad trailing bits. */
-		return(0);
-	}
+	/* FALLTHROUGH: Invalid or incomplete UTF-8 sequence. */
 
-	return(1);
-}
-
-static void
-resize_buf(struct buf *buf, size_t initial)
-{
-
-	buf->sz = buf->sz > initial / 2 ? 
-		2 * buf->sz : initial;
-
-	buf->buf = realloc(buf->buf, buf->sz);
-	if (NULL == buf->buf) {
-		perror(NULL);
-		exit(EXIT_FAILURE);
-	}
-}
-
-static int
-read_whole_file(const char *f, int fd, 
-		struct buf *fb, int *with_mmap)
-{
-	size_t		 off;
-	ssize_t		 ssz;
-
-#ifdef	HAVE_MMAP
-	struct stat	 st;
-	if (-1 == fstat(fd, &st)) {
-		perror(f);
+latin:
+	if ( ! (*filenc & MPARSE_LATIN1))
 		return(0);
-	}
 
-	/*
-	 * If we're a regular file, try just reading in the whole entry
-	 * via mmap().  This is faster than reading it into blocks, and
-	 * since each file is only a few bytes to begin with, I'm not
-	 * concerned that this is going to tank any machines.
-	 */
+	*oi += snprintf(ob->buf + *oi, 11,
+	    "\\[u%.4X]", (unsigned char)ib->buf[(*ii)++]);
 
-	if (S_ISREG(st.st_mode) && st.st_size >= (1U << 31)) {
-		fprintf(stderr, "%s: input too large\n", f);
-		return(0);
-	} 
-	
-	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_SHARED, fd, 0);
-		if (fb->buf != MAP_FAILED)
-			return(1);
-	}
-#endif
-
-	/*
-	 * If this isn't a regular file (like, say, stdin), then we must
-	 * go the old way and just read things in bit by bit.
-	 */
-
-	*with_mmap = 0;
-	off = 0;
-	fb->sz = 0;
-	fb->buf = NULL;
-	for (;;) {
-		if (off == fb->sz && fb->sz == (1U << 31)) {
-			fprintf(stderr, "%s: input too large\n", f);
-			break;
-		} 
-		
-		if (off == fb->sz)
-			resize_buf(fb, 65536);
-
-		ssz = read(fd, fb->buf + (int)off, fb->sz - off);
-		if (ssz == 0) {
-			fb->sz = off;
-			return(1);
-		}
-		if (ssz == -1) {
-			perror(f);
-			break;
-		}
-		off += (size_t)ssz;
-	}
-
-	free(fb->buf);
-	fb->buf = NULL;
-	return(0);
+	*filenc &= ~MPARSE_UTF8;
+	return(1);
 }
 
-static int
-cue_enc(const struct buf *b, size_t *offs, enum enc *enc)
+int
+preconv_cue(const struct buf *b, size_t offset)
 {
 	const char	*ln, *eoln, *eoph;
-	size_t		 sz, phsz, nsz;
-	int		 i;
+	size_t		 sz, phsz;
 
-	ln = b->buf + (int)*offs;
-	sz = b->sz - *offs;
+	ln = b->buf + offset;
+	sz = b->sz - offset;
 
 	/* Look for the end-of-line. */
 
 	if (NULL == (eoln = memchr(ln, '\n', sz)))
-		return(-1);
+		eoln = ln + sz;
 
-	/* Set next-line marker. */
-
-	*offs = (size_t)((eoln + 1) - b->buf);
-
 	/* Check if we have the correct header/trailer. */
 
-	if ((sz = (size_t)(eoln - ln)) < 10 || 
-			memcmp(ln, ".\\\" -*-", 7) ||
-			memcmp(eoln - 3, "-*-", 3))
-		return(0);
+	if ((sz = (size_t)(eoln - ln)) < 10 ||
+	    memcmp(ln, ".\\\" -*-", 7) || memcmp(eoln - 3, "-*-", 3))
+		return(MPARSE_UTF8 | MPARSE_LATIN1);
 
 	/* Move after the header and adjust for the trailer. */
 
 	ln += 7;
 	sz -= 10;
 
 	while (sz > 0) {
 		while (sz > 0 && ' ' == *ln) {
 			ln++;
 			sz--;
 		}
 		if (0 == sz)
 			break;
 
 		/* Find the end-of-phrase marker (or eoln). */
 
 		if (NULL == (eoph = memchr(ln, ';', sz)))
 			eoph = eoln - 3;
 		else
 			eoph++;
 
 		/* Only account for the "coding" phrase. */
 
-		if ((phsz = (size_t)(eoph - ln)) < 7 ||
-				strncasecmp(ln, "coding:", 7)) {
+		if ((phsz = eoph - ln) < 7 ||
+		    strncasecmp(ln, "coding:", 7)) {
 			sz -= phsz;
 			ln += phsz;
 			continue;
-		} 
+		}
 
 		sz -= 7;
 		ln += 7;
 
 		while (sz > 0 && ' ' == *ln) {
 			ln++;
 			sz--;
 		}
 		if (0 == sz)
-			break;
+			return(0);
 
 		/* Check us against known encodings. */
 
-		for (i = 0; i < (int)ENC__MAX; i++) {
-			nsz = strlen(encs[i].name);
-			if (phsz < nsz)
-				continue;
-			if (strncasecmp(ln, encs[i].name, nsz))
-				continue;
-
-			*enc = (enum enc)i;
-			return(1);
-		}
-
-		/* Unknown encoding. */
-
-		*enc = ENC__MAX;
-		return(1);
+		if (phsz > 4 && !strncasecmp(ln, "utf-8", 5))
+			return(MPARSE_UTF8);
+		if (phsz > 10 && !strncasecmp(ln, "iso-latin-1", 11))
+			return(MPARSE_LATIN1);
+		return(0);
 	}
-
-	return(0);
-}
-
-int
-main(int argc, char *argv[])
-{
-	int	 	 i, ch, map, fd, rc;
-	struct buf	 b;
-	const char	*fn;
-	enum enc	 enc, def;
-	unsigned char 	 bom[3] = { 0xEF, 0xBB, 0xBF };
-	size_t		 offs;
-	extern int	 optind;
-	extern char	*optarg;
-
-	progname = strrchr(argv[0], '/');
-	if (progname == NULL)
-		progname = argv[0];
-	else
-		++progname;
-
-	fn = "";
-	fd = STDIN_FILENO;
-	rc = EXIT_FAILURE;
-	enc = def = ENC__MAX;
-	map = 0;
-
-	memset(&b, 0, sizeof(struct buf));
-
-	while (-1 != (ch = getopt(argc, argv, "D:e:rdvh")))
-		switch (ch) {
-		case ('D'):
-			/* FALLTHROUGH */
-		case ('e'):
-			for (i = 0; i < (int)ENC__MAX; i++) {
-				if (strcasecmp(optarg, encs[i].name))
-					continue;
-				break;
-			}
-			if (i < (int)ENC__MAX) {
-				if ('D' == ch)
-					def = (enum enc)i;
-				else
-					enc = (enum enc)i;
-				break;
-			}
-
-			fprintf(stderr, "%s: Bad encoding\n", optarg);
-			return(EXIT_FAILURE);
-		case ('r'):
-			/* FALLTHROUGH */
-		case ('d'):
-			/* FALLTHROUGH */
-		case ('v'):
-			/* Compatibility with GNU preconv. */
-			break;
-		case ('h'):
-			/* Compatibility with GNU preconv. */
-			/* FALLTHROUGH */
-		default:
-			usage();
-			return(EXIT_FAILURE);
-		}
-
-	argc -= optind;
-	argv += optind;
-	
-	/* 
-	 * Open and read the first argument on the command-line.
-	 * If we don't have one, we default to stdin.
-	 */
-
-	if (argc > 0) {
-		fn = *argv;
-		fd = open(fn, O_RDONLY, 0);
-		if (-1 == fd) {
-			perror(fn);
-			return(EXIT_FAILURE);
-		}
-	}
-
-	if ( ! read_whole_file(fn, fd, &b, &map))
-		goto out;
-
-	/* Try to read the UTF-8 BOM. */
-
-	if (ENC__MAX == enc)
-		if (b.sz > 3 && 0 == memcmp(b.buf, bom, 3)) {
-			b.offs = 3;
-			enc = ENC_UTF_8;
-		}
-
-	/* Try reading from the "-*-" cue. */
-
-	if (ENC__MAX == enc) {
-		offs = b.offs;
-		ch = cue_enc(&b, &offs, &enc);
-		if (0 == ch)
-			ch = cue_enc(&b, &offs, &enc);
-	}
-
-	/*
-	 * No encoding has been detected.
-	 * Thus, we either fall into our default encoder, if specified,
-	 * or use Latin-1 if all else fails.
-	 */
-
-	if (ENC__MAX == enc) 
-		enc = ENC__MAX == def ? ENC_LATIN_1 : def;
-
-	if ( ! (*encs[(int)enc].conv)(&b)) {
-		fprintf(stderr, "%s: Bad encoding\n", fn);
-		goto out;
-	}
-
-	rc = EXIT_SUCCESS;
-out:
-#ifdef	HAVE_MMAP
-	if (map)
-		munmap(b.buf, b.sz);
-	else 
-#endif
-		free(b.buf);
-
-	if (fd > STDIN_FILENO)
-		close(fd);
-
-	return(rc);
+	return(MPARSE_UTF8 | MPARSE_LATIN1);
 }
Index: vendor/mdocml/dist/read.c
===================================================================
--- vendor/mdocml/dist/read.c	(revision 275396)
+++ vendor/mdocml/dist/read.c	(revision 275397)
@@ -1,923 +1,1063 @@
-/*	$Id: read.c,v 1.79 2014/08/06 15:09:05 schwarze Exp $ */
+/*	$Id: read.c,v 1.101 2014/11/28 18:09:01 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2010-2014 Ingo Schwarze 
  * Copyright (c) 2010, 2012 Joerg Sonnenberger 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
-#ifdef HAVE_MMAP
-# include 
-# include 
+#include 
+#if HAVE_MMAP
+#include 
+#include 
 #endif
+#include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "libmandoc.h"
 #include "mdoc.h"
 #include "man.h"
 #include "main.h"
 
 #define	REPARSE_LIMIT	1000
 
-struct	buf {
-	char		 *buf; /* binary input buffer */
-	size_t		  sz; /* size of binary buffer */
-};
-
 struct	mparse {
-	enum mandoclevel  file_status; /* status of current parse */
-	enum mandoclevel  wlevel; /* ignore messages below this */
-	int		  line; /* line number in the file */
-	int		  options; /* parser options */
 	struct man	 *pman; /* persistent man parser */
 	struct mdoc	 *pmdoc; /* persistent mdoc parser */
 	struct man	 *man; /* man parser */
 	struct mdoc	 *mdoc; /* mdoc parser */
 	struct roff	 *roff; /* roff parser (!NULL) */
+	const struct mchars *mchars; /* character table */
 	char		 *sodest; /* filename pointed to by .so */
-	int		  reparse_count; /* finite interp. stack */
-	mandocmsg	  mmsg; /* warning/error message handler */
-	const char	 *file;
-	struct buf	 *secondary;
+	const char	 *file; /* filename of current input file */
+	struct buf	 *primary; /* buffer currently being parsed */
+	struct buf	 *secondary; /* preprocessed copy of input */
 	const char	 *defos; /* default operating system */
+	mandocmsg	  mmsg; /* warning/error message handler */
+	enum mandoclevel  file_status; /* status of current parse */
+	enum mandoclevel  wlevel; /* ignore messages below this */
+	int		  options; /* parser options */
+	int		  filenc; /* encoding of the current file */
+	int		  reparse_count; /* finite interp. stack */
+	int		  line; /* line number in the file */
+	pid_t		  child; /* the gunzip(1) process */
 };
 
+static	void	  choose_parser(struct mparse *);
 static	void	  resize_buf(struct buf *, size_t);
-static	void	  mparse_buf_r(struct mparse *, struct buf, int);
-static	void	  pset(const char *, int, struct mparse *);
+static	void	  mparse_buf_r(struct mparse *, struct buf, size_t, int);
 static	int	  read_whole_file(struct mparse *, 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,
 	MANDOCERR_WARNING,
 	MANDOCERR_WARNING,
 	MANDOCERR_ERROR,
 	MANDOCERR_FATAL,
 	MANDOCERR_MAX,
 	MANDOCERR_MAX
 };
 
 static	const char * const	mandocerrs[MANDOCERR_MAX] = {
 	"ok",
 
 	"generic warning",
 
 	/* related to the prologue */
 	"missing manual title, using UNTITLED",
 	"missing manual title, using \"\"",
 	"lower case character in document title",
 	"missing manual section, using \"\"",
 	"unknown manual section",
-	"unknown manual volume or arch",
 	"missing date, using today's date",
 	"cannot parse date, using it verbatim",
 	"missing Os macro, using \"\"",
 	"duplicate prologue macro",
 	"late prologue macro",
 	"skipping late title macro",
 	"prologue macros out of order",
 
 	/* related to document structure */
 	".so is fragile, better use ln(1)",
 	"no document body",
 	"content before first section header",
 	"first section is not \"NAME\"",
 	"bad NAME section contents",
 	"sections out of conventional order",
 	"duplicate section title",
 	"unexpected section",
+	"unusual Xr order",
+	"unusual Xr punctuation",
+	"AUTHORS section without An macro",
 
 	/* related to macros and nesting */
 	"obsolete macro",
 	"skipping paragraph macro",
 	"moving paragraph macro out of list",
 	"skipping no-space macro",
 	"blocks badly nested",
 	"nested displays are not portable",
 	"moving content out of list",
 	".Vt block has child macro",
 	"fill mode already enabled, skipping",
 	"fill mode already disabled, skipping",
 	"line scope broken",
 
 	/* related to missing macro arguments */
 	"skipping empty request",
 	"conditional request controls empty scope",
 	"skipping empty macro",
 	"empty argument, using 0n",
 	"argument count wrong",
 	"missing display type, using -ragged",
 	"list type is not the first argument",
 	"missing -width in -tag list, using 8n",
 	"missing utility name, using \"\"",
 	"empty head in list item",
 	"empty list item",
 	"missing font type, using \\fR",
 	"unknown font type, using \\fR",
 	"missing -std argument, adding it",
+	"missing eqn box, using \"\"",
 
 	/* related to bad macro arguments */
 	"unterminated quoted argument",
 	"duplicate argument",
 	"skipping duplicate argument",
 	"skipping duplicate display type",
 	"skipping duplicate list type",
 	"skipping -width argument",
 	"unknown AT&T UNIX version",
+	"comma in function argument",
+	"parenthesis in function name",
 	"invalid content in Rs block",
 	"invalid Boolean argument",
 	"unknown font, skipping request",
 
 	/* related to plain text */
 	"blank line in fill mode, using .sp",
 	"tab in filled text",
 	"whitespace at end of input line",
 	"bad comment style",
 	"invalid escape sequence",
 	"undefined string, using \"\"",
 
 	"generic error",
 
 	/* related to equations */
 	"unexpected equation scope closure",
 	"equation scope open on exit",
 	"overlapping equation scopes",
 	"unexpected end of equation",
-	"equation syntax error",
 
 	/* related to tables */
 	"bad table syntax",
 	"bad table option",
 	"bad table layout",
 	"no table layout cells specified",
 	"no table data cells specified",
 	"ignore data in cell",
 	"data block still open",
 	"ignoring extra data cells",
 
 	/* related to document structure and macros */
 	"input stack limit exceeded, infinite loop?",
 	"skipping bad character",
 	"skipping unknown macro",
 	"skipping item outside list",
 	"skipping column outside column list",
 	"skipping end of block that is not open",
 	"inserting missing end of block",
 	"appending missing end of block",
 
 	/* related to request and macro arguments */
 	"escaped character not allowed in a name",
 	"argument count wrong",
+	"NOT IMPLEMENTED: Bd -file",
 	"missing list type, using -item",
 	"missing manual name, using \"\"",
 	"uname(3) system call failed, using UNKNOWN",
 	"unknown standard specifier",
 	"skipping request without numeric argument",
 	"skipping all arguments",
 	"skipping excess arguments",
+	"divide by zero",
 
 	"generic fatal error",
 
 	"input too large",
-	"NOT IMPLEMENTED: Bd -file",
 	"NOT IMPLEMENTED: .so with absolute path or \"..\"",
 	".so request failed",
 
 	/* system errors */
+	"cannot dup file descriptor",
+	"cannot exec",
+	"gunzip failed with code",
+	"cannot fork",
 	NULL,
-	"cannot stat file",
+	"cannot open pipe",
 	"cannot read file",
+	"gunzip died from signal",
+	"cannot stat file",
+	"wait failed",
 };
 
 static	const char * const	mandoclevels[MANDOCLEVEL_MAX] = {
 	"SUCCESS",
 	"RESERVED",
 	"WARNING",
 	"ERROR",
 	"FATAL",
 	"BADARG",
 	"SYSERR"
 };
 
 
 static void
 resize_buf(struct buf *buf, size_t initial)
 {
 
 	buf->sz = buf->sz > initial/2 ? 2 * buf->sz : initial;
 	buf->buf = mandoc_realloc(buf->buf, buf->sz);
 }
 
 static void
-pset(const char *buf, int pos, struct mparse *curp)
+choose_parser(struct mparse *curp)
 {
-	int		 i;
+	char		*cp, *ep;
+	int		 format;
 
 	/*
-	 * Try to intuit which kind of manual parser should be used.  If
-	 * passed in by command-line (-man, -mdoc), then use that
-	 * explicitly.  If passed as -mandoc, then try to guess from the
-	 * line: either skip dot-lines, use -mdoc when finding `.Dt', or
-	 * default to -man, which is more lenient.
-	 *
-	 * Separate out pmdoc/pman from mdoc/man: the first persists
-	 * through all parsers, while the latter is used per-parse.
+	 * If neither command line arguments -mdoc or -man select
+	 * a parser nor the roff parser found a .Dd or .TH macro
+	 * yet, look ahead in the main input buffer.
 	 */
 
-	if ('.' == buf[0] || '\'' == buf[0]) {
-		for (i = 1; buf[i]; i++)
-			if (' ' != buf[i] && '\t' != buf[i])
+	if ((format = roff_getformat(curp->roff)) == 0) {
+		cp = curp->primary->buf;
+		ep = cp + curp->primary->sz;
+		while (cp < ep) {
+			if (*cp == '.' || *cp == '\'') {
+				cp++;
+				if (cp[0] == 'D' && cp[1] == 'd') {
+					format = MPARSE_MDOC;
+					break;
+				}
+				if (cp[0] == 'T' && cp[1] == 'H') {
+					format = MPARSE_MAN;
+					break;
+				}
+			}
+			cp = memchr(cp, '\n', ep - cp);
+			if (cp == NULL)
 				break;
-		if ('\0' == buf[i])
-			return;
+			cp++;
+		}
 	}
 
-	if (MPARSE_MDOC & curp->options) {
-		curp->mdoc = curp->pmdoc;
-		return;
-	} else if (MPARSE_MAN & curp->options) {
-		curp->man = curp->pman;
-		return;
-	}
-
-	if (pos >= 3 && 0 == memcmp(buf, ".Dd", 3))  {
+	if (format == MPARSE_MDOC) {
 		if (NULL == curp->pmdoc)
 			curp->pmdoc = mdoc_alloc(
 			    curp->roff, curp, curp->defos,
 			    MPARSE_QUICK & curp->options ? 1 : 0);
 		assert(curp->pmdoc);
 		curp->mdoc = curp->pmdoc;
 		return;
 	}
 
+	/* Fall back to man(7) as a last resort. */
+
 	if (NULL == curp->pman)
 		curp->pman = man_alloc(curp->roff, curp,
 		    MPARSE_QUICK & curp->options ? 1 : 0);
 	assert(curp->pman);
 	curp->man = curp->pman;
 }
 
 /*
- * Main parse routine for an opened file.  This is called for each
- * opened file and simply loops around the full input file, possibly
- * nesting (i.e., with `so').
+ * Main parse routine for a buffer.
+ * It assumes encoding and line numbering are already set up.
+ * It can recurse directly (for invocations of user-defined
+ * macros, inline equations, and input line traps)
+ * and indirectly (for .so file inclusion).
  */
 static void
-mparse_buf_r(struct mparse *curp, struct buf blk, int start)
+mparse_buf_r(struct mparse *curp, struct buf blk, size_t i, int start)
 {
 	const struct tbl_span	*span;
 	struct buf	 ln;
+	size_t		 pos; /* byte number in the ln buffer */
 	enum rofferr	 rr;
-	int		 i, of, rc;
-	int		 pos; /* byte number in the ln buffer */
+	int		 of;
 	int		 lnn; /* line number in the real file */
 	unsigned char	 c;
 
-	memset(&ln, 0, sizeof(struct buf));
+	memset(&ln, 0, sizeof(ln));
 
 	lnn = curp->line;
 	pos = 0;
 
-	for (i = 0; i < (int)blk.sz; ) {
+	while (i < blk.sz) {
 		if (0 == pos && '\0' == blk.buf[i])
 			break;
 
 		if (start) {
 			curp->line = lnn;
 			curp->reparse_count = 0;
+
+			if (lnn < 3 &&
+			    curp->filenc & MPARSE_UTF8 &&
+			    curp->filenc & MPARSE_LATIN1)
+				curp->filenc = preconv_cue(&blk, i);
 		}
 
-		while (i < (int)blk.sz && (start || '\0' != blk.buf[i])) {
+		while (i < blk.sz && (start || blk.buf[i] != '\0')) {
 
 			/*
 			 * When finding an unescaped newline character,
 			 * leave the character loop to process the line.
 			 * Skip a preceding carriage return, if any.
 			 */
 
-			if ('\r' == blk.buf[i] && i + 1 < (int)blk.sz &&
+			if ('\r' == blk.buf[i] && i + 1 < blk.sz &&
 			    '\n' == blk.buf[i + 1])
 				++i;
 			if ('\n' == blk.buf[i]) {
 				++i;
 				++lnn;
 				break;
 			}
 
 			/*
-			 * Make sure we have space for at least
-			 * one backslash and one other character
-			 * and the trailing NUL byte.
+			 * Make sure we have space for the worst
+			 * case of 11 bytes: "\\[u10ffff]\0"
 			 */
 
-			if (pos + 2 >= (int)ln.sz)
+			if (pos + 11 > ln.sz)
 				resize_buf(&ln, 256);
 
 			/*
-			 * Warn about bogus characters.  If you're using
-			 * non-ASCII encoding, you're screwing your
-			 * readers.  Since I'd rather this not happen,
-			 * I'll be helpful and replace these characters
-			 * with "?", so we don't display gibberish.
-			 * Note to manual writers: use special characters.
+			 * Encode 8-bit input.
 			 */
 
-			c = (unsigned char) blk.buf[i];
+			c = blk.buf[i];
+			if (c & 0x80) {
+				if ( ! (curp->filenc && preconv_encode(
+				    &blk, &i, &ln, &pos, &curp->filenc))) {
+					mandoc_vmsg(MANDOCERR_BADCHAR,
+					    curp, curp->line, pos,
+					    "0x%x", c);
+					ln.buf[pos++] = '?';
+					i++;
+				}
+				continue;
+			}
 
-			if ( ! (isascii(c) &&
-			    (isgraph(c) || isblank(c)))) {
+			/*
+			 * Exclude control characters.
+			 */
+
+			if (c == 0x7f || (c < 0x20 && c != 0x09)) {
 				mandoc_vmsg(MANDOCERR_BADCHAR, curp,
 				    curp->line, pos, "0x%x", c);
 				i++;
 				ln.buf[pos++] = '?';
 				continue;
 			}
 
 			/* Trailing backslash = a plain char. */
 
-			if ('\\' != blk.buf[i] || i + 1 == (int)blk.sz) {
+			if (blk.buf[i] != '\\' || i + 1 == blk.sz) {
 				ln.buf[pos++] = blk.buf[i++];
 				continue;
 			}
 
 			/*
 			 * Found escape and at least one other character.
 			 * When it's a newline character, skip it.
 			 * When there is a carriage return in between,
 			 * skip that one as well.
 			 */
 
-			if ('\r' == blk.buf[i + 1] && i + 2 < (int)blk.sz &&
+			if ('\r' == blk.buf[i + 1] && i + 2 < blk.sz &&
 			    '\n' == blk.buf[i + 2])
 				++i;
 			if ('\n' == blk.buf[i + 1]) {
 				i += 2;
 				++lnn;
 				continue;
 			}
 
 			if ('"' == blk.buf[i + 1] || '#' == blk.buf[i + 1]) {
 				i += 2;
 				/* Comment, skip to end of line */
-				for (; i < (int)blk.sz; ++i) {
+				for (; i < blk.sz; ++i) {
 					if ('\n' == blk.buf[i]) {
 						++i;
 						++lnn;
 						break;
 					}
 				}
 
 				/* Backout trailing whitespaces */
 				for (; pos > 0; --pos) {
 					if (ln.buf[pos - 1] != ' ')
 						break;
 					if (pos > 2 && ln.buf[pos - 2] == '\\')
 						break;
 				}
 				break;
 			}
 
 			/* Catch escaped bogus characters. */
 
 			c = (unsigned char) blk.buf[i+1];
 
 			if ( ! (isascii(c) &&
 			    (isgraph(c) || isblank(c)))) {
 				mandoc_vmsg(MANDOCERR_BADCHAR, curp,
 				    curp->line, pos, "0x%x", c);
 				i += 2;
 				ln.buf[pos++] = '?';
 				continue;
 			}
 
 			/* Some other escape sequence, copy & cont. */
 
 			ln.buf[pos++] = blk.buf[i++];
 			ln.buf[pos++] = blk.buf[i++];
 		}
 
-		if (pos >= (int)ln.sz)
+		if (pos >= ln.sz)
 			resize_buf(&ln, 256);
 
 		ln.buf[pos] = '\0';
 
 		/*
 		 * A significant amount of complexity is contained by
 		 * the roff preprocessor.  It's line-oriented but can be
 		 * expressed on one line, so we need at times to
 		 * readjust our starting point and re-run it.  The roff
 		 * preprocessor can also readjust the buffers with new
 		 * data, so we pass them in wholesale.
 		 */
 
 		of = 0;
 
 		/*
 		 * Maintain a lookaside buffer of all parsed lines.  We
 		 * only do this if mparse_keep() has been invoked (the
 		 * buffer may be accessed with mparse_getkeep()).
 		 */
 
 		if (curp->secondary) {
 			curp->secondary->buf = mandoc_realloc(
 			    curp->secondary->buf,
 			    curp->secondary->sz + pos + 2);
 			memcpy(curp->secondary->buf +
 			    curp->secondary->sz,
 			    ln.buf, pos);
 			curp->secondary->sz += pos;
 			curp->secondary->buf
 				[curp->secondary->sz] = '\n';
 			curp->secondary->sz++;
 			curp->secondary->buf
 				[curp->secondary->sz] = '\0';
 		}
 rerun:
-		rr = roff_parseln(curp->roff, curp->line,
-		    &ln.buf, &ln.sz, of, &of);
+		rr = roff_parseln(curp->roff, curp->line, &ln, &of);
 
 		switch (rr) {
 		case ROFF_REPARSE:
 			if (REPARSE_LIMIT >= ++curp->reparse_count)
-				mparse_buf_r(curp, ln, 0);
+				mparse_buf_r(curp, ln, of, 0);
 			else
 				mandoc_msg(MANDOCERR_ROFFLOOP, curp,
 				    curp->line, pos, NULL);
 			pos = 0;
 			continue;
 		case ROFF_APPEND:
-			pos = (int)strlen(ln.buf);
+			pos = strlen(ln.buf);
 			continue;
 		case ROFF_RERUN:
 			goto rerun;
 		case ROFF_IGN:
 			pos = 0;
 			continue;
 		case ROFF_ERR:
 			assert(MANDOCLEVEL_FATAL <= curp->file_status);
 			break;
 		case ROFF_SO:
-			if (0 == (MPARSE_SO & curp->options) &&
-			    (i >= (int)blk.sz || '\0' == blk.buf[i])) {
+			if ( ! (curp->options & MPARSE_SO) &&
+			    (i >= blk.sz || blk.buf[i] == '\0')) {
 				curp->sodest = mandoc_strdup(ln.buf + of);
 				free(ln.buf);
 				return;
 			}
 			/*
 			 * We remove `so' clauses from our lookaside
 			 * buffer because we're going to descend into
 			 * the file recursively.
 			 */
 			if (curp->secondary)
 				curp->secondary->sz -= pos + 1;
 			mparse_readfd(curp, -1, ln.buf + of);
 			if (MANDOCLEVEL_FATAL <= curp->file_status) {
 				mandoc_vmsg(MANDOCERR_SO_FAIL,
 				    curp, curp->line, pos,
 				    ".so %s", ln.buf + of);
 				break;
 			}
 			pos = 0;
 			continue;
 		default:
 			break;
 		}
 
 		/*
 		 * If we encounter errors in the recursive parse, make
 		 * sure we don't continue parsing.
 		 */
 
 		if (MANDOCLEVEL_FATAL <= curp->file_status)
 			break;
 
 		/*
 		 * If input parsers have not been allocated, do so now.
 		 * We keep these instanced between parsers, but set them
 		 * locally per parse routine since we can use different
 		 * parsers with each one.
 		 */
 
 		if ( ! (curp->man || curp->mdoc))
-			pset(ln.buf + of, pos - of, curp);
+			choose_parser(curp);
 
 		/*
-		 * Lastly, push down into the parsers themselves.  One
-		 * of these will have already been set in the pset()
-		 * routine.
+		 * Lastly, push down into the parsers themselves.
 		 * If libroff returns ROFF_TBL, then add it to the
 		 * currently open parse.  Since we only get here if
 		 * there does exist data (see tbl_data.c), we're
 		 * guaranteed that something's been allocated.
 		 * Do the same for ROFF_EQN.
 		 */
 
-		rc = -1;
+		if (rr == ROFF_TBL) {
+			while ((span = roff_span(curp->roff)) != NULL)
+				if (curp->man == NULL)
+					mdoc_addspan(curp->mdoc, span);
+				else
+					man_addspan(curp->man, span);
+		} else if (rr == ROFF_EQN) {
+			if (curp->man == NULL)
+				mdoc_addeqn(curp->mdoc, roff_eqn(curp->roff));
+			else
+				man_addeqn(curp->man, roff_eqn(curp->roff));
+		} else if ((curp->man == NULL ?
+		    mdoc_parseln(curp->mdoc, curp->line, ln.buf, of) :
+		    man_parseln(curp->man, curp->line, ln.buf, of)) == 2)
+				break;
 
-		if (ROFF_TBL == rr)
-			while (NULL != (span = roff_span(curp->roff))) {
-				rc = curp->man ?
-				    man_addspan(curp->man, span) :
-				    mdoc_addspan(curp->mdoc, span);
-				if (0 == rc)
-					break;
-			}
-		else if (ROFF_EQN == rr)
-			rc = curp->mdoc ?
-			    mdoc_addeqn(curp->mdoc,
-				roff_eqn(curp->roff)) :
-			    man_addeqn(curp->man,
-				roff_eqn(curp->roff));
-		else if (curp->man || curp->mdoc)
-			rc = curp->man ?
-			    man_parseln(curp->man,
-				curp->line, ln.buf, of) :
-			    mdoc_parseln(curp->mdoc,
-				curp->line, ln.buf, of);
-
-		if (0 == rc) {
-			assert(MANDOCLEVEL_FATAL <= curp->file_status);
-			break;
-		} else if (2 == rc)
-			break;
-
 		/* Temporary buffers typically are not full. */
 
 		if (0 == start && '\0' == blk.buf[i])
 			break;
 
 		/* Start the next input line. */
 
 		pos = 0;
 	}
 
 	free(ln.buf);
 }
 
 static int
 read_whole_file(struct mparse *curp, const char *file, int fd,
 		struct buf *fb, int *with_mmap)
 {
 	size_t		 off;
 	ssize_t		 ssz;
 
-#ifdef	HAVE_MMAP
+#if HAVE_MMAP
 	struct stat	 st;
 	if (-1 == fstat(fd, &st)) {
 		curp->file_status = MANDOCLEVEL_SYSERR;
 		if (curp->mmsg)
 			(*curp->mmsg)(MANDOCERR_SYSSTAT, curp->file_status,
 			    file, 0, 0, strerror(errno));
 		return(0);
 	}
 
 	/*
 	 * If we're a regular file, try just reading in the whole entry
 	 * via mmap().  This is faster than reading it into blocks, and
 	 * since each file is only a few bytes to begin with, I'm not
 	 * concerned that this is going to tank any machines.
 	 */
 
 	if (S_ISREG(st.st_mode)) {
 		if (st.st_size >= (1U << 31)) {
 			curp->file_status = MANDOCLEVEL_FATAL;
 			if (curp->mmsg)
 				(*curp->mmsg)(MANDOCERR_TOOLARGE,
 				    curp->file_status, file, 0, 0, NULL);
 			return(0);
 		}
 		*with_mmap = 1;
 		fb->sz = (size_t)st.st_size;
 		fb->buf = mmap(NULL, fb->sz, PROT_READ, MAP_SHARED, fd, 0);
 		if (fb->buf != MAP_FAILED)
 			return(1);
 	}
 #endif
 
 	/*
 	 * If this isn't a regular file (like, say, stdin), then we must
 	 * go the old way and just read things in bit by bit.
 	 */
 
 	*with_mmap = 0;
 	off = 0;
 	fb->sz = 0;
 	fb->buf = NULL;
 	for (;;) {
 		if (off == fb->sz) {
 			if (fb->sz == (1U << 31)) {
 				curp->file_status = MANDOCLEVEL_FATAL;
 				if (curp->mmsg)
 					(*curp->mmsg)(MANDOCERR_TOOLARGE,
 					    curp->file_status,
 					    file, 0, 0, NULL);
 				break;
 			}
 			resize_buf(fb, 65536);
 		}
 		ssz = read(fd, fb->buf + (int)off, fb->sz - off);
 		if (ssz == 0) {
 			fb->sz = off;
 			return(1);
 		}
 		if (ssz == -1) {
 			curp->file_status = MANDOCLEVEL_SYSERR;
 			if (curp->mmsg)
 				(*curp->mmsg)(MANDOCERR_SYSREAD,
 				    curp->file_status, file, 0, 0,
 				    strerror(errno));
 			break;
 		}
 		off += (size_t)ssz;
 	}
 
 	free(fb->buf);
 	fb->buf = NULL;
 	return(0);
 }
 
 static void
 mparse_end(struct mparse *curp)
 {
 
 	if (MANDOCLEVEL_FATAL <= curp->file_status)
 		return;
 
 	if (curp->mdoc == NULL &&
 	    curp->man == NULL &&
 	    curp->sodest == NULL) {
 		if (curp->options & MPARSE_MDOC)
 			curp->mdoc = curp->pmdoc;
 		else {
 			if (curp->pman == NULL)
 				curp->pman = man_alloc(curp->roff, curp,
 				    curp->options & MPARSE_QUICK ? 1 : 0);
 			curp->man = curp->pman;
 		}
 	}
 
 	if (curp->mdoc && ! mdoc_endparse(curp->mdoc)) {
 		assert(MANDOCLEVEL_FATAL <= curp->file_status);
 		return;
 	}
 
 	if (curp->man && ! man_endparse(curp->man)) {
 		assert(MANDOCLEVEL_FATAL <= curp->file_status);
 		return;
 	}
 
 	roff_endparse(curp->roff);
 }
 
 static void
 mparse_parse_buffer(struct mparse *curp, struct buf blk, const char *file)
 {
+	struct buf	*svprimary;
 	const char	*svfile;
+	size_t		 offset;
 	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;
+	svprimary = curp->primary;
+	curp->primary = &blk;
 	curp->line = 1;
 	recursion_depth++;
 
-	mparse_buf_r(curp, blk, 1);
+	/* Skip an UTF-8 byte order mark. */
+	if (curp->filenc & MPARSE_UTF8 && blk.sz > 2 &&
+	    (unsigned char)blk.buf[0] == 0xef &&
+	    (unsigned char)blk.buf[1] == 0xbb &&
+	    (unsigned char)blk.buf[2] == 0xbf) {
+		offset = 3;
+		curp->filenc &= ~MPARSE_LATIN1;
+	} else
+		offset = 0;
 
+	mparse_buf_r(curp, blk, offset, 1);
+
 	if (0 == --recursion_depth && MANDOCLEVEL_FATAL > curp->file_status)
 		mparse_end(curp);
 
+	curp->primary = svprimary;
 	curp->file = svfile;
 }
 
 enum mandoclevel
 mparse_readmem(struct mparse *curp, const void *buf, size_t len,
 		const char *file)
 {
 	struct buf blk;
 
 	blk.buf = UNCONST(buf);
 	blk.sz = len;
 
 	mparse_parse_buffer(curp, blk, file);
 	return(curp->file_status);
 }
 
+/*
+ * If a file descriptor is given, use it and assume it points
+ * to the named file.  Otherwise, open the named file.
+ * Read the whole file into memory and call the parsers.
+ * Called recursively when an .so request is encountered.
+ */
 enum mandoclevel
 mparse_readfd(struct mparse *curp, int fd, const char *file)
 {
 	struct buf	 blk;
 	int		 with_mmap;
+	int		 save_filenc;
+	pid_t		 save_child;
 
-	if (-1 == fd && -1 == (fd = open(file, O_RDONLY, 0))) {
-		curp->file_status = MANDOCLEVEL_SYSERR;
-		if (curp->mmsg)
-			(*curp->mmsg)(MANDOCERR_SYSOPEN,
-			    curp->file_status,
-			    file, 0, 0, strerror(errno));
+	save_child = curp->child;
+	if (fd != -1)
+		curp->child = 0;
+	else if (mparse_open(curp, &fd, file) >= MANDOCLEVEL_SYSERR)
 		goto out;
+
+	if (read_whole_file(curp, file, fd, &blk, &with_mmap)) {
+		save_filenc = curp->filenc;
+		curp->filenc = curp->options &
+		    (MPARSE_UTF8 | MPARSE_LATIN1);
+		mparse_parse_buffer(curp, blk, file);
+		curp->filenc = save_filenc;
+#if HAVE_MMAP
+		if (with_mmap)
+			munmap(blk.buf, blk.sz);
+		else
+#endif
+			free(blk.buf);
 	}
 
-	/*
-	 * Run for each opened file; may be called more than once for
-	 * each full parse sequence if the opened file is nested (i.e.,
-	 * from `so').  Simply sucks in the whole file and moves into
-	 * the parse phase for the file.
-	 */
+	if (fd != STDIN_FILENO && close(fd) == -1)
+		perror(file);
 
-	if ( ! read_whole_file(curp, file, fd, &blk, &with_mmap))
+	mparse_wait(curp);
+out:
+	curp->child = save_child;
+	return(curp->file_status);
+}
+
+enum mandoclevel
+mparse_open(struct mparse *curp, int *fd, const char *file)
+{
+	int		  pfd[2];
+	int		  save_errno;
+	char		 *cp;
+	enum mandocerr	  err;
+
+	pfd[1] = -1;
+	curp->file = file;
+
+	/* Unless zipped, try to just open the file. */
+
+	if ((cp = strrchr(file, '.')) == NULL ||
+	    strcmp(cp + 1, "gz")) {
+		curp->child = 0;
+		if ((*fd = open(file, O_RDONLY)) != -1)
+			return(MANDOCLEVEL_OK);
+
+		/* Open failed; try to append ".gz". */
+
+		mandoc_asprintf(&cp, "%s.gz", file);
+		file = cp;
+	} else
+		cp = NULL;
+
+	/* Before forking, make sure the file can be read. */
+
+	save_errno = errno;
+	if (access(file, R_OK) == -1) {
+		if (cp != NULL)
+			errno = save_errno;
+		err = MANDOCERR_SYSOPEN;
 		goto out;
+	}
 
-	mparse_parse_buffer(curp, blk, file);
+	/* Run gunzip(1). */
 
-#ifdef	HAVE_MMAP
-	if (with_mmap)
-		munmap(blk.buf, blk.sz);
-	else
-#endif
-		free(blk.buf);
+	if (pipe(pfd) == -1) {
+		err = MANDOCERR_SYSPIPE;
+		goto out;
+	}
 
-	if (STDIN_FILENO != fd && -1 == close(fd))
-		perror(file);
+	switch (curp->child = fork()) {
+	case -1:
+		err = MANDOCERR_SYSFORK;
+		close(pfd[0]);
+		close(pfd[1]);
+		pfd[1] = -1;
+		break;
+	case 0:
+		close(pfd[0]);
+		if (dup2(pfd[1], STDOUT_FILENO) == -1) {
+			err = MANDOCERR_SYSDUP;
+			break;
+		}
+		execlp("gunzip", "gunzip", "-c", file, NULL);
+		err = MANDOCERR_SYSEXEC;
+		break;
+	default:
+		close(pfd[1]);
+		*fd = pfd[0];
+		return(MANDOCLEVEL_OK);
+	}
+
 out:
+	free(cp);
+	*fd = -1;
+	curp->child = 0;
+	curp->file_status = MANDOCLEVEL_SYSERR;
+	if (curp->mmsg)
+		(*curp->mmsg)(err, curp->file_status, curp->file,
+		    0, 0, strerror(errno));
+	if (pfd[1] != -1)
+		exit(1);
 	return(curp->file_status);
 }
 
+enum mandoclevel
+mparse_wait(struct mparse *curp)
+{
+	int	  status;
+
+	if (curp->child == 0)
+		return(MANDOCLEVEL_OK);
+
+	if (waitpid(curp->child, &status, 0) == -1) {
+		mandoc_msg(MANDOCERR_SYSWAIT, curp, 0, 0,
+		    strerror(errno));
+		curp->file_status = MANDOCLEVEL_SYSERR;
+		return(curp->file_status);
+	}
+	if (WIFSIGNALED(status)) {
+		mandoc_vmsg(MANDOCERR_SYSSIG, curp, 0, 0,
+		    "%d", WTERMSIG(status));
+		curp->file_status = MANDOCLEVEL_SYSERR;
+		return(curp->file_status);
+	}
+	if (WEXITSTATUS(status)) {
+		mandoc_vmsg(MANDOCERR_SYSEXIT, curp, 0, 0,
+		    "%d", WEXITSTATUS(status));
+		curp->file_status = MANDOCLEVEL_SYSERR;
+		return(curp->file_status);
+	}
+	return(MANDOCLEVEL_OK);
+}
+
 struct mparse *
-mparse_alloc(int options, enum mandoclevel wlevel,
-		mandocmsg mmsg, const char *defos)
+mparse_alloc(int options, enum mandoclevel wlevel, mandocmsg mmsg,
+    const struct mchars *mchars, const char *defos)
 {
 	struct mparse	*curp;
 
 	assert(wlevel <= MANDOCLEVEL_FATAL);
 
 	curp = mandoc_calloc(1, sizeof(struct mparse));
 
 	curp->options = options;
 	curp->wlevel = wlevel;
 	curp->mmsg = mmsg;
 	curp->defos = defos;
 
-	curp->roff = roff_alloc(curp, options);
+	curp->mchars = mchars;
+	curp->roff = roff_alloc(curp, curp->mchars, options);
 	if (curp->options & MPARSE_MDOC)
 		curp->pmdoc = mdoc_alloc(
 		    curp->roff, curp, curp->defos,
 		    curp->options & MPARSE_QUICK ? 1 : 0);
 	if (curp->options & MPARSE_MAN)
 		curp->pman = man_alloc(curp->roff, curp,
 		    curp->options & MPARSE_QUICK ? 1 : 0);
 
 	return(curp);
 }
 
 void
 mparse_reset(struct mparse *curp)
 {
 
 	roff_reset(curp->roff);
 
 	if (curp->mdoc)
 		mdoc_reset(curp->mdoc);
 	if (curp->man)
 		man_reset(curp->man);
 	if (curp->secondary)
 		curp->secondary->sz = 0;
 
 	curp->file_status = MANDOCLEVEL_OK;
 	curp->mdoc = NULL;
 	curp->man = NULL;
 
 	free(curp->sodest);
 	curp->sodest = NULL;
 }
 
 void
 mparse_free(struct mparse *curp)
 {
 
 	if (curp->pmdoc)
 		mdoc_free(curp->pmdoc);
 	if (curp->pman)
 		man_free(curp->pman);
 	if (curp->roff)
 		roff_free(curp->roff);
 	if (curp->secondary)
 		free(curp->secondary->buf);
 
 	free(curp->secondary);
 	free(curp->sodest);
 	free(curp);
 }
 
 void
 mparse_result(struct mparse *curp,
 	struct mdoc **mdoc, struct man **man, char **sodest)
 {
 
 	if (sodest && NULL != (*sodest = curp->sodest)) {
 		*mdoc = NULL;
 		*man = NULL;
 		return;
 	}
 	if (mdoc)
 		*mdoc = curp->mdoc;
 	if (man)
 		*man = curp->man;
 }
 
 void
 mandoc_vmsg(enum mandocerr t, struct mparse *m,
 		int ln, int pos, const char *fmt, ...)
 {
 	char		 buf[256];
 	va_list		 ap;
 
 	va_start(ap, fmt);
 	(void)vsnprintf(buf, sizeof(buf), fmt, ap);
 	va_end(ap);
 
 	mandoc_msg(t, m, ln, pos, buf);
 }
 
 void
 mandoc_msg(enum mandocerr er, struct mparse *m,
 		int ln, int col, const char *msg)
 {
 	enum mandoclevel level;
 
 	level = MANDOCLEVEL_FATAL;
 	while (er < mandoclimits[level])
 		level--;
 
 	if (level < m->wlevel)
 		return;
 
 	if (m->mmsg)
 		(*m->mmsg)(er, level, m->file, ln, col, msg);
 
 	if (m->file_status < level)
 		m->file_status = level;
 }
 
 const char *
 mparse_strerror(enum mandocerr er)
 {
 
 	return(mandocerrs[er]);
 }
 
 const char *
 mparse_strlevel(enum mandoclevel lvl)
 {
 	return(mandoclevels[lvl]);
 }
 
 void
 mparse_keep(struct mparse *p)
 {
 
 	assert(NULL == p->secondary);
 	p->secondary = mandoc_calloc(1, sizeof(struct buf));
 }
 
 const char *
 mparse_getkeep(const struct mparse *p)
 {
 
 	assert(p->secondary);
 	return(p->secondary->sz ? p->secondary->buf : NULL);
 }
Index: vendor/mdocml/dist/roff.7
===================================================================
--- vendor/mdocml/dist/roff.7	(revision 275396)
+++ vendor/mdocml/dist/roff.7	(revision 275397)
@@ -1,1429 +1,1442 @@
-.\"	$Id: roff.7,v 1.55 2014/07/07 11:35:06 schwarze Exp $
+.\"	$Id: roff.7,v 1.59 2014/11/19 01:20:25 schwarze Exp $
 .\"
 .\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons 
 .\" Copyright (c) 2010, 2011, 2013, 2014 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" copyright notice and this permission notice appear in all copies.
 .\"
 .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: July 7 2014 $
+.Dd $Mdocdate: November 19 2014 $
 .Dt ROFF 7
 .Os
 .Sh NAME
 .Nm roff
 .Nd roff language reference for mandoc
 .Sh DESCRIPTION
 The
 .Nm roff
 language is a general purpose text formatting language.
 Since traditional implementations of the
 .Xr mdoc 7
 and
 .Xr man 7
 manual formatting languages are based on it,
 many real-world manuals use small numbers of
 .Nm
 requests and escape sequences intermixed with their
 .Xr mdoc 7
 or
 .Xr man 7
 code.
 To properly format such manuals, the
 .Xr mandoc 1
 utility supports a tiny subset of
 .Nm
 requests and escapes.
 Only these requests and escapes supported by
 .Xr mandoc 1
 are documented in the present manual,
 together with the basic language syntax shared by
 .Nm ,
 .Xr mdoc 7 ,
 and
 .Xr man 7 .
 For complete
 .Nm
 manuals, consult the
 .Sx SEE ALSO
 section.
 .Pp
 Input lines beginning with the control character
 .Sq \&.
 are parsed for requests and macros.
 Such lines are called
 .Dq request lines
 or
 .Dq macro lines ,
 respectively.
 Requests change the processing state and manipulate the formatting;
 some macros also define the document structure and produce formatted
 output.
 The single quote
 .Pq Qq \(aq
 is accepted as an alternative control character,
 treated by
 .Xr mandoc 1
 just like
 .Ql \&.
 .Pp
 Lines not beginning with control characters are called
 .Dq text lines .
 They provide free-form text to be printed; the formatting of the text
 depends on the respective processing context.
 .Sh LANGUAGE SYNTAX
 .Nm
 documents may contain only graphable 7-bit ASCII characters, the space
 character, and, in certain circumstances, the tab character.
 The backslash character
 .Sq \e
 indicates the start of an escape sequence, used for example for
 .Sx Comments ,
 .Sx Special Characters ,
 .Sx Predefined Strings ,
 and
 user-defined strings defined using the
 .Sx ds
 request.
 For a listing of escape sequences, consult the
 .Sx ESCAPE SEQUENCE REFERENCE
 below.
 .Ss Comments
 Text following an escaped double-quote
 .Sq \e\(dq ,
 whether in a request, macro, or text line, is ignored to the end of the line.
 A request line beginning with a control character and comment escape
 .Sq \&.\e\(dq
 is also ignored.
 Furthermore, request lines with only a control character and optional
 trailing whitespace are stripped from input.
 .Pp
 Examples:
 .Bd -literal -offset indent -compact
 \&.\e\(dq This is a comment line.
 \&.\e\(dq The next line is ignored:
 \&.
 \&.Sh EXAMPLES \e\(dq This is a comment, too.
 \&example text \e\(dq And so is this.
 .Ed
 .Ss Special Characters
 Special characters are used to encode special glyphs and are rendered
 differently across output media.
 They may occur in request, macro, and text lines.
 Sequences begin with the escape character
 .Sq \e
 followed by either an open-parenthesis
 .Sq \&(
 for two-character sequences; an open-bracket
 .Sq \&[
 for n-character sequences (terminated at a close-bracket
 .Sq \&] ) ;
 or a single one character sequence.
 .Pp
 Examples:
 .Bl -tag -width Ds -offset indent -compact
 .It Li \e(em
 Two-letter em dash escape.
 .It Li \ee
 One-letter backslash escape.
 .El
 .Pp
 See
 .Xr mandoc_char 7
 for a complete list.
 .Ss Text Decoration
 Terms may be text-decorated using the
 .Sq \ef
 escape followed by an indicator: B (bold), I (italic), R (regular), or P
 (revert to previous mode).
 A numerical representation 3, 2, or 1 (bold, italic, and regular,
 respectively) may be used instead.
 The indicator or numerical representative may be preceded by C
 (constant-width), which is ignored.
 .Pp
 The two-character indicator
 .Sq BI
 requests a font that is both bold and italic.
 It may not be portable to old roff implementations.
 .Pp
 Examples:
 .Bl -tag -width Ds -offset indent -compact
 .It Li \efBbold\efR
 Write in \fBbold\fP, then switch to regular font mode.
 .It Li \efIitalic\efP
 Write in \fIitalic\fP, then return to previous font mode.
 .It Li \ef(BIbold italic\efP
 Write in \f(BIbold italic\fP, then return to previous font mode.
 .El
 .Pp
 Text decoration is
 .Em not
 recommended for
 .Xr mdoc 7 ,
 which encourages semantic annotation.
 .Ss Predefined Strings
 Predefined strings, like
 .Sx Special Characters ,
 mark special output glyphs.
 Predefined strings are escaped with the slash-asterisk,
 .Sq \e* :
 single-character
 .Sq \e*X ,
 two-character
 .Sq \e*(XX ,
 and N-character
 .Sq \e*[N] .
 .Pp
 Examples:
 .Bl -tag -width Ds -offset indent -compact
 .It Li \e*(Am
 Two-letter ampersand predefined string.
 .It Li \e*q
 One-letter double-quote predefined string.
 .El
 .Pp
 Predefined strings are not recommended for use,
 as they differ across implementations.
 Those supported by
 .Xr mandoc 1
 are listed in
 .Xr mandoc_char 7 .
 Manuals using these predefined strings are almost certainly not portable.
 .Ss Whitespace
 Whitespace consists of the space character.
 In text lines, whitespace is preserved within a line.
 In request and macro lines, whitespace delimits arguments and is discarded.
 .Pp
 Unescaped trailing spaces are stripped from text line input unless in a
 literal context.
 In general, trailing whitespace on any input line is discouraged for
 reasons of portability.
 In the rare case that a blank character is needed at the end of an
 input line, it may be forced by
 .Sq \e\ \e& .
 .Pp
 Literal space characters can be produced in the output
 using escape sequences.
 In macro lines, they can also be included in arguments using quotation; see
 .Sx MACRO SYNTAX
 for details.
 .Pp
 Blank text lines, which may include whitespace, are only permitted
 within literal contexts.
 If the first character of a text line is a space, that line is printed
 with a leading newline.
 .Ss Scaling Widths
 Many requests and macros support scaled widths for their arguments.
 The syntax for a scaled width is
 .Sq Li [+-]?[0-9]*.[0-9]*[:unit:] ,
 where a decimal must be preceded or followed by at least one digit.
 Negative numbers, while accepted, are truncated to zero.
 .Pp
 The following scaling units are accepted:
 .Pp
 .Bl -tag -width Ds -offset indent -compact
 .It c
 centimetre
 .It i
 inch
 .It P
 pica (~1/6 inch)
 .It p
 point (~1/72 inch)
 .It f
-synonym for
+scale
 .Sq u
+by 65536
 .It v
 default vertical span
 .It m
 width of rendered
 .Sq m
 .Pq em
 character
 .It n
 width of rendered
 .Sq n
 .Pq en
 character
 .It u
-default horizontal span
+default horizontal span for the terminal
 .It M
 mini-em (~1/100 em)
 .El
 .Pp
 Using anything other than
 .Sq m ,
 .Sq n ,
-.Sq u ,
 or
 .Sq v
 is necessarily non-portable across output media.
 See
 .Sx COMPATIBILITY .
 .Pp
 If a scaling unit is not provided, the numerical value is interpreted
 under the default rules of
 .Sq v
 for vertical spaces and
 .Sq u
 for horizontal ones.
 .Pp
 Examples:
 .Bl -tag -width ".Bl -tag -width 2i" -offset indent -compact
 .It Li \&.Bl -tag -width 2i
 two-inch tagged list indentation in
 .Xr mdoc 7
 .It Li \&.HP 2i
 two-inch tagged list indentation in
 .Xr man 7
 .It Li \&.sp 2v
 two vertical spaces
 .El
 .Ss Sentence Spacing
 Each sentence should terminate at the end of an input line.
 By doing this, a formatter will be able to apply the proper amount of
 spacing after the end of sentence (unescaped) period, exclamation mark,
 or question mark followed by zero or more non-sentence closing
 delimiters
 .Po
 .Sq \&) ,
 .Sq \&] ,
 .Sq \&' ,
 .Sq \&"
 .Pc .
 .Pp
 The proper spacing is also intelligently preserved if a sentence ends at
 the boundary of a macro line.
 .Pp
 Examples:
 .Bd -literal -offset indent -compact
 Do not end sentences mid-line like this.  Instead,
 end a sentence like this.
 A macro would end like this:
 \&.Xr mandoc 1 \&.
 .Ed
 .Sh REQUEST SYNTAX
 A request or macro line consists of:
 .Pp
 .Bl -enum -compact
 .It
 the control character
 .Sq \&.
 or
 .Sq \(aq
 at the beginning of the line,
 .It
 optionally an arbitrary amount of whitespace,
 .It
 the name of the request or the macro, which is one word of arbitrary
 length, terminated by whitespace,
 .It
 and zero or more arguments delimited by whitespace.
 .El
 .Pp
 Thus, the following request lines are all equivalent:
 .Bd -literal -offset indent
 \&.ig end
 \&.ig    end
 \&.   ig end
 .Ed
 .Sh MACRO SYNTAX
 Macros are provided by the
 .Xr mdoc 7
 and
 .Xr man 7
 languages and can be defined by the
 .Sx \&de
 request.
 When called, they follow the same syntax as requests, except that
 macro arguments may optionally be quoted by enclosing them
 in double quote characters
 .Pq Sq \(dq .
 Quoted text, even if it contains whitespace or would cause
 a macro invocation when unquoted, is always considered literal text.
 Inside quoted text, pairs of double quote characters
 .Pq Sq Qq
 resolve to single double quote characters.
 .Pp
 To be recognised as the beginning of a quoted argument, the opening
 quote character must be preceded by a space character.
 A quoted argument extends to the next double quote character that is not
 part of a pair, or to the end of the input line, whichever comes earlier.
 Leaving out the terminating double quote character at the end of the line
 is discouraged.
 For clarity, if more arguments follow on the same input line,
 it is recommended to follow the terminating double quote character
 by a space character; in case the next character after the terminating
 double quote character is anything else, it is regarded as the beginning
 of the next, unquoted argument.
 .Pp
 Both in quoted and unquoted arguments, pairs of backslashes
 .Pq Sq \e\e
 resolve to single backslashes.
 In unquoted arguments, space characters can alternatively be included
 by preceding them with a backslash
 .Pq Sq \e\~ ,
 but quoting is usually better for clarity.
 .Pp
 Examples:
 .Bl -tag -width Ds -offset indent -compact
 .It Li .Fn strlen \(dqconst char *s\(dq
 Group arguments
 .Qq const char *s
 into one function argument.
 If unspecified,
 .Qq const ,
 .Qq char ,
 and
 .Qq *s
 would be considered separate arguments.
 .It Li .Op \(dqFl a\(dq
 Consider
 .Qq \&Fl a
 as literal text instead of a flag macro.
 .El
 .Sh REQUEST REFERENCE
 The
 .Xr mandoc 1
 .Nm
 parser recognises the following requests.
 Note that the
 .Nm
 language defines many more requests not implemented in
 .Xr mandoc 1 .
 .Ss \&ad
 Set line adjustment mode.
 This line-scoped request is intended to have one argument to select
 normal, left, right, or centre adjustment for subsequent text.
 Currently, it is ignored including its arguments,
 and the number of arguments is not checked.
 .Ss \&am
 Append to a macro definition.
 The syntax of this request is the same as that of
 .Sx \&de .
 .Ss \&ami
 Append to a macro definition, specifying the macro name indirectly.
 The syntax of this request is the same as that of
 .Sx \&dei .
 .Ss \&am1
 Append to a macro definition, switching roff compatibility mode off
 during macro execution.
 The syntax of this request is the same as that of
 .Sx \&de1 .
 Since
 .Xr mandoc 1
 does not implement
 .Nm
 compatibility mode at all, it handles this request as an alias for
 .Sx \&am .
 .Ss \&as
 Append to a user-defined string.
 The syntax of this request is the same as that of
 .Sx \&ds .
 If a user-defined string with the specified name does not yet exist,
 it is set to the empty string before appending.
 .Ss \&cc
 Changes the control character.
 Its syntax is as follows:
 .Bd -literal -offset indent
 .Pf . Cm \&cc Op Ar c
 .Ed
 .Pp
 If
 .Ar c
 is not specified, the control character is reset to
 .Sq \&. .
 Trailing characters are ignored.
 .Ss \&ce
 Center some lines.
 This line-scoped request is intended to take one integer argument,
 specifying how many lines to center.
 Currently, it is ignored including its arguments, and the number
 of arguments is not checked.
 .Ss \&de
 Define a
 .Nm
 macro.
 Its syntax can be either
 .Bd -literal -offset indent
 .Pf . Cm \&de Ar name
 .Ar macro definition
 \&..
 .Ed
 .Pp
 or
 .Bd -literal -offset indent
 .Pf . Cm \&de Ar name Ar end
 .Ar macro definition
 .Pf . Ar end
 .Ed
 .Pp
 Both forms define or redefine the macro
 .Ar name
 to represent the
 .Ar macro definition ,
 which may consist of one or more input lines, including the newline
 characters terminating each line, optionally containing calls to
 .Nm
 requests,
 .Nm
 macros or high-level macros like
 .Xr man 7
 or
 .Xr mdoc 7
 macros, whichever applies to the document in question.
 .Pp
 Specifying a custom
 .Ar end
 macro works in the same way as for
 .Sx \&ig ;
 namely, the call to
 .Sq Pf . Ar end
 first ends the
 .Ar macro definition ,
 and after that, it is also evaluated as a
 .Nm
 request or
 .Nm
 macro, but not as a high-level macro.
 .Pp
 The macro can be invoked later using the syntax
 .Pp
 .D1 Pf . Ar name Op Ar argument Op Ar argument ...
 .Pp
 Regarding argument parsing, see
 .Sx MACRO SYNTAX
 above.
 .Pp
 The line invoking the macro will be replaced
 in the input stream by the
 .Ar macro definition ,
 replacing all occurrences of
 .No \e\e$ Ns Ar N ,
 where
 .Ar N
 is a digit, by the
 .Ar N Ns th Ar argument .
 For example,
 .Bd -literal -offset indent
 \&.de ZN
 \efI\e^\e\e$1\e^\efP\e\e$2
 \&..
 \&.ZN XtFree .
 .Ed
 .Pp
 produces
 .Pp
 .D1 \efI\e^XtFree\e^\efP.
 .Pp
 in the input stream, and thus in the output: \fI\^XtFree\^\fP.
 .Pp
 Since macros and user-defined strings share a common string table,
 defining a macro
 .Ar name
 clobbers the user-defined string
 .Ar name ,
 and the
 .Ar macro definition
 can also be printed using the
 .Sq \e*
 string interpolation syntax described below
 .Sx ds ,
 but this is rarely useful because every macro definition contains at least
 one explicit newline character.
 .Pp
 In order to prevent endless recursion, both groff and
 .Xr mandoc 1
 limit the stack depth for expanding macros and strings
 to a large, but finite number.
 Do not rely on the exact value of this limit.
 .Ss \&dei
 Define a
 .Nm
 macro, specifying the macro name indirectly.
 The syntax of this request is the same as that of
 .Sx \&de .
 The request
 .Pp
 .D1 Pf . Cm \&dei Ar name Op Ar end
 .Pp
 has the same effect as:
 .Pp
 .D1 Pf . Cm \&de No \e* Ns Bo Ar name Bc Op \e* Ns Bq Ar end
 .Ss \&de1
 Define a
 .Nm
 macro that will be executed with
 .Nm
 compatibility mode switched off during macro execution.
 This is a GNU extension not available in traditional
 .Nm
 implementations and not even in older versions of groff.
 Since
 .Xr mandoc 1
 does not implement
 .Nm
 compatibility mode at all, it handles this request as an alias for
 .Sx \&de .
 .Ss \&ds
 Define a user-defined string.
 Its syntax is as follows:
 .Pp
 .D1 Pf . Cm \&ds Ar name Oo \(dq Oc Ns Ar string
 .Pp
 The
 .Ar name
 and
 .Ar string
 arguments are space-separated.
 If the
 .Ar string
 begins with a double-quote character, that character will not be part
 of the string.
 All remaining characters on the input line form the
 .Ar string ,
 including whitespace and double-quote characters, even trailing ones.
 .Pp
 The
 .Ar string
 can be interpolated into subsequent text by using
 .No \e* Ns Bq Ar name
 for a
 .Ar name
 of arbitrary length, or \e*(NN or \e*N if the length of
 .Ar name
 is two or one characters, respectively.
 Interpolation can be prevented by escaping the leading backslash;
 that is, an asterisk preceded by an even number of backslashes
 does not trigger string interpolation.
 .Pp
 Since user-defined strings and macros share a common string table,
 defining a string
 .Ar name
 clobbers the macro
 .Ar name ,
 and the
 .Ar name
 used for defining a string can also be invoked as a macro,
 in which case the following input line will be appended to the
 .Ar string ,
 forming a new input line passed to the
 .Nm
 parser.
 For example,
 .Bd -literal -offset indent
 \&.ds badidea .S
 \&.badidea
 H SYNOPSIS
 .Ed
 .Pp
 invokes the
 .Cm SH
 macro when used in a
 .Xr man 7
 document.
 Such abuse is of course strongly discouraged.
 .Ss \&el
 The
 .Qq else
 half of an if/else conditional.
 Pops a result off the stack of conditional evaluations pushed by
 .Sx \&ie
 and uses it as its conditional.
 If no stack entries are present (e.g., due to no prior
 .Sx \&ie
 calls)
 then false is assumed.
 The syntax of this request is similar to
 .Sx \&if
 except that the conditional is missing.
 .Ss \&EN
 End an equation block.
 See
 .Sx \&EQ .
 .Ss \&EQ
 Begin an equation block.
 See
 .Xr eqn 7
 for a description of the equation language.
 .Ss \&fam
 Change the font family.
 This line-scoped request is intended to have one argument specifying
 the font family to be selected.
 It is a groff extension, and currently, it is ignored including its
 arguments, and the number of arguments is not checked.
 .Ss \&ft
 Change the font.
 Its syntax is as follows:
 .Pp
 .D1 Pf . Cm \&ft Op Ar font
 .Pp
 The following
 .Ar font
 arguments are supported:
 .Bl -tag -width 4n -offset indent
 .It Cm B , BI , 3 , 4
 switches to
 .Sy bold
 font
 .It Cm I , 2
 switches to
 .Em underlined
 font
 .It Cm R , CW , 1
 switches to normal font
 .It Cm P No "or no argument"
 switches back to the previous font
 .El
 .Pp
 This request takes effect only locally, may be overridden by macros
 and escape sequences, and is only supported in
 .Xr man 7
 for now.
 .Ss \&hw
 Specify hyphenation points in words.
 This line-scoped request is currently ignored.
 .Ss \&hy
 Set automatic hyphenation mode.
 This line-scoped request is currently ignored.
 .Ss \&ie
 The
 .Qq if
 half of an if/else conditional.
 The result of the conditional is pushed into a stack used by subsequent
 invocations of
 .Sx \&el ,
 which may be separated by any intervening input (or not exist at all).
 Its syntax is equivalent to
 .Sx \&if .
 .Ss \&if
 Begins a conditional.
 This request has the following syntax:
 .Bd -literal -offset indent
 \&.if COND BODY
 .Ed
 .Bd -literal -offset indent
 \&.if COND \e{BODY
 BODY...\e}
 .Ed
 .Bd -literal -offset indent
 \&.if COND \e{\e
 BODY...
 \&.\e}
 .Ed
 .Pp
 COND is a conditional statement.
 Currently,
 .Xr mandoc 1
 supports the following subset of roff conditionals:
 .Bl -bullet
 .It
 If
 .Sq \&!
 is prefixed to COND, the condition is logically inverted.
 .It
 If the first character of COND is
 .Sq n
 .Pq nroff mode
 or
 .Sq o
 .Pq odd page ,
 COND evaluates to true.
 .It
 If the first character of COND is
 .Sq c
 .Pq character available ,
 .Sq d
 .Pq string defined ,
 .Sq e
 .Pq even page ,
 .Sq r
 .Pq register accessed ,
-or
 .Sq t
 .Pq troff mode ,
+or
+.Sq v
+.Pq vroff mode ,
 COND evaluates to false.
 .It
 If COND starts with a parenthesis or with an optionally signed
 integer number, it is evaluated according to the rules of
 .Sx Numerical expressions
 explained below.
-It evaluates to true if the the result is positive,
+It evaluates to true if the result is positive,
 or to false if the result is zero or negative.
 .It
 Otherwise, the first character of COND is regarded as a delimiter
 and COND evaluates to true if the string extending from its first
 to its second occurrence is equal to the string extending from its
 second to its third occurrence.
 .It
 If COND cannot be parsed, it evaluates to false.
 .El
 .Pp
 If a conditional is false, its children are not processed, but are
 syntactically interpreted to preserve the integrity of the input
 document.
 Thus,
 .Pp
 .D1 \&.if t .ig
 .Pp
 will discard the
 .Sq \&.ig ,
 which may lead to interesting results, but
 .Pp
 .D1 \&.if t .if t \e{\e
 .Pp
 will continue to syntactically interpret to the block close of the final
 conditional.
 Sub-conditionals, in this case, obviously inherit the truth value of
 the parent.
 .Pp
 If the BODY section is begun by an escaped brace
 .Sq \e{ ,
 scope continues until the end of the input line containing the
 matching closing-brace escape sequence
 .Sq \e} .
 If the BODY is not enclosed in braces, scope continues until
 the end of the line.
 If the COND is followed by a BODY on the same line, whether after a
 brace or not, then requests and macros
 .Em must
 begin with a control character.
 It is generally more intuitive, in this case, to write
 .Bd -literal -offset indent
 \&.if COND \e{\e
 \&.foo
 bar
 \&.\e}
 .Ed
 .Pp
 than having the request or macro follow as
 .Pp
 .D1 \&.if COND \e{ .foo
 .Pp
 The scope of a conditional is always parsed, but only executed if the
 conditional evaluates to true.
 .Pp
 Note that the
 .Sq \e}
 is converted into a zero-width escape sequence if not passed as a
 standalone macro
 .Sq \&.\e} .
 For example,
 .Pp
 .D1 \&.Fl a \e} b
 .Pp
 will result in
 .Sq \e}
 being considered an argument of the
 .Sq \&Fl
 macro.
 .Ss \&ig
 Ignore input.
 Its syntax can be either
 .Bd -literal -offset indent
 .Pf . Cm \&ig
 .Ar ignored text
 \&..
 .Ed
 .Pp
 or
 .Bd -literal -offset indent
 .Pf . Cm \&ig Ar end
 .Ar ignored text
 .Pf . Ar end
 .Ed
 .Pp
 In the first case, input is ignored until a
 .Sq \&..
 request is encountered on its own line.
 In the second case, input is ignored until the specified
 .Sq Pf . Ar end
 macro is encountered.
 Do not use the escape character
 .Sq \e
 anywhere in the definition of
 .Ar end ;
 it would cause very strange behaviour.
 .Pp
 When the
 .Ar end
 macro is a roff request or a roff macro, like in
 .Pp
 .D1 \&.ig if
 .Pp
 the subsequent invocation of
 .Sx \&if
 will first terminate the
 .Ar ignored text ,
 then be invoked as usual.
 Otherwise, it only terminates the
 .Ar ignored text ,
 and arguments following it or the
 .Sq \&..
 request are discarded.
 .Ss \&ll
 Change the output line length.
 Its syntax is as follows:
 .Pp
 .D1 Pf . Cm \&ll Op Oo +|- Oc Ns Ar width
 .Pp
 If the
 .Ar width
 argument is omitted, the line length is reset to its previous value.
 The default setting for terminal output is 78n.
 If a sign is given, the line length is added to or subtracted from;
 otherwise, it is set to the provided value.
 Using this request in new manuals is discouraged for several reasons,
 among others because it overrides the
 .Xr mandoc 1
 .Fl O Cm width
 command line option.
 .Ss \&ne
 Declare the need for the specified minimum vertical space
 before the next trap or the bottom of the page.
 This line-scoped request is currently ignored.
 .Ss \&nh
 Turn off automatic hyphenation mode.
 This line-scoped request is currently ignored.
 .Ss \&nr
 Define or change a register.
 A register is an arbitrary string value that defines some sort of state,
 which influences parsing and/or formatting.
 Its syntax is as follows:
 .Pp
 .D1 Pf \. Cm \&nr Ar name Oo +|- Oc Ns Ar expression
 .Pp
 For the syntax of
 .Ar expression ,
 see
 .Sx Numerical expressions
 below.
 If it is prefixed by a sign, the register will be
 incremented or decremented instead of assigned to.
 .Pp
 The following register
 .Ar name
 is handled specially:
 .Bl -tag -width Ds
 .It Cm nS
 If set to a positive integer value, certain
 .Xr mdoc 7
 macros will behave in the same way as in the
 .Em SYNOPSIS
 section.
 If set to 0, these macros will behave in the same way as outside the
 .Em SYNOPSIS
 section, even when called within the
 .Em SYNOPSIS
 section itself.
 Note that starting a new
 .Xr mdoc 7
 section with the
 .Cm \&Sh
 macro will reset this register.
 .El
 .Ss \&ns
 Turn on no-space mode.
 This line-scoped request is intended to take no arguments.
 Currently, it is ignored including its arguments,
 and the number of arguments is not checked.
+.Ss \&pl
+Change page length.
+This line-scoped request is intended to take one height argument.
+Currently, it is ignored including its arguments,
+and the number of arguments is not checked.
 .Ss \&ps
 Change point size.
 This line-scoped request is intended to take one numerical argument.
 Currently, it is ignored including its arguments,
 and the number of arguments is not checked.
 .Ss \&rm
 Remove a request, macro or string.
 Its syntax is as follows:
 .Pp
 .D1 Pf \. Cm \&rm Ar name
 .Ss \&rr
 Remove a register.
 Its syntax is as follows:
 .Pp
 .D1 Pf \. Cm \&rr Ar name
 .Ss \&so
 Include a source file.
 Its syntax is as follows:
 .Pp
 .D1 Pf \. Cm \&so Ar file
 .Pp
 The
 .Ar file
 will be read and its contents processed as input in place of the
 .Sq \&.so
 request line.
 To avoid inadvertent inclusion of unrelated files,
 .Xr mandoc 1
 only accepts relative paths not containing the strings
 .Qq ../
 and
 .Qq /.. .
 .Pp
 This request requires
 .Xr man 1
 to change to the right directory before calling
 .Xr mandoc 1 ,
 per convention to the root of the manual tree.
 Typical usage looks like:
 .Pp
 .Dl \&.so man3/Xcursor.3
 .Pp
 As the whole concept is rather fragile, the use of
 .Sx \&so
 is discouraged.
 Use
 .Xr ln 1
 instead.
 .Ss \&ta
 Set tab stops.
 This line-scoped request can take an arbitrary number of arguments.
 Currently, it is ignored including its arguments.
 .Ss \&tr
 Output character translation.
 Its syntax is as follows:
 .Pp
 .D1 Pf \. Cm \&tr Ar [ab]+
 .Pp
 Pairs of
 .Ar ab
 characters are replaced
 .Ar ( a
 for
 .Ar b ) .
 Replacement (or origin) characters may also be character escapes; thus,
 .Pp
 .Dl tr \e(xx\e(yy
 .Pp
 replaces all invocations of \e(xx with \e(yy.
 .Ss \&T&
 Re-start a table layout, retaining the options of the prior table
 invocation.
 See
 .Sx \&TS .
 .Ss \&TE
 End a table context.
 See
 .Sx \&TS .
 .Ss \&TS
 Begin a table, which formats input in aligned rows and columns.
 See
 .Xr tbl 7
 for a description of the tbl language.
 .Ss Numerical expressions
 The
 .Sx \&nr ,
 .Sx \&if ,
 and
 .Sx \&ie
 requests accept integer numerical expressions as arguments.
 These are always evaluated using the C
 .Vt int
 type; integer overflow works the same way as in the C language.
 Numbers consist of an arbitrary number of digits
 .Sq 0
 to
 .Sq 9
 prefixed by an optional sign
 .Sq +
 or
 .Sq - .
 .Pp
 The following binary operators are implemented.
 Unless otherwise stated, they behave as in the C language:
 .Pp
 .Bl -tag -width 2n -compact
 .It Ic +
 addition
 .It Ic -
 subtraction
 .It Ic *
 multiplication
 .It Ic /
 division
 .It Ic %
 remainder of division
 .It Ic <
 less than
 .It Ic >
 greater than
 .It Ic ==
 equal to
 .It Ic =
 equal to, same effect as
 .Ic ==
 (this differs from C)
 .It Ic <=
 less than or equal to
 .It Ic >=
 greater than or equal to
 .It Ic <>
 not equal to (corresponds to C
 .Ic != ;
 this one is of limited portability, it is supported by Heirloom roff,
 but not by groff)
 .It Ic &
 logical and (corresponds to C
 .Ic && )
 .It Ic \&:
 logical or (corresponds to C
 .Ic \&|| )
 .It Ic ?
 maximum (not available in C)
 .El
 .Pp
 There is no concept of precendence; evaluation proceeds from left to right,
 except when subexpressions are enclosed in parantheses.
 Inside parentheses, whitespace is ignored.
 .Sh ESCAPE SEQUENCE REFERENCE
 The
 .Xr mandoc 1
 .Nm
 parser recognises the following escape sequences.
 Note that the
 .Nm
 language defines more escape sequences not implemented in
 .Xr mandoc 1 .
 In
 .Xr mdoc 7
 and
 .Xr man 7
 documents, using escape sequences is discouraged except for those
 described in the
 .Sx LANGUAGE SYNTAX
 section above.
 .Pp
 A backslash followed by any character not listed here
 simply prints that character itself.
 .Ss \e
 A backslash at the end of an input line can be used to continue the
 logical input line on the next physical input line, joining the text
 on both lines together as if it were on a single input line.
 .Ss \e
 The escape sequence backslash-space
 .Pq Sq \e\ \&
 is an unpaddable space-sized non-breaking space character; see
 .Sx Whitespace .
 .Ss \e\(dq
 The rest of the input line is treated as
 .Sx Comments .
 .Ss \e%
 Hyphenation allowed at this point of the word; ignored by
 .Xr mandoc 1 .
 .Ss \e&
 Non-printing zero-width character; see
 .Sx Whitespace .
 .Ss \e\(aq
 Acute accent special character; use
 .Sq \e(aa
 instead.
 .Ss \e( Ns Ar cc
 .Sx Special Characters
 with two-letter names, see
 .Xr mandoc_char 7 .
 .Ss \e*[ Ns Ar name ]
 Interpolate the string with the
 .Ar name ;
 see
 .Sx Predefined Strings
 and
 .Sx ds .
 For short names, there are variants
 .No \e* Ns Ar c
 and
 .No \e*( Ns Ar cc .
 .Ss \e-
 Special character
 .Dq mathematical minus sign .
 .Ss \e[ Ns Ar name ]
 .Sx Special Characters
 with names of arbitrary length, see
 .Xr mandoc_char 7 .
 .Ss \e^
 One-twelfth em half-narrow space character, effectively zero-width in
 .Xr mandoc 1 .
 .Ss \e`
 Grave accent special character; use
 .Sq \e(ga
 instead.
 .Ss \e{
 Begin conditional input; see
 .Sx if .
 .Ss \e\(ba
 One-sixth em narrow space character, effectively zero-width in
 .Xr mandoc 1 .
 .Ss \e}
 End conditional input; see
 .Sx if .
 .Ss \e~
 Paddable non-breaking space character.
 .Ss \e0
 Digit width space character.
 .Ss \eA\(aq Ns Ar string Ns \(aq
 Anchor definition; ignored by
 .Xr mandoc 1 .
 .Ss \eB\(aq Ns Ar string Ns \(aq
 Interpolate
 .Sq 1
 if
 .Ar string
 conforms to the syntax of
 .Sx Numerical expressions
 explained above and
 .Sq 0
 otherwise.
 .Ss \eb\(aq Ns Ar string Ns \(aq
 Bracket building function; ignored by
 .Xr mandoc 1 .
 .Ss \eC\(aq Ns Ar name Ns \(aq
 .Sx Special Characters
 with names of arbitrary length.
 .Ss \ec
 Interrupt text processing to insert requests or macros; ignored by
 .Xr mandoc 1 .
 .Ss \eD\(aq Ns Ar string Ns \(aq
 Draw graphics function; ignored by
 .Xr mandoc 1 .
 .Ss \ed
 Move down by half a line; ignored by
 .Xr mandoc 1 .
 .Ss \ee
 Backslash special character.
 .Ss \eF[ Ns Ar name ]
 Switch font family (groff extension); ignored by
 .Xr mandoc 1 .
 For short names, there are variants
 .No \eF Ns Ar c
 and
 .No \eF( Ns Ar cc .
 .Ss \ef[ Ns Ar name ]
 Switch to the font
 .Ar name ,
 see
 .Sx Text Decoration .
 For short names, there are variants
 .No \ef Ns Ar c
 and
 .No \ef( Ns Ar cc .
 .Ss \eg[ Ns Ar name ]
 Interpolate the format of a number register; ignored by
 .Xr mandoc 1 .
 For short names, there are variants
 .No \eg Ns Ar c
 and
 .No \eg( Ns Ar cc .
 .Ss \eH\(aq Ns Oo +|- Oc Ns Ar number Ns \(aq
 Set the height of the current font; ignored by
 .Xr mandoc 1 .
 .Ss \eh\(aq Ns Ar number Ns \(aq
 Horizontal motion; ignored by
 .Xr mandoc 1 .
 .Ss \ek[ Ns Ar name ]
 Mark horizontal input place in register; ignored by
 .Xr mandoc 1 .
 For short names, there are variants
 .No \ek Ns Ar c
 and
 .No \ek( Ns Ar cc .
 .Ss \eL\(aq Ns Ar number Ns Oo Ar c Oc Ns \(aq
 Vertical line drawing function; ignored by
 .Xr mandoc 1 .
 .Ss \el\(aq Ns Ar number Ns Oo Ar c Oc Ns \(aq
 Horizontal line drawing function; ignored by
 .Xr mandoc 1 .
 .Ss \eM[ Ns Ar name ]
 Set fill (background) color (groff extension); ignored by
 .Xr mandoc 1 .
 For short names, there are variants
 .No \eM Ns Ar c
 and
 .No \eM( Ns Ar cc .
 .Ss \em[ Ns Ar name ]
 Set glyph drawing color (groff extension); ignored by
 .Xr mandoc 1 .
 For short names, there are variants
 .No \em Ns Ar c
 and
 .No \em( Ns Ar cc .
 .Ss \eN\(aq Ns Ar number Ns \(aq
 Character
 .Ar number
 on the current font.
 .Ss \en[ Ns Ar name ]
 Interpolate the number register
 .Ar name .
 For short names, there are variants
 .No \en Ns Ar c
 and
 .No \en( Ns Ar cc .
 .Ss \eo\(aq Ns Ar string Ns \(aq
 Overstrike
 .Ar string ;
 ignored by
 .Xr mandoc 1 .
 .Ss \eR\(aq Ns Ar name Oo +|- Oc Ns Ar number Ns \(aq
 Set number register; ignored by
 .Xr mandoc 1 .
 .Ss \eS\(aq Ns Ar number Ns \(aq
 Slant output; ignored by
 .Xr mandoc 1 .
 .Ss \es\(aq Ns Oo +|- Oc Ns Ar number Ns \(aq
 Change point size; ignored by
 .Xr mandoc 1 .
 Alternative forms
 .No \es Ns Oo +|- Oc Ns Ar n ,
 .No \es Ns Oo +|- Oc Ns \(aq Ns Ar number Ns \(aq ,
 .No \es Ns [ Oo +|- Oc Ns Ar number ] ,
 and
 .No \es Ns Oo +|- Oc Ns [ Ar number Ns ]
 are also parsed and ignored.
 .Ss \et
 Horizontal tab; ignored by
 .Xr mandoc 1 .
 .Ss \eu
 Move up by half a line; ignored by
 .Xr mandoc 1 .
 .Ss \eV[ Ns Ar name ]
 Interpolate an environment variable; ignored by
 .Xr mandoc 1 .
 For short names, there are variants
 .No \eV Ns Ar c
 and
 .No \eV( Ns Ar cc .
 .Ss \ev\(aq Ns Ar number Ns \(aq
 Vertical motion; ignored by
 .Xr mandoc 1 .
 .Ss \ew\(aq Ns Ar string Ns \(aq
 Interpolate the width of the
 .Ar string .
 The
 .Xr mandoc 1
 implementation assumes that after expansion of user-defined strings, the
 .Ar string
 only contains normal characters, no escape sequences, and that each
 character has a width of 24 basic units.
 .Ss \eX\(aq Ns Ar string Ns \(aq
 Output
 .Ar string
 as device control function; ignored in nroff mode and by
 .Xr mandoc 1 .
 .Ss \ex\(aq Ns Ar number Ns \(aq
 Extra line space function; ignored by
 .Xr mandoc 1 .
 .Ss \eY[ Ns Ar name ]
 Output a string as a device control function; ignored in nroff mode and by
 .Xr mandoc 1 .
 For short names, there are variants
 .No \eY Ns Ar c
 and
 .No \eY( Ns Ar cc .
 .Ss \eZ\(aq Ns Ar string Ns \(aq
 Print
 .Ar string
 with zero width and height; ignored by
 .Xr mandoc 1 .
 .Ss \ez
 Output the next character without advancing the cursor position;
 approximated in
 .Xr mandoc 1
 by simply skipping the next character.
 .Sh COMPATIBILITY
 This section documents compatibility between mandoc and other
 .Nm
 implementations, at this time limited to GNU troff
 .Pq Qq groff .
 The term
 .Qq historic groff
 refers to groff version 1.15.
 .Pp
 .Bl -dash -compact
+.It
+The
+.Sq u
+scaling unit is the default terminal unit.
+In traditional troff systems, this unit would change depending on the
+output media.
 .It
 In mandoc, the
 .Sx \&EQ ,
 .Sx \&TE ,
 .Sx \&TS ,
 and
 .Sx \&T& ,
 macros are considered regular macros.
 In all other
 .Nm
 implementations, these are special macros that must be specified without
 spacing between the control character (which must be a period) and the
 macro name.
 .It
 The
 .Cm nS
 register is only compatible with OpenBSD's groff-1.15.
 .It
 Historic groff did not accept white-space before a custom
 .Ar end
 macro for the
 .Sx \&ig
 request.
 .It
 The
 .Sx \&if
 and family would print funny white-spaces with historic groff when
 using the next-line syntax.
 .El
 .Sh SEE ALSO
 .Xr mandoc 1 ,
 .Xr eqn 7 ,
 .Xr man 7 ,
 .Xr mandoc_char 7 ,
 .Xr mdoc 7 ,
 .Xr tbl 7
 .Rs
 .%A Joseph F. Ossanna
 .%A Brian W. Kernighan
 .%I AT&T Bell Laboratories
 .%T Troff User's Manual
 .%R Computing Science Technical Report
 .%N 54
 .%C Murray Hill, New Jersey
 .%D 1976 and 1992
 .%U http://www.kohala.com/start/troff/cstr54.ps
 .Re
 .Rs
 .%A Joseph F. Ossanna
 .%A Brian W. Kernighan
 .%A Gunnar Ritter
 .%T Heirloom Documentation Tools Nroff/Troff User's Manual
 .%D September 17, 2007
 .%U http://heirloom.sourceforge.net/doctools/troff.pdf
 .Re
 .Sh HISTORY
 The RUNOFF typesetting system, whose input forms the basis for
 .Nm ,
 was written in MAD and FAP for the CTSS operating system by Jerome E.
 Saltzer in 1964.
 Doug McIlroy rewrote it in BCPL in 1969, renaming it
 .Nm .
 Dennis M. Ritchie rewrote McIlroy's
 .Nm
 in PDP-11 assembly for
 .At v1 ,
 Joseph F. Ossanna improved roff and renamed it nroff
 for
 .At v2 ,
 then ported nroff to C as troff, which Brian W. Kernighan released with
 .At v7 .
 In 1989, James Clarke re-implemented troff in C++, naming it groff.
 .Sh AUTHORS
 .An -nosplit
 This
 .Nm
 reference was written by
 .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv
 and
 .An Ingo Schwarze Aq Mt schwarze@openbsd.org .
Index: vendor/mdocml/dist/roff.c
===================================================================
--- vendor/mdocml/dist/roff.c	(revision 275396)
+++ vendor/mdocml/dist/roff.c	(revision 275397)
@@ -1,2343 +1,2448 @@
-/*	$Id: roff.c,v 1.224 2014/08/01 17:27:44 schwarze Exp $ */
+/*	$Id: roff.c,v 1.239 2014/11/19 01:20:25 schwarze Exp $ */
 /*
  * Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons 
  * Copyright (c) 2010-2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
-#include "libroff.h"
 #include "libmandoc.h"
+#include "libroff.h"
 
 /* Maximum number of nested if-else conditionals. */
 #define	RSTACK_MAX	128
 
 /* Maximum number of string expansions per line, to break infinite loops. */
 #define	EXPAND_LIMIT	1000
 
 enum	rofft {
 	ROFF_ad,
 	ROFF_am,
 	ROFF_ami,
 	ROFF_am1,
 	ROFF_as,
 	ROFF_cc,
 	ROFF_ce,
 	ROFF_de,
 	ROFF_dei,
 	ROFF_de1,
 	ROFF_ds,
 	ROFF_el,
 	ROFF_fam,
 	ROFF_hw,
 	ROFF_hy,
 	ROFF_ie,
 	ROFF_if,
 	ROFF_ig,
 	ROFF_it,
 	ROFF_ne,
 	ROFF_nh,
 	ROFF_nr,
 	ROFF_ns,
+	ROFF_pl,
 	ROFF_ps,
 	ROFF_rm,
 	ROFF_rr,
 	ROFF_so,
 	ROFF_ta,
 	ROFF_tr,
 	ROFF_Dd,
 	ROFF_TH,
 	ROFF_TS,
 	ROFF_TE,
 	ROFF_T_,
 	ROFF_EQ,
 	ROFF_EN,
 	ROFF_cblock,
 	ROFF_USERDEF,
 	ROFF_MAX
 };
 
 /*
  * An incredibly-simple string buffer.
  */
 struct	roffstr {
 	char		*p; /* nil-terminated buffer */
 	size_t		 sz; /* saved strlen(p) */
 };
 
 /*
  * A key-value roffstr pair as part of a singly-linked list.
  */
 struct	roffkv {
 	struct roffstr	 key;
 	struct roffstr	 val;
 	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 {
 	struct mparse	*parse; /* parse point */
+	const struct mchars *mchars; /* character table */
 	struct roffnode	*last; /* leaf of stack */
 	int		*rstack; /* stack of inverted `ie' values */
 	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') */
 	const char	*current_string; /* value of last called user macro */
 	struct tbl_node	*first_tbl; /* first table parsed */
 	struct tbl_node	*last_tbl; /* last table parsed */
 	struct tbl_node	*tbl; /* current table being parsed */
 	struct eqn_node	*last_eqn; /* last equation parsed */
 	struct eqn_node	*first_eqn; /* first equation parsed */
 	struct eqn_node	*eqn; /* current equation being parsed */
+	int		 eqn_inline; /* current equation is inline */
 	int		 options; /* parse options */
 	int		 rstacksz; /* current size limit of rstack */
 	int		 rstackpos; /* position in rstack */
+	int		 format; /* current file in mdoc or man format */
 	char		 control; /* control character */
 };
 
 struct	roffnode {
 	enum rofft	 tok; /* type of node */
 	struct roffnode	*parent; /* up one in stack */
 	int		 line; /* parse line */
 	int		 col; /* parse col */
 	char		*name; /* node name, e.g. macro name */
 	char		*end; /* end-rules: custom token */
 	int		 endspan; /* end-rules: next-line or infty */
 	int		 rule; /* current evaluation rule */
 };
 
 #define	ROFF_ARGS	 struct roff *r, /* parse ctx */ \
 			 enum rofft tok, /* tok of macro */ \
-			 char **bufp, /* input buffer */ \
-			 size_t *szp, /* size of input buffer */ \
+			 struct buf *buf, /* input buffer */ \
 			 int ln, /* parse line */ \
 			 int ppos, /* original pos in buffer */ \
 			 int pos, /* current pos in buffer */ \
 			 int *offs /* reset offset of buffer data */
 
 typedef	enum rofferr (*roffproc)(ROFF_ARGS);
 
 struct	roffmac {
 	const char	*name; /* macro name */
 	roffproc	 proc; /* process new macro */
 	roffproc	 text; /* process as child text of macro */
 	roffproc	 sub; /* process as child of macro */
 	int		 flags;
 #define	ROFFMAC_STRUCT	(1 << 0) /* always interpret */
 	struct roffmac	*next;
 };
 
 struct	predef {
 	const char	*name; /* predefined input name */
 	const char	*str; /* replacement symbol */
 };
 
 #define	PREDEF(__name, __str) \
 	{ (__name), (__str) },
 
 static	enum rofft	 roffhash_find(const char *, size_t);
 static	void		 roffhash_init(void);
 static	void		 roffnode_cleanscope(struct roff *);
 static	void		 roffnode_pop(struct roff *);
 static	void		 roffnode_push(struct roff *, enum rofft,
 				const char *, int, int);
 static	enum rofferr	 roff_block(ROFF_ARGS);
 static	enum rofferr	 roff_block_text(ROFF_ARGS);
 static	enum rofferr	 roff_block_sub(ROFF_ARGS);
 static	enum rofferr	 roff_cblock(ROFF_ARGS);
 static	enum rofferr	 roff_cc(ROFF_ARGS);
 static	void		 roff_ccond(struct roff *, int, int);
 static	enum rofferr	 roff_cond(ROFF_ARGS);
 static	enum rofferr	 roff_cond_text(ROFF_ARGS);
 static	enum rofferr	 roff_cond_sub(ROFF_ARGS);
 static	enum rofferr	 roff_ds(ROFF_ARGS);
-static	int		 roff_evalcond(const char *, int *);
-static	int		 roff_evalnum(const char *, int *, int *, int);
-static	int		 roff_evalpar(const char *, int *, int *);
+static	enum rofferr	 roff_eqndelim(struct roff *, struct buf *, int);
+static	int		 roff_evalcond(struct roff *r, int,
+				const char *, int *);
+static	int		 roff_evalnum(struct roff *, int,
+				const char *, int *, int *, int);
+static	int		 roff_evalpar(struct roff *, int,
+				const char *, int *, int *);
 static	int		 roff_evalstrcond(const char *, int *);
 static	void		 roff_free1(struct roff *);
 static	void		 roff_freereg(struct roffreg *);
 static	void		 roff_freestr(struct roffkv *);
 static	size_t		 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	int		 roff_getregro(const char *name);
 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 *, char *, int *,
 				int, int);
-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_parsetext(struct buf *, int, int *);
+static	enum rofferr	 roff_res(struct roff *, struct buf *, int, int);
 static	enum rofferr	 roff_rm(ROFF_ARGS);
 static	enum rofferr	 roff_rr(ROFF_ARGS);
 static	void		 roff_setstr(struct roff *,
 				const char *, const char *, int);
 static	void		 roff_setstrn(struct roffkv **, const char *,
 				size_t, const char *, size_t, int);
 static	enum rofferr	 roff_so(ROFF_ARGS);
 static	enum rofferr	 roff_tr(ROFF_ARGS);
 static	enum rofferr	 roff_Dd(ROFF_ARGS);
 static	enum rofferr	 roff_TH(ROFF_ARGS);
 static	enum rofferr	 roff_TE(ROFF_ARGS);
 static	enum rofferr	 roff_TS(ROFF_ARGS);
 static	enum rofferr	 roff_EQ(ROFF_ARGS);
 static	enum rofferr	 roff_EN(ROFF_ARGS);
 static	enum rofferr	 roff_T_(ROFF_ARGS);
 static	enum rofferr	 roff_userdef(ROFF_ARGS);
 
 /* See roffhash_find() */
 
 #define	ASCII_HI	 126
 #define	ASCII_LO	 33
 #define	HASHWIDTH	(ASCII_HI - ASCII_LO + 1)
 
 static	struct roffmac	*hash[HASHWIDTH];
 
 static	struct roffmac	 roffs[ROFF_MAX] = {
 	{ "ad", roff_line_ignore, NULL, NULL, 0, NULL },
 	{ "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 },
 	{ "as", roff_ds, NULL, NULL, 0, NULL },
 	{ "cc", roff_cc, NULL, NULL, 0, NULL },
 	{ "ce", roff_line_ignore, 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_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 },
 	{ "ns", roff_line_ignore, NULL, NULL, 0, NULL },
+	{ "pl", roff_line_ignore, NULL, NULL, 0, NULL },
 	{ "ps", roff_line_ignore, NULL, NULL, 0, NULL },
 	{ "rm", roff_rm, NULL, NULL, 0, NULL },
 	{ "rr", roff_rr, NULL, NULL, 0, NULL },
 	{ "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 },
 	{ "EQ", roff_EQ, NULL, NULL, 0, NULL },
 	{ "EN", roff_EN, NULL, NULL, 0, NULL },
 	{ ".", roff_cblock, NULL, NULL, 0, NULL },
 	{ NULL, roff_userdef, NULL, NULL, 0, NULL },
 };
 
 /* not currently implemented: Ds em Eq LP Me PP pp Or Rd Sf SH */
 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",
 	"Dt", "Dv", "Dx", "D1",
 	"Ec", "Ed", "Ef", "Ek", "El", "Em",
 	"En", "Eo", "Er", "Es", "Ev", "Ex",
 	"Fa", "Fc", "Fd", "Fl", "Fn", "Fo", "Fr", "Ft", "Fx",
 	"Hf", "Ic", "In", "It", "Lb", "Li", "Lk", "Lp",
 	"Ms", "Mt", "Nd", "Nm", "No", "Ns", "Nx",
 	"Oc", "Oo", "Op", "Os", "Ot", "Ox",
 	"Pa", "Pc", "Pf", "Po", "Pp", "Pq",
 	"Qc", "Ql", "Qo", "Qq", "Re", "Rs", "Rv",
 	"Sc", "Sh", "Sm", "So", "Sq",
 	"Ss", "St", "Sx", "Sy",
 	"Ta", "Tn", "Ud", "Ux", "Va", "Vt", "Xc", "Xo", "Xr",
 	"%A", "%B", "%C", "%D", "%I", "%J", "%N", "%O",
 	"%P", "%Q", "%R", "%T", "%U", "%V",
 	NULL
 };
 
 /* not currently implemented: BT DE DS ME MT PT SY TQ YS */
 const	char *const __man_reserved[] = {
 	"AT", "B", "BI", "BR", "DT",
 	"EE", "EN", "EQ", "EX", "HP", "I", "IB", "IP", "IR",
 	"LP", "OP", "P", "PD", "PP",
 	"R", "RB", "RE", "RI", "RS", "SB", "SH", "SM", "SS",
 	"TE", "TH", "TP", "TS", "T&", "UC", "UE", "UR",
 	NULL
 };
 
 /* Array of injected predefined strings. */
 #define	PREDEFS_MAX	 38
 static	const struct predef predefs[PREDEFS_MAX] = {
 #include "predefs.in"
 };
 
 /* 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)
 {
 	struct roffmac	 *n;
 	int		  buc, i;
 
 	for (i = 0; i < (int)ROFF_USERDEF; i++) {
 		assert(roffs[i].name[0] >= ASCII_LO);
 		assert(roffs[i].name[0] <= ASCII_HI);
 
 		buc = ROFF_HASH(roffs[i].name);
 
 		if (NULL != (n = hash[buc])) {
 			for ( ; n->next; n = n->next)
 				/* Do nothing. */ ;
 			n->next = &roffs[i];
 		} else
 			hash[buc] = &roffs[i];
 	}
 }
 
 /*
  * Look up a roff token by its name.  Returns ROFF_MAX if no macro by
  * the nil-terminated string name could be found.
  */
 static enum rofft
 roffhash_find(const char *p, size_t s)
 {
 	int		 buc;
 	struct roffmac	*n;
 
 	/*
 	 * libroff has an extremely simple hashtable, for the time
 	 * being, which simply keys on the first character, which must
 	 * be printable, then walks a chain.  It works well enough until
 	 * optimised.
 	 */
 
 	if (p[0] < ASCII_LO || p[0] > ASCII_HI)
 		return(ROFF_MAX);
 
 	buc = ROFF_HASH(p);
 
 	if (NULL == (n = hash[buc]))
 		return(ROFF_MAX);
 	for ( ; n; n = n->next)
 		if (0 == strncmp(n->name, p, s) && '\0' == n->name[(int)s])
 			return((enum rofft)(n - roffs));
 
 	return(ROFF_MAX);
 }
 
 /*
  * Pop the current node off of the stack of roff instructions currently
  * pending.
  */
 static void
 roffnode_pop(struct roff *r)
 {
 	struct roffnode	*p;
 
 	assert(r->last);
 	p = r->last;
 
 	r->last = r->last->parent;
 	free(p->name);
 	free(p->end);
 	free(p);
 }
 
 /*
  * Push a roff node onto the instruction stack.  This must later be
  * removed with roffnode_pop().
  */
 static void
 roffnode_push(struct roff *r, enum rofft tok, const char *name,
 		int line, int col)
 {
 	struct roffnode	*p;
 
 	p = mandoc_calloc(1, sizeof(struct roffnode));
 	p->tok = tok;
 	if (name)
 		p->name = mandoc_strdup(name);
 	p->parent = r->last;
 	p->line = line;
 	p->col = col;
 	p->rule = p->parent ? p->parent->rule : 0;
 
 	r->last = p;
 }
 
 static void
 roff_free1(struct roff *r)
 {
 	struct tbl_node	*tbl;
 	struct eqn_node	*e;
 	int		 i;
 
 	while (NULL != (tbl = r->first_tbl)) {
 		r->first_tbl = tbl->next;
 		tbl_free(tbl);
 	}
 	r->first_tbl = r->last_tbl = r->tbl = NULL;
 
 	while (NULL != (e = r->first_eqn)) {
 		r->first_eqn = e->next;
 		eqn_free(e);
 	}
 	r->first_eqn = r->last_eqn = r->eqn = NULL;
 
 	while (r->last)
 		roffnode_pop(r);
 
 	free (r->rstack);
 	r->rstack = NULL;
 	r->rstacksz = 0;
 	r->rstackpos = -1;
 
 	roff_freereg(r->regtab);
 	r->regtab = NULL;
 
 	roff_freestr(r->strtab);
 	roff_freestr(r->xmbtab);
 	r->strtab = r->xmbtab = NULL;
 
 	if (r->xtab)
 		for (i = 0; i < 128; i++)
 			free(r->xtab[i].p);
 	free(r->xtab);
 	r->xtab = NULL;
 }
 
 void
 roff_reset(struct roff *r)
 {
 
 	roff_free1(r);
+	r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
 	r->control = 0;
 }
 
 void
 roff_free(struct roff *r)
 {
 
 	roff_free1(r);
 	free(r);
 }
 
 struct roff *
-roff_alloc(struct mparse *parse, int options)
+roff_alloc(struct mparse *parse, const struct mchars *mchars, int options)
 {
 	struct roff	*r;
 
 	r = mandoc_calloc(1, sizeof(struct roff));
 	r->parse = parse;
+	r->mchars = mchars;
 	r->options = options;
+	r->format = options & (MPARSE_MDOC | MPARSE_MAN);
 	r->rstackpos = -1;
 
 	roffhash_init();
 
 	return(r);
 }
 
 /*
  * In the current line, expand escape sequences that tend to get
  * used in numerical expressions and conditional requests.
  * Also check the syntax of the remaining escape sequences.
  */
 static enum rofferr
-roff_res(struct roff *r, char **bufp, size_t *szp, int ln, int pos)
+roff_res(struct roff *r, struct buf *buf, int ln, int pos)
 {
 	char		 ubuf[24]; /* buffer to print the number */
 	const char	*start;	/* start of the string to process */
 	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 */
-	char		*nbuf;	/* new buffer to copy bufp to */
+	char		*nbuf;	/* new buffer to copy buf->buf to */
 	size_t		 maxl;  /* expected length of the escape name */
 	size_t		 naml;	/* actual length of the escape name */
+	enum mandoc_esc	 esc;	/* type of the escape sequence */
+	int		 inaml;	/* length returned from mandoc_escape() */
 	int		 expand_count;	/* to avoid infinite loops */
 	int		 npos;	/* position in numeric expression */
 	int		 arg_complete; /* argument not interrupted by eol */
 	char		 term;	/* character terminating the escape */
 
 	expand_count = 0;
-	start = *bufp + pos;
+	start = buf->buf + pos;
 	stesc = strchr(start, '\0') - 1;
 	while (stesc-- > start) {
 
 		/* Search backwards for the next backslash. */
 
-		if ('\\' != *stesc)
+		if (*stesc != '\\')
 			continue;
 
 		/* If it is escaped, skip it. */
 
 		for (cp = stesc - 1; cp >= start; cp--)
-			if ('\\' != *cp)
+			if (*cp != '\\')
 				break;
 
-		if (0 == (stesc - cp) % 2) {
+		if ((stesc - cp) % 2 == 0) {
 			stesc = (char *)cp;
 			continue;
 		}
 
 		/* Decide whether to expand or to check only. */
 
 		term = '\0';
 		cp = stesc + 1;
 		switch (*cp) {
 		case '*':
 			res = NULL;
 			break;
 		case 'B':
 			/* FALLTHROUGH */
 		case 'w':
 			term = cp[1];
 			/* FALLTHROUGH */
 		case 'n':
 			res = ubuf;
 			break;
 		default:
-			if (ESCAPE_ERROR == mandoc_escape(&cp, NULL, NULL))
+			esc = mandoc_escape(&cp, &stnam, &inaml);
+			if (esc == ESCAPE_ERROR ||
+			    (esc == ESCAPE_SPECIAL &&
+			     mchars_spec2cp(r->mchars, stnam, inaml) < 0))
 				mandoc_vmsg(MANDOCERR_ESC_BAD,
-				    r->parse, ln, (int)(stesc - *bufp),
+				    r->parse, ln, (int)(stesc - buf->buf),
 				    "%.*s", (int)(cp - stesc), stesc);
 			continue;
 		}
 
 		if (EXPAND_LIMIT < ++expand_count) {
 			mandoc_msg(MANDOCERR_ROFFLOOP, r->parse,
-			    ln, (int)(stesc - *bufp), NULL);
+			    ln, (int)(stesc - buf->buf), NULL);
 			return(ROFF_IGN);
 		}
 
 		/*
 		 * The third character decides the length
 		 * of the name of the string or register.
 		 * Save a pointer to the name.
 		 */
 
-		if ('\0' == term) {
+		if (term == '\0') {
 			switch (*++cp) {
 			case '\0':
 				maxl = 0;
 				break;
 			case '(':
 				cp++;
 				maxl = 2;
 				break;
 			case '[':
 				cp++;
 				term = ']';
 				maxl = 0;
 				break;
 			default:
 				maxl = 1;
 				break;
 			}
 		} else {
 			cp += 2;
 			maxl = 0;
 		}
 		stnam = cp;
 
 		/* Advance to the end of the name. */
 
 		arg_complete = 1;
-		for (naml = 0; 0 == maxl || naml < maxl; naml++, cp++) {
-			if ('\0' == *cp) {
+		for (naml = 0; maxl == 0 || naml < maxl; naml++, cp++) {
+			if (*cp == '\0') {
 				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
-				    ln, (int)(stesc - *bufp), stesc);
+				    ln, (int)(stesc - buf->buf), stesc);
 				arg_complete = 0;
 				break;
 			}
-			if (0 == maxl && *cp == term) {
+			if (maxl == 0 && *cp == term) {
 				cp++;
 				break;
 			}
 		}
 
 		/*
 		 * Retrieve the replacement string; if it is
 		 * undefined, resume searching for escapes.
 		 */
 
 		switch (stesc[1]) {
 		case '*':
 			if (arg_complete)
 				res = roff_getstrn(r, stnam, naml);
 			break;
 		case 'B':
 			npos = 0;
 			ubuf[0] = arg_complete &&
-			    roff_evalnum(stnam, &npos, NULL, 0) &&
+			    roff_evalnum(r, ln, stnam, &npos, NULL, 0) &&
 			    stnam + npos + 1 == cp ? '1' : '0';
 			ubuf[1] = '\0';
 			break;
 		case 'n':
 			if (arg_complete)
 				(void)snprintf(ubuf, sizeof(ubuf), "%d",
 				    roff_getregn(r, stnam, naml));
 			else
 				ubuf[0] = '\0';
 			break;
 		case 'w':
 			/* use even incomplete args */
 			(void)snprintf(ubuf, sizeof(ubuf), "%d",
 			    24 * (int)naml);
 			break;
 		}
 
-		if (NULL == res) {
+		if (res == NULL) {
 			mandoc_vmsg(MANDOCERR_STR_UNDEF,
-			    r->parse, ln, (int)(stesc - *bufp),
+			    r->parse, ln, (int)(stesc - buf->buf),
 			    "%.*s", (int)naml, stnam);
 			res = "";
 		}
 
 		/* Replace the escape sequence by the string. */
 
 		*stesc = '\0';
-		*szp = mandoc_asprintf(&nbuf, "%s%s%s",
-		    *bufp, res, cp) + 1;
+		buf->sz = mandoc_asprintf(&nbuf, "%s%s%s",
+		    buf->buf, res, cp) + 1;
 
 		/* Prepare for the next replacement. */
 
 		start = nbuf + pos;
-		stesc = nbuf + (stesc - *bufp) + strlen(res);
-		free(*bufp);
-		*bufp = nbuf;
+		stesc = nbuf + (stesc - buf->buf) + strlen(res);
+		free(buf->buf);
+		buf->buf = nbuf;
 	}
 	return(ROFF_CONT);
 }
 
 /*
  * Process text streams:
  * Convert all breakable hyphens into ASCII_HYPH.
  * Decrement and spring input line trap.
  */
 static enum rofferr
-roff_parsetext(char **bufp, size_t *szp, int pos, int *offs)
+roff_parsetext(struct buf *buf, int pos, int *offs)
 {
 	size_t		 sz;
 	const char	*start;
 	char		*p;
 	int		 isz;
 	enum mandoc_esc	 esc;
 
-	start = p = *bufp + pos;
+	start = p = buf->buf + pos;
 
-	while ('\0' != *p) {
+	while (*p != '\0') {
 		sz = strcspn(p, "-\\");
 		p += sz;
 
-		if ('\0' == *p)
+		if (*p == '\0')
 			break;
 
-		if ('\\' == *p) {
+		if (*p == '\\') {
 			/* Skip over escapes. */
 			p++;
 			esc = mandoc_escape((const char **)&p, NULL, NULL);
-			if (ESCAPE_ERROR == esc)
+			if (esc == ESCAPE_ERROR)
 				break;
 			continue;
 		} else if (p == start) {
 			p++;
 			continue;
 		}
 
 		if (isalpha((unsigned char)p[-1]) &&
 		    isalpha((unsigned char)p[1]))
 			*p = ASCII_HYPH;
 		p++;
 	}
 
 	/* Spring the input line trap. */
-	if (1 == roffit_lines) {
-		isz = mandoc_asprintf(&p, "%s\n.%s", *bufp, roffit_macro);
-		free(*bufp);
-		*bufp = p;
-		*szp = isz + 1;
+	if (roffit_lines == 1) {
+		isz = mandoc_asprintf(&p, "%s\n.%s", buf->buf, roffit_macro);
+		free(buf->buf);
+		buf->buf = p;
+		buf->sz = isz + 1;
 		*offs = 0;
 		free(roffit_macro);
 		roffit_lines = 0;
 		return(ROFF_REPARSE);
-	} else if (1 < roffit_lines)
+	} else if (roffit_lines > 1)
 		--roffit_lines;
 	return(ROFF_CONT);
 }
 
 enum rofferr
-roff_parseln(struct roff *r, int ln, char **bufp,
-		size_t *szp, int pos, int *offs)
+roff_parseln(struct roff *r, int ln, struct buf *buf, int *offs)
 {
 	enum rofft	 t;
 	enum rofferr	 e;
-	int		 ppos, ctl;
+	int		 pos;	/* parse point */
+	int		 ppos;	/* original offset in buf->buf */
+	int		 ctl;	/* macro line (boolean) */
 
-	/*
-	 * Run the reserved-word filter only if we have some reserved
-	 * words to fill in.
-	 */
+	ppos = pos = *offs;
 
-	e = roff_res(r, bufp, szp, ln, pos);
-	if (ROFF_IGN == e)
+	/* Handle in-line equation delimiters. */
+
+	if (r->tbl == NULL &&
+	    r->last_eqn != NULL && r->last_eqn->delim &&
+	    (r->eqn == NULL || r->eqn_inline)) {
+		e = roff_eqndelim(r, buf, pos);
+		if (e == ROFF_REPARSE)
+			return(e);
+		assert(e == ROFF_CONT);
+	}
+
+	/* Expand some escape sequences. */
+
+	e = roff_res(r, buf, ln, pos);
+	if (e == ROFF_IGN)
 		return(e);
-	assert(ROFF_CONT == e);
+	assert(e == ROFF_CONT);
 
-	ppos = pos;
-	ctl = roff_getcontrol(r, *bufp, &pos);
+	ctl = roff_getcontrol(r, buf->buf, &pos);
 
 	/*
 	 * First, if a scope is open and we're not a macro, pass the
 	 * text through the macro's filter.  If a scope isn't open and
 	 * we're not a macro, just let it through.
 	 * Finally, if there's an equation scope open, divert it into it
 	 * no matter our state.
 	 */
 
 	if (r->last && ! ctl) {
 		t = r->last->tok;
 		assert(roffs[t].text);
-		e = (*roffs[t].text)(r, t, bufp, szp, ln, pos, pos, offs);
-		assert(ROFF_IGN == e || ROFF_CONT == e);
-		if (ROFF_CONT != e)
+		e = (*roffs[t].text)(r, t, buf, ln, pos, pos, offs);
+		assert(e == ROFF_IGN || e == ROFF_CONT);
+		if (e != ROFF_CONT)
 			return(e);
 	}
 	if (r->eqn)
-		return(eqn_read(&r->eqn, ln, *bufp, ppos, offs));
+		return(eqn_read(&r->eqn, ln, buf->buf, ppos, offs));
 	if ( ! ctl) {
 		if (r->tbl)
-			return(tbl_read(r->tbl, ln, *bufp, pos));
-		return(roff_parsetext(bufp, szp, pos, offs));
+			return(tbl_read(r->tbl, ln, buf->buf, pos));
+		return(roff_parsetext(buf, pos, offs));
 	}
 
+	/* Skip empty request lines. */
+
+	if (buf->buf[pos] == '"') {
+		mandoc_msg(MANDOCERR_COMMENT_BAD, r->parse,
+		    ln, pos, NULL);
+		return(ROFF_IGN);
+	} else if (buf->buf[pos] == '\0')
+		return(ROFF_IGN);
+
 	/*
 	 * If a scope is open, go to the child handler for that macro,
 	 * as it may want to preprocess before doing anything with it.
 	 * Don't do so if an equation is open.
 	 */
 
 	if (r->last) {
 		t = r->last->tok;
 		assert(roffs[t].sub);
-		return((*roffs[t].sub)(r, t, bufp, szp,
-		    ln, ppos, pos, offs));
+		return((*roffs[t].sub)(r, t, buf, ln, ppos, pos, offs));
 	}
 
 	/*
 	 * Lastly, as we've no scope open, try to look up and execute
 	 * the new macro.  If no macro is found, simply return and let
 	 * the compilers handle it.
 	 */
 
-	if (ROFF_MAX == (t = roff_parse(r, *bufp, &pos, ln, ppos)))
+	if ((t = roff_parse(r, buf->buf, &pos, ln, ppos)) == ROFF_MAX)
 		return(ROFF_CONT);
 
 	assert(roffs[t].proc);
-	return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs));
+	return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
 }
 
 void
 roff_endparse(struct roff *r)
 {
 
 	if (r->last)
 		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
 		    r->last->line, r->last->col,
 		    roffs[r->last->tok].name);
 
 	if (r->eqn) {
 		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
 		    r->eqn->eqn.ln, r->eqn->eqn.pos, "EQ");
 		eqn_end(&r->eqn);
 	}
 
 	if (r->tbl) {
 		mandoc_msg(MANDOCERR_BLK_NOEND, r->parse,
 		    r->tbl->line, r->tbl->pos, "TS");
 		tbl_end(&r->tbl);
 	}
 }
 
 /*
  * Parse a roff node's type from the input buffer.  This must be in the
  * form of ".foo xxx" in the usual way.
  */
 static enum rofft
 roff_parse(struct roff *r, char *buf, int *pos, int ln, int ppos)
 {
 	char		*cp;
 	const char	*mac;
 	size_t		 maclen;
 	enum rofft	 t;
 
 	cp = buf + *pos;
 
 	if ('\0' == *cp || '"' == *cp || '\t' == *cp || ' ' == *cp)
 		return(ROFF_MAX);
 
 	mac = cp;
 	maclen = roff_getname(r, &cp, ln, ppos);
 
 	t = (r->current_string = roff_getstrn(r, mac, maclen))
 	    ? ROFF_USERDEF : roffhash_find(mac, maclen);
 
 	if (ROFF_MAX != t)
 		*pos = cp - buf;
 
 	return(t);
 }
 
 static enum rofferr
 roff_cblock(ROFF_ARGS)
 {
 
 	/*
 	 * A block-close `..' should only be invoked as a child of an
 	 * ignore macro, otherwise raise a warning and just ignore it.
 	 */
 
-	if (NULL == r->last) {
+	if (r->last == NULL) {
 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
 		    ln, ppos, "..");
 		return(ROFF_IGN);
 	}
 
 	switch (r->last->tok) {
 	case ROFF_am:
 		/* ROFF_am1 is remapped to ROFF_am in roff_block(). */
 		/* FALLTHROUGH */
 	case ROFF_ami:
 		/* FALLTHROUGH */
 	case ROFF_de:
 		/* ROFF_de1 is remapped to ROFF_de in roff_block(). */
 		/* FALLTHROUGH */
 	case ROFF_dei:
 		/* FALLTHROUGH */
 	case ROFF_ig:
 		break;
 	default:
 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
 		    ln, ppos, "..");
 		return(ROFF_IGN);
 	}
 
-	if ((*bufp)[pos])
+	if (buf->buf[pos] != '\0')
 		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
-		    ".. %s", *bufp + pos);
+		    ".. %s", buf->buf + pos);
 
 	roffnode_pop(r);
 	roffnode_cleanscope(r);
 	return(ROFF_IGN);
 
 }
 
 static void
 roffnode_cleanscope(struct roff *r)
 {
 
 	while (r->last) {
 		if (--r->last->endspan != 0)
 			break;
 		roffnode_pop(r);
 	}
 }
 
 static void
 roff_ccond(struct roff *r, int ln, int ppos)
 {
 
 	if (NULL == r->last) {
 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
 		    ln, ppos, "\\}");
 		return;
 	}
 
 	switch (r->last->tok) {
 	case ROFF_el:
 		/* FALLTHROUGH */
 	case ROFF_ie:
 		/* FALLTHROUGH */
 	case ROFF_if:
 		break;
 	default:
 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
 		    ln, ppos, "\\}");
 		return;
 	}
 
 	if (r->last->endspan > -1) {
 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
 		    ln, ppos, "\\}");
 		return;
 	}
 
 	roffnode_pop(r);
 	roffnode_cleanscope(r);
 	return;
 }
 
 static enum rofferr
 roff_block(ROFF_ARGS)
 {
 	const char	*name;
 	char		*iname, *cp;
 	size_t		 namesz;
 
 	/* Ignore groff compatibility mode for now. */
 
-	if (ROFF_de1 == tok)
+	if (tok == ROFF_de1)
 		tok = ROFF_de;
-	else if (ROFF_am1 == tok)
+	else if (tok == ROFF_am1)
 		tok = ROFF_am;
 
 	/* Parse the macro name argument. */
 
-	cp = *bufp + pos;
-	if (ROFF_ig == tok) {
+	cp = buf->buf + pos;
+	if (tok == ROFF_ig) {
 		iname = NULL;
 		namesz = 0;
 	} else {
 		iname = cp;
 		namesz = roff_getname(r, &cp, ln, ppos);
 		iname[namesz] = '\0';
 	}
 
 	/* Resolve the macro name argument if it is indirect. */
 
-	if (namesz && (ROFF_dei == tok || ROFF_ami == tok)) {
-		if (NULL == (name = roff_getstrn(r, iname, namesz))) {
+	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
+		if ((name = roff_getstrn(r, iname, namesz)) == NULL) {
 			mandoc_vmsg(MANDOCERR_STR_UNDEF,
-			    r->parse, ln, (int)(iname - *bufp),
+			    r->parse, ln, (int)(iname - buf->buf),
 			    "%.*s", (int)namesz, iname);
 			namesz = 0;
 		} else
 			namesz = strlen(name);
 	} else
 		name = iname;
 
-	if (0 == namesz && ROFF_ig != tok) {
+	if (namesz == 0 && tok != ROFF_ig) {
 		mandoc_msg(MANDOCERR_REQ_EMPTY, r->parse,
 		    ln, ppos, roffs[tok].name);
 		return(ROFF_IGN);
 	}
 
 	roffnode_push(r, tok, name, ln, ppos);
 
 	/*
 	 * At the beginning of a `de' macro, clear the existing string
 	 * with the same name, if there is one.  New content will be
 	 * appended from roff_block_text() in multiline mode.
 	 */
 
-	if (ROFF_de == tok || ROFF_dei == tok)
+	if (tok == ROFF_de || tok == ROFF_dei)
 		roff_setstrn(&r->strtab, name, namesz, "", 0, 0);
 
-	if ('\0' == *cp)
+	if (*cp == '\0')
 		return(ROFF_IGN);
 
 	/* Get the custom end marker. */
 
 	iname = cp;
 	namesz = roff_getname(r, &cp, ln, ppos);
 
 	/* Resolve the end marker if it is indirect. */
 
-	if (namesz && (ROFF_dei == tok || ROFF_ami == tok)) {
-		if (NULL == (name = roff_getstrn(r, iname, namesz))) {
+	if (namesz && (tok == ROFF_dei || tok == ROFF_ami)) {
+		if ((name = roff_getstrn(r, iname, namesz)) == NULL) {
 			mandoc_vmsg(MANDOCERR_STR_UNDEF,
-			    r->parse, ln, (int)(iname - *bufp),
+			    r->parse, ln, (int)(iname - buf->buf),
 			    "%.*s", (int)namesz, iname);
 			namesz = 0;
 		} else
 			namesz = strlen(name);
 	} else
 		name = iname;
 
 	if (namesz)
 		r->last->end = mandoc_strndup(name, namesz);
 
-	if ('\0' != *cp)
+	if (*cp != '\0')
 		mandoc_vmsg(MANDOCERR_ARG_EXCESS, r->parse,
 		    ln, pos, ".%s ... %s", roffs[tok].name, cp);
 
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_block_sub(ROFF_ARGS)
 {
 	enum rofft	t;
 	int		i, j;
 
 	/*
 	 * First check whether a custom macro exists at this level.  If
 	 * it does, then check against it.  This is some of groff's
 	 * stranger behaviours.  If we encountered a custom end-scope
 	 * tag and that tag also happens to be a "real" macro, then we
 	 * need to try interpreting it again as a real macro.  If it's
 	 * not, then return ignore.  Else continue.
 	 */
 
 	if (r->last->end) {
 		for (i = pos, j = 0; r->last->end[j]; j++, i++)
-			if ((*bufp)[i] != r->last->end[j])
+			if (buf->buf[i] != r->last->end[j])
 				break;
 
-		if ('\0' == r->last->end[j] &&
-		    ('\0' == (*bufp)[i] ||
-		     ' '  == (*bufp)[i] ||
-		     '\t' == (*bufp)[i])) {
+		if (r->last->end[j] == '\0' &&
+		    (buf->buf[i] == '\0' ||
+		     buf->buf[i] == ' ' ||
+		     buf->buf[i] == '\t')) {
 			roffnode_pop(r);
 			roffnode_cleanscope(r);
 
-			while (' ' == (*bufp)[i] || '\t' == (*bufp)[i])
+			while (buf->buf[i] == ' ' || buf->buf[i] == '\t')
 				i++;
 
 			pos = i;
-			if (ROFF_MAX != roff_parse(r, *bufp, &pos, ln, ppos))
+			if (roff_parse(r, buf->buf, &pos, ln, ppos) !=
+			    ROFF_MAX)
 				return(ROFF_RERUN);
 			return(ROFF_IGN);
 		}
 	}
 
 	/*
 	 * If we have no custom end-query or lookup failed, then try
 	 * pulling it out of the hashtable.
 	 */
 
-	t = roff_parse(r, *bufp, &pos, ln, ppos);
+	t = roff_parse(r, buf->buf, &pos, ln, ppos);
 
-	if (ROFF_cblock != t) {
-		if (ROFF_ig != tok)
-			roff_setstr(r, r->last->name, *bufp + ppos, 2);
+	if (t != ROFF_cblock) {
+		if (tok != ROFF_ig)
+			roff_setstr(r, r->last->name, buf->buf + ppos, 2);
 		return(ROFF_IGN);
 	}
 
 	assert(roffs[t].proc);
-	return((*roffs[t].proc)(r, t, bufp, szp, ln, ppos, pos, offs));
+	return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
 }
 
 static enum rofferr
 roff_block_text(ROFF_ARGS)
 {
 
-	if (ROFF_ig != tok)
-		roff_setstr(r, r->last->name, *bufp + pos, 2);
+	if (tok != ROFF_ig)
+		roff_setstr(r, r->last->name, buf->buf + pos, 2);
 
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_cond_sub(ROFF_ARGS)
 {
 	enum rofft	 t;
 	char		*ep;
 	int		 rr;
 
 	rr = r->last->rule;
 	roffnode_cleanscope(r);
-	t = roff_parse(r, *bufp, &pos, ln, ppos);
+	t = roff_parse(r, buf->buf, &pos, ln, ppos);
 
 	/*
 	 * Fully handle known macros when they are structurally
 	 * required or when the conditional evaluated to true.
 	 */
 
-	if ((ROFF_MAX != t) &&
-	    (rr || ROFFMAC_STRUCT & roffs[t].flags)) {
+	if ((t != ROFF_MAX) &&
+	    (rr || roffs[t].flags & ROFFMAC_STRUCT)) {
 		assert(roffs[t].proc);
-		return((*roffs[t].proc)(r, t, bufp, szp,
-		    ln, ppos, pos, offs));
+		return((*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs));
 	}
 
 	/*
 	 * If `\}' occurs on a macro line without a preceding macro,
 	 * drop the line completely.
 	 */
 
-	ep = *bufp + pos;
-	if ('\\' == ep[0] && '}' == ep[1])
+	ep = buf->buf + pos;
+	if (ep[0] == '\\' && ep[1] == '}')
 		rr = 0;
 
 	/* Always check for the closing delimiter `\}'. */
 
-	while (NULL != (ep = strchr(ep, '\\'))) {
-		if ('}' == *(++ep)) {
+	while ((ep = strchr(ep, '\\')) != NULL) {
+		if (*(++ep) == '}') {
 			*ep = '&';
-			roff_ccond(r, ln, ep - *bufp - 1);
+			roff_ccond(r, ln, ep - buf->buf - 1);
 		}
 		++ep;
 	}
 	return(rr ? ROFF_CONT : ROFF_IGN);
 }
 
 static enum rofferr
 roff_cond_text(ROFF_ARGS)
 {
 	char		*ep;
 	int		 rr;
 
 	rr = r->last->rule;
 	roffnode_cleanscope(r);
 
-	ep = *bufp + pos;
-	while (NULL != (ep = strchr(ep, '\\'))) {
-		if ('}' == *(++ep)) {
+	ep = buf->buf + pos;
+	while ((ep = strchr(ep, '\\')) != NULL) {
+		if (*(++ep) == '}') {
 			*ep = '&';
-			roff_ccond(r, ln, ep - *bufp - 1);
+			roff_ccond(r, ln, ep - buf->buf - 1);
 		}
 		++ep;
 	}
 	return(rr ? ROFF_CONT : ROFF_IGN);
 }
 
 /*
  * Parse a single signed integer number.  Stop at the first non-digit.
  * If there is at least one digit, return success and advance the
  * parse point, else return failure and let the parse point unchanged.
  * Ignore overflows, treat them just like the C language.
  */
 static int
 roff_getnum(const char *v, int *pos, int *res)
 {
 	int	 myres, n, p;
 
 	if (NULL == res)
 		res = &myres;
 
 	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;
 }
 
 /*
  * Evaluate a string comparison condition.
  * The first character is the delimiter.
  * Succeed if the string up to its second occurrence
  * matches the string up to its third occurence.
  * Advance the cursor after the third occurrence
  * or lacking that, to the end of the line.
  */
 static int
 roff_evalstrcond(const char *v, int *pos)
 {
 	const char	*s1, *s2, *s3;
 	int		 match;
 
 	match = 0;
 	s1 = v + *pos;		/* initial delimiter */
 	s2 = s1 + 1;		/* for scanning the first string */
 	s3 = strchr(s2, *s1);	/* for scanning the second string */
 
 	if (NULL == s3)		/* found no middle delimiter */
 		goto out;
 
 	while ('\0' != *++s3) {
 		if (*s2 != *s3) {  /* mismatch */
 			s3 = strchr(s3, *s1);
 			break;
 		}
 		if (*s3 == *s1) {  /* found the final delimiter */
 			match = 1;
 			break;
 		}
 		s2++;
 	}
 
 out:
 	if (NULL == s3)
 		s3 = strchr(s2, '\0');
 	else
 		s3++;
 	*pos = s3 - v;
 	return(match);
 }
 
 /*
  * Evaluate an optionally negated single character, numerical,
  * or string condition.
  */
 static int
-roff_evalcond(const char *v, int *pos)
+roff_evalcond(struct roff *r, int ln, const char *v, int *pos)
 {
 	int	 wanttrue, number;
 
 	if ('!' == v[*pos]) {
 		wanttrue = 0;
 		(*pos)++;
 	} else
 		wanttrue = 1;
 
 	switch (v[*pos]) {
 	case 'n':
 		/* FALLTHROUGH */
 	case 'o':
 		(*pos)++;
 		return(wanttrue);
 	case 'c':
 		/* FALLTHROUGH */
 	case 'd':
 		/* FALLTHROUGH */
 	case 'e':
 		/* FALLTHROUGH */
 	case 'r':
 		/* FALLTHROUGH */
 	case 't':
+		/* FALLTHROUGH */
+	case 'v':
 		(*pos)++;
 		return(!wanttrue);
 	default:
 		break;
 	}
 
-	if (roff_evalnum(v, pos, &number, 0))
+	if (roff_evalnum(r, ln, v, pos, &number, 0))
 		return((number > 0) == wanttrue);
 	else
 		return(roff_evalstrcond(v, pos) == wanttrue);
 }
 
 static enum rofferr
 roff_line_ignore(ROFF_ARGS)
 {
 
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_cond(ROFF_ARGS)
 {
 
 	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
 	 * defaults to DENY.
 	 *
 	 * If we're not an `el', however, then evaluate the conditional.
 	 */
 
-	r->last->rule = ROFF_el == tok ?
+	r->last->rule = tok == ROFF_el ?
 	    (r->rstackpos < 0 ? 0 : r->rstack[r->rstackpos--]) :
-	    roff_evalcond(*bufp, &pos);
+	    roff_evalcond(r, ln, buf->buf, &pos);
 
 	/*
 	 * An if-else will put the NEGATION of the current evaluated
 	 * conditional into the stack of rules.
 	 */
 
-	if (ROFF_ie == tok) {
+	if (tok == ROFF_ie) {
 		if (r->rstackpos + 1 == r->rstacksz) {
 			r->rstacksz += 16;
 			r->rstack = mandoc_reallocarray(r->rstack,
 			    r->rstacksz, sizeof(int));
 		}
 		r->rstack[++r->rstackpos] = !r->last->rule;
 	}
 
 	/* If the parent has false as its rule, then so do we. */
 
 	if (r->last->parent && !r->last->parent->rule)
 		r->last->rule = 0;
 
 	/*
 	 * Determine scope.
 	 * If there is nothing on the line after the conditional,
 	 * not even whitespace, use next-line scope.
 	 */
 
-	if ('\0' == (*bufp)[pos]) {
+	if (buf->buf[pos] == '\0') {
 		r->last->endspan = 2;
 		goto out;
 	}
 
-	while (' ' == (*bufp)[pos])
+	while (buf->buf[pos] == ' ')
 		pos++;
 
 	/* An opening brace requests multiline scope. */
 
-	if ('\\' == (*bufp)[pos] && '{' == (*bufp)[pos + 1]) {
+	if (buf->buf[pos] == '\\' && buf->buf[pos + 1] == '{') {
 		r->last->endspan = -1;
 		pos += 2;
 		goto out;
 	}
 
 	/*
 	 * Anything else following the conditional causes
 	 * single-line scope.  Warn if the scope contains
 	 * nothing but trailing whitespace.
 	 */
 
-	if ('\0' == (*bufp)[pos])
+	if (buf->buf[pos] == '\0')
 		mandoc_msg(MANDOCERR_COND_EMPTY, r->parse,
 		    ln, ppos, roffs[tok].name);
 
 	r->last->endspan = 1;
 
 out:
 	*offs = pos;
 	return(ROFF_RERUN);
 }
 
 static enum rofferr
 roff_ds(ROFF_ARGS)
 {
 	char		*string;
 	const char	*name;
 	size_t		 namesz;
 
 	/*
 	 * The first word is the name of the string.
 	 * If it is empty or terminated by an escape sequence,
 	 * abort the `ds' request without defining anything.
 	 */
 
-	name = string = *bufp + pos;
-	if ('\0' == *name)
+	name = string = buf->buf + pos;
+	if (*name == '\0')
 		return(ROFF_IGN);
 
 	namesz = roff_getname(r, &string, ln, pos);
-	if ('\\' == name[namesz])
+	if (name[namesz] == '\\')
 		return(ROFF_IGN);
 
 	/* Read past the initial double-quote, if any. */
-	if ('"' == *string)
+	if (*string == '"')
 		string++;
 
 	/* The rest is the value. */
 	roff_setstrn(&r->strtab, name, namesz, string, strlen(string),
 	    ROFF_as == tok);
 	return(ROFF_IGN);
 }
 
 /*
  * Parse a single operator, one or two characters long.
  * If the operator is recognized, return success and advance the
  * parse point, else return failure and let the parse point unchanged.
  */
 static int
 roff_getop(const char *v, int *pos, char *res)
 {
 
 	*res = v[*pos];
 
 	switch (*res) {
 	case '+':
 		/* FALLTHROUGH */
 	case '-':
 		/* FALLTHROUGH */
 	case '*':
 		/* FALLTHROUGH */
 	case '/':
 		/* FALLTHROUGH */
 	case '%':
 		/* FALLTHROUGH */
 	case '&':
 		/* FALLTHROUGH */
 	case ':':
 		break;
 	case '<':
 		switch (v[*pos + 1]) {
 		case '=':
 			*res = 'l';
 			(*pos)++;
 			break;
 		case '>':
 			*res = '!';
 			(*pos)++;
 			break;
 		case '?':
 			*res = 'i';
 			(*pos)++;
 			break;
 		default:
 			break;
 		}
 		break;
 	case '>':
 		switch (v[*pos + 1]) {
 		case '=':
 			*res = 'g';
 			(*pos)++;
 			break;
 		case '?':
 			*res = 'a';
 			(*pos)++;
 			break;
 		default:
 			break;
 		}
 		break;
 	case '=':
 		if ('=' == v[*pos + 1])
 			(*pos)++;
 		break;
 	default:
 		return(0);
 	}
 	(*pos)++;
 
 	return(*res);
 }
 
 /*
  * Evaluate either a parenthesized numeric expression
  * or a single signed integer number.
  */
 static int
-roff_evalpar(const char *v, int *pos, int *res)
+roff_evalpar(struct roff *r, int ln,
+	const char *v, int *pos, int *res)
 {
 
 	if ('(' != v[*pos])
 		return(roff_getnum(v, pos, res));
 
 	(*pos)++;
-	if ( ! roff_evalnum(v, pos, res, 1))
+	if ( ! roff_evalnum(r, ln, v, pos, res, 1))
 		return(0);
 
 	/*
 	 * Omission of the closing parenthesis
 	 * is an error in validation mode,
 	 * but ignored in evaluation mode.
 	 */
 
 	if (')' == v[*pos])
 		(*pos)++;
 	else if (NULL == res)
 		return(0);
 
 	return(1);
 }
 
 /*
  * Evaluate a complete numeric expression.
  * Proceed left to right, there is no concept of precedence.
  */
 static int
-roff_evalnum(const char *v, int *pos, int *res, int skipwhite)
+roff_evalnum(struct roff *r, int ln, const char *v,
+	int *pos, int *res, int skipwhite)
 {
 	int		 mypos, operand2;
 	char		 operator;
 
 	if (NULL == pos) {
 		mypos = 0;
 		pos = &mypos;
 	}
 
 	if (skipwhite)
 		while (isspace((unsigned char)v[*pos]))
 			(*pos)++;
 
-	if ( ! roff_evalpar(v, pos, res))
+	if ( ! roff_evalpar(r, ln, v, pos, res))
 		return(0);
 
 	while (1) {
 		if (skipwhite)
 			while (isspace((unsigned char)v[*pos]))
 				(*pos)++;
 
 		if ( ! roff_getop(v, pos, &operator))
 			break;
 
 		if (skipwhite)
 			while (isspace((unsigned char)v[*pos]))
 				(*pos)++;
 
-		if ( ! roff_evalpar(v, pos, &operand2))
+		if ( ! roff_evalpar(r, ln, v, pos, &operand2))
 			return(0);
 
 		if (skipwhite)
 			while (isspace((unsigned char)v[*pos]))
 				(*pos)++;
 
 		if (NULL == res)
 			continue;
 
 		switch (operator) {
 		case '+':
 			*res += operand2;
 			break;
 		case '-':
 			*res -= operand2;
 			break;
 		case '*':
 			*res *= operand2;
 			break;
 		case '/':
+			if (0 == operand2) {
+				mandoc_msg(MANDOCERR_DIVZERO,
+					r->parse, ln, *pos, v);
+				*res = 0;
+				break;
+			}
 			*res /= operand2;
 			break;
 		case '%':
 			*res %= operand2;
 			break;
 		case '<':
 			*res = *res < operand2;
 			break;
 		case '>':
 			*res = *res > operand2;
 			break;
 		case 'l':
 			*res = *res <= operand2;
 			break;
 		case 'g':
 			*res = *res >= operand2;
 			break;
 		case '=':
 			*res = *res == operand2;
 			break;
 		case '!':
 			*res = *res != operand2;
 			break;
 		case '&':
 			*res = *res && operand2;
 			break;
 		case ':':
 			*res = *res || operand2;
 			break;
 		case 'i':
 			if (operand2 < *res)
 				*res = operand2;
 			break;
 		case 'a':
 			if (operand2 > *res)
 				*res = operand2;
 			break;
 		default:
 			abort();
 		}
 	}
 	return(1);
 }
 
 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;
 }
 
 /*
  * Handle some predefined read-only number registers.
  * For now, return -1 if the requested register is not predefined;
  * in case a predefined read-only register having the value -1
  * were to turn up, another special value would have to be chosen.
  */
 static int
 roff_getregro(const char *name)
 {
 
 	switch (*name) {
 	case 'A':  /* ASCII approximation mode is always off. */
 		return(0);
 	case 'g':  /* Groff compatibility mode is always on. */
 		return(1);
 	case 'H':  /* Fixed horizontal resolution. */
 		return (24);
 	case 'j':  /* Always adjust left margin only. */
 		return(0);
 	case 'T':  /* Some output device is always defined. */
 		return(1);
 	case 'V':  /* Fixed vertical resolution. */
 		return (40);
 	default:
 		return (-1);
 	}
 }
 
 int
 roff_getreg(const struct roff *r, const char *name)
 {
 	struct roffreg	*reg;
 	int		 val;
 
 	if ('.' == name[0] && '\0' != name[1] && '\0' == name[2]) {
 		val = roff_getregro(name + 1);
 		if (-1 != val)
 			return (val);
 	}
 
 	for (reg = r->regtab; reg; reg = reg->next)
 		if (0 == strcmp(name, reg->key.p))
 			return(reg->val);
 
 	return(0);
 }
 
 static int
 roff_getregn(const struct roff *r, const char *name, size_t len)
 {
 	struct roffreg	*reg;
 	int		 val;
 
 	if ('.' == name[0] && 2 == len) {
 		val = roff_getregro(name + 1);
 		if (-1 != val)
 			return (val);
 	}
 
 	for (reg = r->regtab; reg; reg = reg->next)
 		if (len == reg->key.sz &&
 		    0 == strncmp(name, reg->key.p, len))
 			return(reg->val);
 
 	return(0);
 }
 
 static void
 roff_freereg(struct roffreg *reg)
 {
 	struct roffreg	*old_reg;
 
 	while (NULL != reg) {
 		free(reg->key.p);
 		old_reg = reg;
 		reg = reg->next;
 		free(old_reg);
 	}
 }
 
 static enum rofferr
 roff_nr(ROFF_ARGS)
 {
 	char		*key, *val;
 	size_t		 keysz;
 	int		 iv;
 	char		 sign;
 
-	key = val = *bufp + pos;
-	if ('\0' == *key)
+	key = val = buf->buf + pos;
+	if (*key == '\0')
 		return(ROFF_IGN);
 
 	keysz = roff_getname(r, &val, ln, pos);
-	if ('\\' == key[keysz])
+	if (key[keysz] == '\\')
 		return(ROFF_IGN);
 	key[keysz] = '\0';
 
 	sign = *val;
-	if ('+' == sign || '-' == sign)
+	if (sign == '+' || sign == '-')
 		val++;
 
-	if (roff_evalnum(val, NULL, &iv, 0))
+	if (roff_evalnum(r, ln, val, NULL, &iv, 0))
 		roff_setreg(r, key, iv, sign);
 
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_rr(ROFF_ARGS)
 {
 	struct roffreg	*reg, **prev;
 	char		*name, *cp;
 	size_t		 namesz;
 
-	name = cp = *bufp + pos;
-	if ('\0' == *name)
+	name = cp = buf->buf + pos;
+	if (*name == '\0')
 		return(ROFF_IGN);
 	namesz = roff_getname(r, &cp, ln, pos);
 	name[namesz] = '\0';
 
 	prev = &r->regtab;
 	while (1) {
 		reg = *prev;
-		if (NULL == reg || !strcmp(name, reg->key.p))
+		if (reg == NULL || !strcmp(name, reg->key.p))
 			break;
 		prev = ®->next;
 	}
-	if (NULL != reg) {
+	if (reg != NULL) {
 		*prev = reg->next;
 		free(reg->key.p);
 		free(reg);
 	}
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_rm(ROFF_ARGS)
 {
 	const char	 *name;
 	char		 *cp;
 	size_t		  namesz;
 
-	cp = *bufp + pos;
-	while ('\0' != *cp) {
+	cp = buf->buf + pos;
+	while (*cp != '\0') {
 		name = cp;
-		namesz = roff_getname(r, &cp, ln, (int)(cp - *bufp));
+		namesz = roff_getname(r, &cp, ln, (int)(cp - buf->buf));
 		roff_setstrn(&r->strtab, name, namesz, NULL, 0, 0);
-		if ('\\' == name[namesz])
+		if (name[namesz] == '\\')
 			break;
 	}
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_it(ROFF_ARGS)
 {
 	char		*cp;
 	size_t		 len;
 	int		 iv;
 
 	/* Parse the number of lines. */
-	cp = *bufp + pos;
+	cp = buf->buf + pos;
 	len = strcspn(cp, " \t");
 	cp[len] = '\0';
 	if ((iv = mandoc_strntoi(cp, len, 10)) <= 0) {
 		mandoc_msg(MANDOCERR_IT_NONUM, r->parse,
-		    ln, ppos, *bufp + 1);
+		    ln, ppos, buf->buf + 1);
 		return(ROFF_IGN);
 	}
 	cp += len + 1;
 
 	/* Arm the input line trap. */
 	roffit_lines = iv;
 	roffit_macro = mandoc_strdup(cp);
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_Dd(ROFF_ARGS)
 {
 	const char *const	*cp;
 
-	if (0 == ((MPARSE_MDOC | MPARSE_QUICK) & r->options))
+	if ((r->options & (MPARSE_MDOC | MPARSE_QUICK)) == 0)
 		for (cp = __mdoc_reserved; *cp; cp++)
 			roff_setstr(r, *cp, NULL, 0);
 
+	if (r->format == 0)
+		r->format = MPARSE_MDOC;
+
 	return(ROFF_CONT);
 }
 
 static enum rofferr
 roff_TH(ROFF_ARGS)
 {
 	const char *const	*cp;
 
-	if (0 == (MPARSE_QUICK & r->options))
+	if ((r->options & MPARSE_QUICK) == 0)
 		for (cp = __man_reserved; *cp; cp++)
 			roff_setstr(r, *cp, NULL, 0);
 
+	if (r->format == 0)
+		r->format = MPARSE_MAN;
+
 	return(ROFF_CONT);
 }
 
 static enum rofferr
 roff_TE(ROFF_ARGS)
 {
 
 	if (NULL == r->tbl)
 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
 		    ln, ppos, "TE");
 	else
 		tbl_end(&r->tbl);
 
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_T_(ROFF_ARGS)
 {
 
 	if (NULL == r->tbl)
 		mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse,
 		    ln, ppos, "T&");
 	else
 		tbl_restart(ppos, ln, r->tbl);
 
 	return(ROFF_IGN);
 }
 
-#if 0
-static int
-roff_closeeqn(struct roff *r)
+/*
+ * Handle in-line equation delimiters.
+ */
+static enum rofferr
+roff_eqndelim(struct roff *r, struct buf *buf, int pos)
 {
+	char		*cp1, *cp2;
+	const char	*bef_pr, *bef_nl, *mac, *aft_nl, *aft_pr;
 
-	return(r->eqn && ROFF_EQN == eqn_end(&r->eqn) ? 1 : 0);
-}
-#endif
+	/*
+	 * Outside equations, look for an opening delimiter.
+	 * If we are inside an equation, we already know it is
+	 * in-line, or this function wouldn't have been called;
+	 * so look for a closing delimiter.
+	 */
 
-static void
-roff_openeqn(struct roff *r, const char *name, int line,
-		int offs, const char *buf)
-{
-	struct eqn_node *e;
-	int		 poff;
+	cp1 = buf->buf + pos;
+	cp2 = strchr(cp1, r->eqn == NULL ?
+	    r->last_eqn->odelim : r->last_eqn->cdelim);
+	if (cp2 == NULL)
+		return(ROFF_CONT);
 
-	assert(NULL == r->eqn);
-	e = eqn_alloc(name, offs, line, r->parse);
+	*cp2++ = '\0';
+	bef_pr = bef_nl = aft_nl = aft_pr = "";
 
-	if (r->last_eqn)
-		r->last_eqn->next = e;
-	else
-		r->first_eqn = r->last_eqn = e;
+	/* Handle preceding text, protecting whitespace. */
 
-	r->eqn = r->last_eqn = e;
+	if (*buf->buf != '\0') {
+		if (r->eqn == NULL)
+			bef_pr = "\\&";
+		bef_nl = "\n";
+	}
 
-	if (buf) {
-		poff = 0;
-		eqn_read(&r->eqn, line, buf, offs, &poff);
+	/*
+	 * Prepare replacing the delimiter with an equation macro
+	 * and drop leading white space from the equation.
+	 */
+
+	if (r->eqn == NULL) {
+		while (*cp2 == ' ')
+			cp2++;
+		mac = ".EQ";
+	} else
+		mac = ".EN";
+
+	/* Handle following text, protecting whitespace. */
+
+	if (*cp2 != '\0') {
+		aft_nl = "\n";
+		if (r->eqn != NULL)
+			aft_pr = "\\&";
 	}
+
+	/* Do the actual replacement. */
+
+	buf->sz = mandoc_asprintf(&cp1, "%s%s%s%s%s%s%s", buf->buf,
+	    bef_pr, bef_nl, mac, aft_nl, aft_pr, cp2) + 1;
+	free(buf->buf);
+	buf->buf = cp1;
+
+	/* Toggle the in-line state of the eqn subsystem. */
+
+	r->eqn_inline = r->eqn == NULL;
+	return(ROFF_REPARSE);
 }
 
 static enum rofferr
 roff_EQ(ROFF_ARGS)
 {
+	struct eqn_node *e;
 
-	roff_openeqn(r, *bufp + pos, ln, ppos, NULL);
+	assert(r->eqn == NULL);
+	e = eqn_alloc(ppos, ln, r->parse);
+
+	if (r->last_eqn) {
+		r->last_eqn->next = e;
+		e->delim = r->last_eqn->delim;
+		e->odelim = r->last_eqn->odelim;
+		e->cdelim = r->last_eqn->cdelim;
+	} else
+		r->first_eqn = r->last_eqn = e;
+
+	r->eqn = r->last_eqn = e;
+
+	if (buf->buf[pos] != '\0')
+		mandoc_vmsg(MANDOCERR_ARG_SKIP, r->parse, ln, pos,
+		    ".EQ %s", buf->buf + pos);
+
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_EN(ROFF_ARGS)
 {
 
 	mandoc_msg(MANDOCERR_BLK_NOTOPEN, r->parse, ln, ppos, "EN");
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_TS(ROFF_ARGS)
 {
 	struct tbl_node	*tbl;
 
 	if (r->tbl) {
 		mandoc_msg(MANDOCERR_BLK_BROKEN, r->parse,
 		    ln, ppos, "TS breaks TS");
 		tbl_end(&r->tbl);
 	}
 
 	tbl = tbl_alloc(ppos, ln, r->parse);
 
 	if (r->last_tbl)
 		r->last_tbl->next = tbl;
 	else
 		r->first_tbl = r->last_tbl = tbl;
 
 	r->tbl = r->last_tbl = tbl;
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_cc(ROFF_ARGS)
 {
 	const char	*p;
 
-	p = *bufp + pos;
+	p = buf->buf + pos;
 
-	if ('\0' == *p || '.' == (r->control = *p++))
+	if (*p == '\0' || (r->control = *p++) == '.')
 		r->control = 0;
 
-	if ('\0' != *p)
+	if (*p != '\0')
 		mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
 
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_tr(ROFF_ARGS)
 {
 	const char	*p, *first, *second;
 	size_t		 fsz, ssz;
 	enum mandoc_esc	 esc;
 
-	p = *bufp + pos;
+	p = buf->buf + pos;
 
-	if ('\0' == *p) {
+	if (*p == '\0') {
 		mandoc_msg(MANDOCERR_ARGCOUNT, r->parse, ln, ppos, NULL);
 		return(ROFF_IGN);
 	}
 
-	while ('\0' != *p) {
+	while (*p != '\0') {
 		fsz = ssz = 1;
 
 		first = p++;
-		if ('\\' == *first) {
+		if (*first == '\\') {
 			esc = mandoc_escape(&p, NULL, NULL);
-			if (ESCAPE_ERROR == esc) {
+			if (esc == ESCAPE_ERROR) {
 				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
-				    ln, (int)(p - *bufp), first);
+				    ln, (int)(p - buf->buf), first);
 				return(ROFF_IGN);
 			}
 			fsz = (size_t)(p - first);
 		}
 
 		second = p++;
-		if ('\\' == *second) {
+		if (*second == '\\') {
 			esc = mandoc_escape(&p, NULL, NULL);
-			if (ESCAPE_ERROR == esc) {
+			if (esc == ESCAPE_ERROR) {
 				mandoc_msg(MANDOCERR_ESC_BAD, r->parse,
-				    ln, (int)(p - *bufp), second);
+				    ln, (int)(p - buf->buf), second);
 				return(ROFF_IGN);
 			}
 			ssz = (size_t)(p - second);
-		} else if ('\0' == *second) {
+		} else if (*second == '\0') {
 			mandoc_msg(MANDOCERR_ARGCOUNT, r->parse,
-			    ln, (int)(p - *bufp), NULL);
+			    ln, (int)(p - buf->buf), NULL);
 			second = " ";
 			p--;
 		}
 
 		if (fsz > 1) {
 			roff_setstrn(&r->xmbtab, first, fsz,
 			    second, ssz, 0);
 			continue;
 		}
 
-		if (NULL == r->xtab)
+		if (r->xtab == NULL)
 			r->xtab = mandoc_calloc(128,
 			    sizeof(struct roffstr));
 
 		free(r->xtab[(int)*first].p);
 		r->xtab[(int)*first].p = mandoc_strndup(second, ssz);
 		r->xtab[(int)*first].sz = ssz;
 	}
 
 	return(ROFF_IGN);
 }
 
 static enum rofferr
 roff_so(ROFF_ARGS)
 {
 	char *name;
 
-	name = *bufp + pos;
+	name = buf->buf + pos;
 	mandoc_vmsg(MANDOCERR_SO, r->parse, ln, ppos, "so %s", name);
 
 	/*
 	 * Handle `so'.  Be EXTREMELY careful, as we shouldn't be
 	 * opening anything that's not in our cwd or anything beneath
 	 * it.  Thus, explicitly disallow traversing up the file-system
 	 * or using absolute paths.
 	 */
 
-	if ('/' == *name || strstr(name, "../") || strstr(name, "/..")) {
+	if (*name == '/' || strstr(name, "../") || strstr(name, "/..")) {
 		mandoc_vmsg(MANDOCERR_SO_PATH, r->parse, ln, ppos,
 		    ".so %s", name);
 		return(ROFF_ERR);
 	}
 
 	*offs = pos;
 	return(ROFF_SO);
 }
 
 static enum rofferr
 roff_userdef(ROFF_ARGS)
 {
 	const char	 *arg[9];
 	char		 *cp, *n1, *n2;
 	int		  i;
 
 	/*
 	 * Collect pointers to macro argument strings
 	 * and NUL-terminate them.
 	 */
-	cp = *bufp + pos;
+	cp = buf->buf + pos;
 	for (i = 0; i < 9; i++)
-		arg[i] = '\0' == *cp ? "" :
+		arg[i] = *cp == '\0' ? "" :
 		    mandoc_getarg(r->parse, &cp, ln, &pos);
 
 	/*
 	 * Expand macro arguments.
 	 */
-	*szp = 0;
+	buf->sz = 0;
 	n1 = cp = mandoc_strdup(r->current_string);
-	while (NULL != (cp = strstr(cp, "\\$"))) {
+	while ((cp = strstr(cp, "\\$")) != NULL) {
 		i = cp[2] - '1';
 		if (0 > i || 8 < i) {
 			/* Not an argument invocation. */
 			cp += 2;
 			continue;
 		}
 		*cp = '\0';
-		*szp = mandoc_asprintf(&n2, "%s%s%s",
+		buf->sz = mandoc_asprintf(&n2, "%s%s%s",
 		    n1, arg[i], cp + 3) + 1;
 		cp = n2 + (cp - n1);
 		free(n1);
 		n1 = n2;
 	}
 
 	/*
 	 * Replace the macro invocation
 	 * by the expanded macro.
 	 */
-	free(*bufp);
-	*bufp = n1;
-	if (0 == *szp)
-		*szp = strlen(*bufp) + 1;
+	free(buf->buf);
+	buf->buf = n1;
+	if (buf->sz == 0)
+		buf->sz = strlen(buf->buf) + 1;
 
-	return(*szp > 1 && '\n' == (*bufp)[(int)*szp - 2] ?
+	return(buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ?
 	   ROFF_REPARSE : ROFF_APPEND);
 }
 
 static size_t
 roff_getname(struct roff *r, char **cpp, int ln, int pos)
 {
 	char	 *name, *cp;
 	size_t	  namesz;
 
 	name = *cpp;
 	if ('\0' == *name)
 		return(0);
 
 	/* Read until end of name and terminate it with NUL. */
 	for (cp = name; 1; cp++) {
 		if ('\0' == *cp || ' ' == *cp) {
 			namesz = cp - name;
 			break;
 		}
 		if ('\\' != *cp)
 			continue;
 		namesz = cp - name;
 		if ('{' == cp[1] || '}' == cp[1])
 			break;
 		cp++;
 		if ('\\' == *cp)
 			continue;
 		mandoc_vmsg(MANDOCERR_NAMESC, r->parse, ln, pos,
 		    "%.*s", (int)(cp - name + 1), name);
 		mandoc_escape((const char **)&cp, NULL, NULL);
 		break;
 	}
 
 	/* Read past spaces. */
 	while (' ' == *cp)
 		cp++;
 
 	*cpp = cp;
 	return(namesz);
 }
 
 /*
  * Store *string into the user-defined string called *name.
  * To clear an existing entry, call with (*r, *name, NULL, 0).
  * append == 0: replace mode
  * append == 1: single-line append mode
  * append == 2: multiline append mode, append '\n' after each call
  */
 static void
 roff_setstr(struct roff *r, const char *name, const char *string,
 	int append)
 {
 
 	roff_setstrn(&r->strtab, name, strlen(name), string,
 	    string ? strlen(string) : 0, append);
 }
 
 static void
 roff_setstrn(struct roffkv **r, const char *name, size_t namesz,
 		const char *string, size_t stringsz, int append)
 {
 	struct roffkv	*n;
 	char		*c;
 	int		 i;
 	size_t		 oldch, newch;
 
 	/* Search for an existing string with the same name. */
 	n = *r;
 
 	while (n && (namesz != n->key.sz ||
 			strncmp(n->key.p, name, namesz)))
 		n = n->next;
 
 	if (NULL == n) {
 		/* Create a new string table entry. */
 		n = mandoc_malloc(sizeof(struct roffkv));
 		n->key.p = mandoc_strndup(name, namesz);
 		n->key.sz = namesz;
 		n->val.p = NULL;
 		n->val.sz = 0;
 		n->next = *r;
 		*r = n;
 	} else if (0 == append) {
 		free(n->val.p);
 		n->val.p = NULL;
 		n->val.sz = 0;
 	}
 
 	if (NULL == string)
 		return;
 
 	/*
 	 * One additional byte for the '\n' in multiline mode,
 	 * and one for the terminating '\0'.
 	 */
 	newch = stringsz + (1 < append ? 2u : 1u);
 
 	if (NULL == n->val.p) {
 		n->val.p = mandoc_malloc(newch);
 		*n->val.p = '\0';
 		oldch = 0;
 	} else {
 		oldch = n->val.sz;
 		n->val.p = mandoc_realloc(n->val.p, oldch + newch);
 	}
 
 	/* Skip existing content in the destination buffer. */
 	c = n->val.p + (int)oldch;
 
 	/* Append new content to the destination buffer. */
 	i = 0;
 	while (i < (int)stringsz) {
 		/*
 		 * Rudimentary roff copy mode:
 		 * Handle escaped backslashes.
 		 */
 		if ('\\' == string[i] && '\\' == string[i + 1])
 			i++;
 		*c++ = string[i++];
 	}
 
 	/* Append terminating bytes. */
 	if (1 < append)
 		*c++ = '\n';
 
 	*c = '\0';
 	n->val.sz = (int)(c - n->val.p);
 }
 
 static const char *
 roff_getstrn(const struct roff *r, const char *name, size_t len)
 {
 	const struct roffkv *n;
 	int i;
 
 	for (n = r->strtab; n; n = n->next)
 		if (0 == strncmp(name, n->key.p, len) &&
 		    '\0' == n->key.p[(int)len])
 			return(n->val.p);
 
 	for (i = 0; i < PREDEFS_MAX; i++)
 		if (0 == strncmp(name, predefs[i].name, len) &&
 				'\0' == predefs[i].name[(int)len])
 			return(predefs[i].str);
 
 	return(NULL);
 }
 
 static void
 roff_freestr(struct roffkv *r)
 {
 	struct roffkv	 *n, *nn;
 
 	for (n = r; n; n = nn) {
 		free(n->key.p);
 		free(n->val.p);
 		nn = n->next;
 		free(n);
 	}
 }
 
 const struct tbl_span *
 roff_span(const struct roff *r)
 {
 
 	return(r->tbl ? tbl_span(r->tbl) : NULL);
 }
 
 const struct eqn *
 roff_eqn(const struct roff *r)
 {
 
 	return(r->last_eqn ? &r->last_eqn->eqn : NULL);
 }
 
 /*
  * Duplicate an input string, making the appropriate character
  * conversations (as stipulated by `tr') along the way.
  * Returns a heap-allocated string with all the replacements made.
  */
 char *
 roff_strdup(const struct roff *r, const char *p)
 {
 	const struct roffkv *cp;
 	char		*res;
 	const char	*pp;
 	size_t		 ssz, sz;
 	enum mandoc_esc	 esc;
 
 	if (NULL == r->xmbtab && NULL == r->xtab)
 		return(mandoc_strdup(p));
 	else if ('\0' == *p)
 		return(mandoc_strdup(""));
 
 	/*
 	 * Step through each character looking for term matches
 	 * (remember that a `tr' can be invoked with an escape, which is
 	 * a glyph but the escape is multi-character).
 	 * We only do this if the character hash has been initialised
 	 * and the string is >0 length.
 	 */
 
 	res = NULL;
 	ssz = 0;
 
 	while ('\0' != *p) {
 		if ('\\' != *p && r->xtab && r->xtab[(int)*p].p) {
 			sz = r->xtab[(int)*p].sz;
 			res = mandoc_realloc(res, ssz + sz + 1);
 			memcpy(res + ssz, r->xtab[(int)*p].p, sz);
 			ssz += sz;
 			p++;
 			continue;
 		} else if ('\\' != *p) {
 			res = mandoc_realloc(res, ssz + 2);
 			res[ssz++] = *p++;
 			continue;
 		}
 
 		/* Search for term matches. */
 		for (cp = r->xmbtab; cp; cp = cp->next)
 			if (0 == strncmp(p, cp->key.p, cp->key.sz))
 				break;
 
 		if (NULL != cp) {
 			/*
 			 * A match has been found.
 			 * Append the match to the array and move
 			 * forward by its keysize.
 			 */
 			res = mandoc_realloc(res,
 			    ssz + cp->val.sz + 1);
 			memcpy(res + ssz, cp->val.p, cp->val.sz);
 			ssz += cp->val.sz;
 			p += (int)cp->key.sz;
 			continue;
 		}
 
 		/*
 		 * Handle escapes carefully: we need to copy
 		 * over just the escape itself, or else we might
 		 * do replacements within the escape itself.
 		 * Make sure to pass along the bogus string.
 		 */
 		pp = p++;
 		esc = mandoc_escape(&p, NULL, NULL);
 		if (ESCAPE_ERROR == esc) {
 			sz = strlen(pp);
 			res = mandoc_realloc(res, ssz + sz + 1);
 			memcpy(res + ssz, pp, sz);
 			break;
 		}
 		/*
 		 * We bail out on bad escapes.
 		 * No need to warn: we already did so when
 		 * roff_res() was called.
 		 */
 		sz = (int)(p - pp);
 		res = mandoc_realloc(res, ssz + sz + 1);
 		memcpy(res + ssz, pp, sz);
 		ssz += sz;
 	}
 
 	res[(int)ssz] = '\0';
 	return(res);
+}
+
+int
+roff_getformat(const struct roff *r)
+{
+
+	return(r->format);
 }
 
 /*
  * 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);
 }
Index: vendor/mdocml/dist/st.c
===================================================================
--- vendor/mdocml/dist/st.c	(revision 275396)
+++ vendor/mdocml/dist/st.c	(revision 275397)
@@ -1,36 +1,36 @@
-/*	$Id: st.c,v 1.10 2014/03/23 11:25:26 schwarze Exp $ */
+/*	$Id: st.c,v 1.11 2014/08/10 23:54:41 schwarze Exp $ */
 /*
  * Copyright (c) 2009 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
+
+#include 
 
 #include 
 
 #include "mdoc.h"
 #include "libmdoc.h"
 
 #define LINE(x, y) \
 	if (0 == strcmp(p, x)) return(y);
 
 const char *
 mdoc_a2st(const char *p)
 {
 
 #include "st.in"
 
 	return(NULL);
 }
Index: vendor/mdocml/dist/st.in
===================================================================
--- vendor/mdocml/dist/st.in	(revision 275396)
+++ vendor/mdocml/dist/st.in	(revision 275397)
@@ -1,83 +1,80 @@
-/*	$Id: st.in,v 1.24 2014/04/20 16:46:05 schwarze Exp $ */
+/*	$Id: st.in,v 1.26 2014/11/16 20:46:21 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
 /*
  * This file defines the .St macro arguments.  If you add a new
  * standard, make sure that the left-and side corresponds to the .St
  * argument (like .St -p1003.1) and the right-hand side corresponds to
  * the formatted output string.
  *
  * Be sure to escape strings.
  * The non-breaking blanks prevent ending an output line right before
  * a number.  Groff prevent line breaks at the same places.
  *
  * REMEMBER TO ADD NEW STANDARDS TO MDOC.7!
  */
 
 LINE("-p1003.1-88",	"IEEE Std 1003.1-1988 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1-90",	"IEEE Std 1003.1-1990 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1-96",	"ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1-2001",	"IEEE Std 1003.1-2001 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1-2004",	"IEEE Std 1003.1-2004 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1-2008",	"IEEE Std 1003.1-2008 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1-2013",	"IEEE Std 1003.1-2008/Cor 1-2013 (\\(lqPOSIX.1\\(rq)")
 LINE("-p1003.1",	"IEEE Std 1003.1 (\\(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",	"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)")
 LINE("-isoC-tcor1",	"ISO/IEC 9899/TCOR1:1994 (\\(lqISO\\~C90, Technical Corrigendum 1\\(rq)")
 LINE("-isoC-tcor2",	"ISO/IEC 9899/TCOR2:1995 (\\(lqISO\\~C90, Technical Corrigendum 2\\(rq)")
 LINE("-isoC-99",	"ISO/IEC 9899:1999 (\\(lqISO\\~C99\\(rq)")
 LINE("-isoC-2011",	"ISO/IEC 9899:2011 (\\(lqISO\\~C11\\(rq)")
 LINE("-iso9945-1-90",	"ISO/IEC 9945-1:1990 (\\(lqPOSIX.1\\(rq)")
 LINE("-iso9945-1-96",	"ISO/IEC 9945-1:1996 (\\(lqPOSIX.1\\(rq)")
 LINE("-iso9945-2-93",	"ISO/IEC 9945-2:1993 (\\(lqPOSIX.2\\(rq)")
 LINE("-ansiC",		"ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)")
 LINE("-ansiC-89",	"ANSI X3.159-1989 (\\(lqANSI\\~C89\\(rq)")
 LINE("-ansiC-99",	"ANSI/ISO/IEC 9899-1999 (\\(lqANSI\\~C99\\(rq)")
 LINE("-ieee754",	"IEEE Std 754-1985")
 LINE("-iso8802-3",	"ISO 8802-3: 1989")
 LINE("-iso8601",	"ISO 8601")
 LINE("-ieee1275-94",	"IEEE Std 1275-1994 (\\(lqOpen Firmware\\(rq)")
 LINE("-xpg3",		"X/Open Portability Guide Issue\\~3 (\\(lqXPG3\\(rq)")
 LINE("-xpg4",		"X/Open Portability Guide Issue\\~4 (\\(lqXPG4\\(rq)")
 LINE("-xpg4.2",		"X/Open Portability Guide Issue\\~4, Version\\~2 (\\(lqXPG4.2\\(rq)")
 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("-susv1",		"Version\\~1 of the Single UNIX Specification (\\(lqSUSv1\\(rq)")
 LINE("-susv2",		"Version\\~2 of the Single UNIX Specification (\\(lqSUSv2\\(rq)")
 LINE("-susv3",		"Version\\~3 of the Single UNIX Specification (\\(lqSUSv3\\(rq)")
+LINE("-susv4",		"Version\\~4 of the Single UNIX Specification (\\(lqSUSv4\\(rq)")
 LINE("-svid4",		"System\\~V Interface Definition, Fourth Edition (\\(lqSVID4\\(rq)")
Index: vendor/mdocml/dist/style.css
===================================================================
--- vendor/mdocml/dist/style.css	(revision 275396)
+++ vendor/mdocml/dist/style.css	(revision 275397)
@@ -1,144 +1,145 @@
-/* $Id: style.css,v 1.25 2011/08/26 09:03:17 kristaps Exp $ */
+/* $Id: style.css,v 1.30 2014/09/27 11:16:24 kristaps Exp $ */
 
 /*
  * This is an example style-sheet provided for mandoc(1) and the -Thtml
  * or -Txhtml output mode.
  *
  * It mimics the appearance of the traditional cvsweb output.
  *
  * See mdoc(7) and man(7) for macro explanations.
  */
 
 html		{ max-width: 880px; margin-left: 1em; }
 body		{ font-size: smaller; font-family: Helvetica,Arial,sans-serif; }
 h1		{ margin-bottom: 1ex; font-size: 110%; margin-left: -4ex; } /* Section header (Sh, SH). */
 h2		{ margin-bottom: 1ex; font-size: 105%; margin-left: -2ex; } /* Sub-section header (Ss, SS). */
 table		{ width: 100%; margin-top: 0ex; margin-bottom: 0ex; } /* All tables. */
 td		{ vertical-align: top; } /* All table cells. */
 p		{ } /* Paragraph: Pp, Lp. */
 blockquote	{ margin-left: 5ex; margin-top: 0ex; margin-bottom: 0ex; } /* D1. */
 div.section	{ margin-bottom: 2ex; margin-left: 5ex; } /* Sections (Sh, SH). */
 div.subsection	{ } /* Sub-sections (Ss, SS). */
 table.synopsis	{ } /* SYNOPSIS section table. */
+div.spacer	{ margin: 1em 0; }
 
 /* Preamble structure. */
 
 table.foot	{ font-size: smaller; margin-top: 1em; border-top: 1px dotted #dddddd; } /* Document footer. */
 td.foot-date	{ width: 50%; } /* Document footer: date. */
-td.foot-os	{ width: 50%; text-align: right; } /* Document footer: OS/source. */
+td.foot-os	{ width: 50%; } /* Document footer: OS/source. */
 table.head	{ font-size: smaller; margin-bottom: 1em; border-bottom: 1px dotted #dddddd; } /* Document header. */
 td.head-ltitle	{ width: 10%; } /* Document header: left-title. */
-td.head-vol	{ width: 80%; text-align: center; } /* Document header: volume. */
-td.head-rtitle	{ width: 10%; text-align: right; } /* Document header: right-title. */
+td.head-vol	{ width: 80%; } /* Document header: volume. */
+td.head-rtitle	{ width: 10%; } /* Document header: right-title. */
 
 /* General font modes. */
 
 i		{ } /* Italic: BI, IB, I, (implicit). */
 .emph		{ font-style: italic; font-weight: normal; } /* Emphasis: Em, Bl -emphasis. */
 b		{ } /* Bold: SB, BI, IB, BR, RB, B, (implicit). */
 .symb		{ font-style: normal; font-weight: bold; } /* Symbolic: Sy, Ms, Bf -symbolic. */
 small		{ } /* Small: SB, SM. */
 .lit		{ font-style: normal; font-weight: normal; font-family: monospace; } /* Literal: Dl, Li, Ql, Bf -literal, Bl -literal, Bl -unfilled. */
 
 /* Block modes. */
 
 .display	{ } /* Top of all Bd, D1, Dl. */
 .list		{ } /* Top of all Bl. */
 
 /* Context-specific modes. */
 
 i.addr		{ font-weight: normal; } /* Address (Ad). */
 i.arg		{ font-weight: normal; } /* Command argument (Ar). */
 span.author	{ } /* Author name (An). */
 b.cmd		{ font-style: normal; } /* Command (Cm). */ 
 b.config	{ font-style: normal; } /* Config statement (Cd). */
 span.define	{ } /* Defines (Dv). */
 span.desc	{ } /* Nd.  After em-dash. */
 b.diag		{ font-style: normal; } /* Diagnostic (Bl -diag). */
 span.env	{ } /* Environment variables (Ev). */
 span.errno	{ } /* Error string (Er). */
 i.farg		{ font-weight: normal; } /* Function argument (Fa, Fn). */
 i.file		{ font-weight: normal; } /* File (Pa). */
 b.flag		{ font-style: normal; } /* Flag (Fl, Cm). */
 b.fname		{ font-style: normal; } /* Function name (Fa, Fn, Rv). */
 i.ftype		{ font-weight: normal; } /* Function types (Ft, Fn). */
 b.includes	{ font-style: normal; } /* Header includes (In). */
 span.lib	{ } /* Library (Lb). */
 i.link-sec	{ font-weight: normal; } /* Section links (Sx). */
 b.macro		{ font-style: normal; } /* Macro-ish thing (Fd). */
 b.name		{ font-style: normal; } /* Name of utility (Nm). */
 span.opt	{ } /* Options (Op, Oo/Oc). */
 span.ref	{ } /* Citations (Rs). */
 span.ref-auth	{ } /* Reference author (%A). */
 i.ref-book	{ font-weight: normal; } /* Reference book (%B). */
 span.ref-city	{ } /* Reference city (%C). */
 span.ref-date	{ } /* Reference date (%D). */
 i.ref-issue	{ font-weight: normal; } /* Reference issuer/publisher (%I). */
 i.ref-jrnl	{ font-weight: normal; } /* Reference journal (%J). */
 span.ref-num	{ } /* Reference number (%N). */
 span.ref-opt	{ } /* Reference optionals (%O). */
 span.ref-page	{ } /* Reference page (%P). */
 span.ref-corp	{ } /* Reference corporate/foreign author (%Q). */
 span.ref-rep	{ } /* Reference report (%R). */
 span.ref-title	{ text-decoration: underline; } /* Reference title (%T). */
 span.ref-vol	{ } /* Reference volume (%V). */
 span.type	{ font-style: italic; font-weight: normal; } /* Variable types (Vt). */
 span.unix	{ } /* Unices (Ux, Ox, Nx, Fx, Bx, Bsx, Dx). */
 b.utility	{ font-style: normal; } /* Name of utility (Ex). */
 b.var		{ font-style: normal; } /* Variables (Rv). */
 
 a.link-ext	{ } /* Off-site link (Lk). */
 a.link-includes	{ } /* Include-file link (In). */
 a.link-mail	{ } /* Mailto links (Mt). */
 a.link-man	{ } /* Manual links (Xr). */
 a.link-ref	{ } /* Reference section links (%Q). */
 a.link-sec	{ } /* Section links (Sx). */
 
 /* Formatting for lists.  See mdoc(7). */
 
 dl.list-diag	{ }
 dt.list-diag	{ }
 dd.list-diag	{ }
 
 dl.list-hang	{ }
 dt.list-hang	{ }
 dd.list-hang	{ }
 
 dl.list-inset	{ }
 dt.list-inset	{ }
 dd.list-inset	{ }
 
 dl.list-ohang	{ }
 dt.list-ohang	{ }
 dd.list-ohang	{ margin-left: 0ex; }
 
 dl.list-tag	{ }
 dt.list-tag	{ }
 dd.list-tag	{ }
 
 table.list-col	{ }
 tr.list-col	{ }
 td.list-col	{ }
 
 ul.list-bul	{ list-style-type: disc; padding-left: 1em; }
 li.list-bul	{ }
 
 ul.list-dash	{ list-style-type: none; padding-left: 0em; }
 li.list-dash:before { content: "\2014  "; }
 
 ul.list-hyph	{ list-style-type: none; padding-left: 0em; }
 li.list-hyph:before { content: "\2013  "; }
 
 ul.list-item	{ list-style-type: none; padding-left: 0em; }
 li.list-item	{ }
 
 ol.list-enum	{ padding-left: 2em; }
 li.list-enum	{ }
 
 /* Equation modes.  See eqn(7). */
 
 span.eqn	{ }
 
 /* Table modes.  See tbl(7). */
 
 table.tbl	{ }
Index: vendor/mdocml/dist/tbl.7
===================================================================
--- vendor/mdocml/dist/tbl.7	(revision 275396)
+++ vendor/mdocml/dist/tbl.7	(revision 275397)
@@ -1,340 +1,352 @@
-.\"	$Id: tbl.7,v 1.18 2013/09/16 22:39:19 schwarze Exp $
+.\"	$Id: tbl.7,v 1.21 2014/11/26 17:51:55 schwarze Exp $
 .\"
 .\" Copyright (c) 2010, 2011 Kristaps Dzonsons 
+.\" Copyright (c) 2014 Ingo Schwarze 
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
 .\" copyright notice and this permission notice appear in all copies.
 .\"
 .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: September 16 2013 $
+.Dd $Mdocdate: November 26 2014 $
 .Dt TBL 7
 .Os
 .Sh NAME
 .Nm tbl
 .Nd tbl language reference for mandoc
 .Sh DESCRIPTION
 The
 .Nm tbl
 language is a table-formatting language.
 It is used within
 .Xr mdoc 7
 and
 .Xr man 7
 .Ux
 manual pages.
 This manual describes the subset of the
 .Nm
 language accepted by the
 .Xr mandoc 1
 utility.
 .Pp
 Tables within
 .Xr mdoc 7
 or
 .Xr man 7
 are enclosed by the
 .Sq TS
 and
 .Sq TE
 macro tags, whose precise syntax is documented in
 .Xr roff 7 .
 Tables consist of a series of options on a single line, followed by the
 table layout, followed by data.
 .Pp
 For example, the following creates a boxed table with digits centred in
 the cells.
 .Bd -literal -offset indent
 \&.TS
 tab(:) box;
 c5 c5 c5.
 1:2:3
 4:5:6
 \&.TE
 .Ed
 .Pp
 When formatted, the following output is produced:
 .Bd -filled -offset indent -compact
 .TS
 tab(:) box;
 c5 c5 c5.
 1:2:3
 4:5:6
 .TE
 .Ed
 .Sh TABLE STRUCTURE
 Tables are enclosed by the
 .Sq TS
 and
 .Sq TE
 .Xr roff 7
 macros.
 A table consists of an optional single line of table
 .Sx Options
 terminated by a semicolon, followed by one or more lines of
 .Sx Layout
 specifications terminated by a period, then
 .Sx Data .
 All input must be 7-bit ASCII.
 Example:
 .Bd -literal -offset indent
 \&.TS
 box tab(:);
 c | c
 | c | c.
 1:2
 3:4
 \&.TE
 .Ed
 .Pp
 Table data is
 .Em pre-processed ,
 that is, data rows are parsed then inserted into the underlying stream
 of input data.
 This allows data rows to be interspersed by arbitrary
 .Xr roff 7 ,
 .Xr mdoc 7 ,
 and
 .Xr man 7
 macros such as
 .Bd -literal -offset indent
 \&.TS
 tab(:);
 c c c.
 1:2:3
 \&.Ao
 3:2:1
 \&.Ac
 \&.TE
 .Ed
 .Pp
 in the case of
 .Xr mdoc 7
 or
 .Bd -literal -offset indent
 \&.TS
 tab(:);
 c c c.
 \&.ds ab 2
 1:\e*(ab:3
 \&.I
 3:2:1
 \&.TE
 .Ed
 .Pp
 in the case of
 .Xr man 7 .
 .Ss Options
 The first line of a table consists of space-separated option keys and
 modifiers terminated by a semicolon.
+For GNU compatibility, option keys can also be separated by commas.
 If the first line does not have a terminating semicolon, it is assumed
 that no options are specified and instead a
 .Sx Layout
 is processed.
 Some options accept arguments enclosed by parenthesis.
 The following case-insensitive options are available:
 .Bl -tag -width Ds
 .It Cm center
 This option is not supported by
 .Xr mandoc 1 .
 This may also be invoked with
 .Cm centre .
 .It Cm delim
 Accepts a two-character argument.
 This option is not supported by
 .Xr mandoc 1 .
 .It Cm expand
 This option is not supported by
 .Xr mandoc 1 .
 .It Cm box
 Draw a single-line box around the table.
 This may also be invoked with
 .Cm frame .
 .It Cm doublebox
 Draw a double-line box around the table.
 This may also be invoked with
 .Cm doubleframe .
 .It Cm allbox
 This option is not supported by
 .Xr mandoc 1 .
 .It Cm tab
 Accepts a single-character argument.
 This character is used as a delimiter between data cells, which otherwise
 defaults to the tab character.
 .It Cm linesize
 Accepts a natural number (all digits).
 This option is not supported by
 .Xr mandoc 1 .
 .It Cm nokeep
 This option is not supported by
 .Xr mandoc 1 .
 .It Cm decimalpoint
 Accepts a single-character argument.
 This character will be used as the decimal point with the
 .Cm n
 layout key.
 .It Cm nospaces
 This option is not supported by
 .Xr mandoc 1 .
 .El
 .Ss Layout
 The table layout follows
 .Sx Options
 or a
 .Sq \&T&
 macro invocation.
 Layout specifies how data rows are displayed on output.
 Each layout line corresponds to a line of data; the last layout line
 applies to all remaining data lines.
 Layout lines may also be separated by a comma.
 Each layout cell consists of one of the following case-insensitive keys:
-.Bl -tag -width Ds
+.Bl -tag -width 2n
 .It Cm c
 Centre a literal string within its column.
 .It Cm r
 Right-justify a literal string within its column.
 .It Cm l
 Left-justify a literal string within its column.
 .It Cm n
 Justify a number around its last decimal point.
 If the decimal point is not found on the number, it's assumed to trail
 the number.
 .It Cm s
 Horizontally span columns from the last
 .No non- Ns Cm s
 data cell.
 It is an error if spanning columns follow a
 .Cm \-
 or
 .Cm \(ba
 cell, or come first.
 This option is not supported by
 .Xr mandoc 1 .
 .It Cm a
 Left-justify a literal string and pad with one space.
 .It Cm ^
 Vertically span rows from the last
 .No non- Ns Cm ^
 data cell.
 It is an error to invoke a vertical span on the first layout row.
 Unlike a horizontal spanner, you must specify an empty cell (if it not
 empty, the data is discarded) in the corresponding data cell.
 .It Cm \-
 Replace the data cell (its contents will be lost) with a single
 horizontal line.
 This may also be invoked with
 .Cm _ .
 .It Cm =
 Replace the data cell (its contents will be lost) with a double
 horizontal line.
 .It Cm \(ba
 Emit a vertical bar instead of data.
 .It Cm \(ba\(ba
 Emit a double-vertical bar instead of data.
 .El
 .Pp
 Keys may be followed by a set of modifiers.
 A modifier is either a modifier key or a natural number for specifying
 the minimum width of a column.
 The following case-insensitive modifier keys are available:
-.Cm z ,
-.Cm u ,
-.Cm e ,
-.Cm t ,
+.Bl -tag -width 2n
+.It Cm b
+Use a bold font for the contents of this column.
+.It Cm e
+Make this column wider to match the maximum width
+of any other column also having the
+.Cm e
+modifier.
+.It Cm f
+The next character selects the font to use for this column.
+See the
+.Xr roff 7
+manual for supported one-character font names.
+.It Cm i
+Use an italic font for the contents of this column.
+.It Cm x
+After determining the width of all other columns, distribute the
+rest of the line length among all columns having the
+.Cm x
+modifier.
+.It Cm z
+Do not use this cell for determining the width of this column.
+.El
+.Pp
+The modifiers
 .Cm d ,
-.Cm b ,
-.Cm i ,
-.Cm r ,
+.Cm t ,
+.Cm u ,
 and
-.Cm f
-.Po
-followed by
-.Cm b ,
-.Cm i ,
-.Cm r ,
-.Cm 3 ,
-.Cm 2 ,
-or
-.Cm 1
-.Pc .
-All of these are ignored by
+.Cm w
+are ignored by
 .Xr mandoc 1 .
 .Pp
 For example, the following layout specifies a centre-justified column of
 minimum width 10, followed by vertical bar, followed by a left-justified
-column of minimum width 10, another vertical bar, then a column
-justified about the decimal point in numbers:
+column of minimum width 10, another vertical bar, then a column using
+bold font justified about the decimal point in numbers:
 .Pp
-.Dl c10 | l10 | n
+.Dl c10 | l10 | nfB
 .Ss Data
 The data section follows the last layout row.
 By default, cells in a data section are delimited by a tab.
 This behaviour may be changed with the
 .Cm tab
 option.
 If
 .Cm _
 or
 .Cm =
 is specified, a single or double line, respectively, is drawn across the
 data field.
 If
 .Cm \e-
 or
 .Cm \e=
 is specified, a line is drawn within the data field (i.e. terminating
 within the cell and not draw to the border).
 If the last cell of a line is
 .Cm T{ ,
 all subsequent lines are included as part of the cell until
 .Cm T}
 is specified as its own data cell.
 It may then be followed by a tab
 .Pq or as designated by Cm tab
 or an end-of-line to terminate the row.
 .Sh COMPATIBILITY
 This section documents compatibility between mandoc and other
 .Nm
 implementations, at this time limited to GNU tbl.
 .Pp
 .Bl -dash -compact
 .It
 In GNU tbl, comments and macros are disallowed prior to the data block
 of a table.
 The
 .Xr mandoc 1
 implementation allows them.
 .El
 .Sh SEE ALSO
 .Xr mandoc 1 ,
 .Xr man 7 ,
 .Xr mandoc_char 7 ,
 .Xr mdoc 7 ,
 .Xr roff 7
 .Rs
 .%A M. E. Lesk
 .%T Tbl\(emA Program to Format Tables
 .%D June 11, 1976
 .Re
 .Sh HISTORY
 The tbl utility, a preprocessor for troff, was originally written by M.
 E. Lesk at Bell Labs in 1975.
 The GNU reimplementation of tbl, part of the groff package, was released
 in 1990 by James Clark.
 A standalone tbl implementation was written by Kristaps Dzonsons in
 2010.
 This formed the basis of the implementation that is part of the
 .Xr mandoc 1
 utility.
 .Sh AUTHORS
 This
 .Nm
 reference was written by
 .An Kristaps Dzonsons Aq Mt kristaps@bsd.lv .
Index: vendor/mdocml/dist/tbl.c
===================================================================
--- vendor/mdocml/dist/tbl.c	(revision 275396)
+++ vendor/mdocml/dist/tbl.c	(revision 275397)
@@ -1,176 +1,176 @@
-/*	$Id: tbl.c,v 1.29 2014/04/20 16:46:05 schwarze Exp $ */
+/*	$Id: tbl.c,v 1.30 2014/08/10 23:54:41 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2011 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
+
+#include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "libmandoc.h"
 #include "libroff.h"
 
 
 enum rofferr
 tbl_read(struct tbl_node *tbl, int ln, const char *p, int offs)
 {
 	int		 len;
 	const char	*cp;
 
 	cp = &p[offs];
 	len = (int)strlen(cp);
 
 	/*
 	 * If we're in the options section and we don't have a
 	 * terminating semicolon, assume we've moved directly into the
 	 * layout section.  No need to report a warning: this is,
 	 * apparently, standard behaviour.
 	 */
 
 	if (TBL_PART_OPTS == tbl->part && len)
 		if (';' != cp[len - 1])
 			tbl->part = TBL_PART_LAYOUT;
 
 	/* Now process each logical section of the table.  */
 
 	switch (tbl->part) {
 	case TBL_PART_OPTS:
 		return(tbl_option(tbl, ln, p) ? ROFF_IGN : ROFF_ERR);
 	case TBL_PART_LAYOUT:
 		return(tbl_layout(tbl, ln, p) ? ROFF_IGN : ROFF_ERR);
 	case TBL_PART_CDATA:
 		return(tbl_cdata(tbl, ln, p) ? ROFF_TBL : ROFF_IGN);
 	default:
 		break;
 	}
 
 	/*
 	 * This only returns zero if the line is empty, so we ignore it
 	 * and continue on.
 	 */
 	return(tbl_data(tbl, ln, p) ? ROFF_TBL : ROFF_IGN);
 }
 
 struct tbl_node *
 tbl_alloc(int pos, int line, struct mparse *parse)
 {
 	struct tbl_node	*tbl;
 
 	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 *tbl)
 {
 	struct tbl_row	*rp;
 	struct tbl_cell	*cp;
 	struct tbl_span	*sp;
 	struct tbl_dat	*dp;
 	struct tbl_head	*hp;
 
 	while (NULL != (rp = tbl->first_row)) {
 		tbl->first_row = rp->next;
 		while (rp->first) {
 			cp = rp->first;
 			rp->first = cp->next;
 			free(cp);
 		}
 		free(rp);
 	}
 
 	while (NULL != (sp = tbl->first_span)) {
 		tbl->first_span = sp->next;
 		while (sp->first) {
 			dp = sp->first;
 			sp->first = dp->next;
 			if (dp->string)
 				free(dp->string);
 			free(dp);
 		}
 		free(sp);
 	}
 
 	while (NULL != (hp = tbl->first_head)) {
 		tbl->first_head = hp->next;
 		free(hp);
 	}
 
 	free(tbl);
 }
 
 void
 tbl_restart(int line, int pos, struct tbl_node *tbl)
 {
 	if (TBL_PART_CDATA == tbl->part)
 		mandoc_msg(MANDOCERR_TBLBLOCK, tbl->parse,
 		    tbl->line, tbl->pos, NULL);
 
 	tbl->part = TBL_PART_LAYOUT;
 	tbl->line = line;
 	tbl->pos = pos;
 
 	if (NULL == tbl->first_span || NULL == tbl->first_span->first)
 		mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse,
 		    tbl->line, tbl->pos, NULL);
 }
 
 const struct tbl_span *
 tbl_span(struct tbl_node *tbl)
 {
 	struct tbl_span	 *span;
 
 	assert(tbl);
 	span = tbl->current_span ? tbl->current_span->next
 				 : tbl->first_span;
 	if (span)
 		tbl->current_span = span;
 	return(span);
 }
 
 void
 tbl_end(struct tbl_node **tblp)
 {
 	struct tbl_node	*tbl;
 
 	tbl = *tblp;
 	*tblp = NULL;
 
 	if (NULL == tbl->first_span || NULL == tbl->first_span->first)
 		mandoc_msg(MANDOCERR_TBLNODATA, tbl->parse,
 		    tbl->line, tbl->pos, NULL);
 
 	if (tbl->last_span)
 		tbl->last_span->flags |= TBL_SPAN_LAST;
 
 	if (TBL_PART_CDATA == tbl->part)
 		mandoc_msg(MANDOCERR_TBLBLOCK, tbl->parse,
 		    tbl->line, tbl->pos, NULL);
 }
Index: vendor/mdocml/dist/tbl_data.c
===================================================================
--- vendor/mdocml/dist/tbl_data.c	(revision 275396)
+++ vendor/mdocml/dist/tbl_data.c	(revision 275397)
@@ -1,275 +1,275 @@
-/*	$Id: tbl_data.c,v 1.31 2014/04/23 16:08:33 schwarze Exp $ */
+/*	$Id: tbl_data.c,v 1.32 2014/08/10 23:54:41 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2011 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
+
+#include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "libmandoc.h"
 #include "libroff.h"
 
 static	int		 getdata(struct tbl_node *, struct tbl_span *,
 				int, const char *, int *);
 static	struct tbl_span	*newspan(struct tbl_node *, int,
 				struct tbl_row *);
 
 
 static int
 getdata(struct tbl_node *tbl, struct tbl_span *dp,
 		int ln, const char *p, int *pos)
 {
 	struct tbl_dat	*dat;
 	struct tbl_cell	*cp;
 	int		 sv, spans;
 
 	cp = NULL;
 	if (dp->last && dp->last->layout)
 		cp = dp->last->layout->next;
 	else if (NULL == dp->last)
 		cp = dp->layout->first;
 
 	/*
 	 * Skip over spanners, since
 	 * we want to match data with data layout cells in the header.
 	 */
 
 	while (cp && TBL_CELL_SPAN == cp->pos)
 		cp = cp->next;
 
 	/*
 	 * Stop processing when we reach the end of the available layout
 	 * cells.  This means that we have extra input.
 	 */
 
 	if (NULL == cp) {
 		mandoc_msg(MANDOCERR_TBLEXTRADAT, tbl->parse,
 		    ln, *pos, NULL);
 		/* Skip to the end... */
 		while (p[*pos])
 			(*pos)++;
 		return(1);
 	}
 
 	dat = mandoc_calloc(1, sizeof(struct tbl_dat));
 	dat->layout = cp;
 	dat->pos = TBL_DATA_NONE;
 
 	assert(TBL_CELL_SPAN != cp->pos);
 
 	for (spans = 0, cp = cp->next; cp; cp = cp->next)
 		if (TBL_CELL_SPAN == cp->pos)
 			spans++;
 		else
 			break;
 
 	dat->spans = spans;
 
 	if (dp->last) {
 		dp->last->next = dat;
 		dp->last = dat;
 	} else
 		dp->last = dp->first = dat;
 
 	sv = *pos;
 	while (p[*pos] && p[*pos] != tbl->opts.tab)
 		(*pos)++;
 
 	/*
 	 * Check for a continued-data scope opening.  This consists of a
 	 * trailing `T{' at the end of the line.  Subsequent lines,
 	 * until a standalone `T}', are included in our cell.
 	 */
 
 	if (*pos - sv == 2 && 'T' == p[sv] && '{' == p[sv + 1]) {
 		tbl->part = TBL_PART_CDATA;
 		return(1);
 	}
 
 	assert(*pos - sv >= 0);
 
 	dat->string = mandoc_malloc((size_t)(*pos - sv + 1));
 	memcpy(dat->string, &p[sv], (size_t)(*pos - sv));
 	dat->string[*pos - sv] = '\0';
 
 	if (p[*pos])
 		(*pos)++;
 
 	if ( ! strcmp(dat->string, "_"))
 		dat->pos = TBL_DATA_HORIZ;
 	else if ( ! strcmp(dat->string, "="))
 		dat->pos = TBL_DATA_DHORIZ;
 	else if ( ! strcmp(dat->string, "\\_"))
 		dat->pos = TBL_DATA_NHORIZ;
 	else if ( ! strcmp(dat->string, "\\="))
 		dat->pos = TBL_DATA_NDHORIZ;
 	else
 		dat->pos = TBL_DATA_DATA;
 
 	if (TBL_CELL_HORIZ == dat->layout->pos ||
 	    TBL_CELL_DHORIZ == dat->layout->pos ||
 	    TBL_CELL_DOWN == dat->layout->pos)
 		if (TBL_DATA_DATA == dat->pos && '\0' != *dat->string)
 			mandoc_msg(MANDOCERR_TBLIGNDATA,
 			    tbl->parse, ln, sv, NULL);
 
 	return(1);
 }
 
 int
 tbl_cdata(struct tbl_node *tbl, int ln, const char *p)
 {
 	struct tbl_dat	*dat;
 	size_t		 sz;
 	int		 pos;
 
 	pos = 0;
 
 	dat = tbl->last_span->last;
 
 	if (p[pos] == 'T' && p[pos + 1] == '}') {
 		pos += 2;
 		if (p[pos] == tbl->opts.tab) {
 			tbl->part = TBL_PART_DATA;
 			pos++;
 			return(getdata(tbl, tbl->last_span, ln, p, &pos));
 		} else if ('\0' == p[pos]) {
 			tbl->part = TBL_PART_DATA;
 			return(1);
 		}
 
 		/* Fallthrough: T} is part of a word. */
 	}
 
 	dat->pos = TBL_DATA_DATA;
 
 	if (dat->string) {
 		sz = strlen(p) + strlen(dat->string) + 2;
 		dat->string = mandoc_realloc(dat->string, sz);
 		(void)strlcat(dat->string, " ", sz);
 		(void)strlcat(dat->string, p, sz);
 	} else
 		dat->string = mandoc_strdup(p);
 
 	if (TBL_CELL_DOWN == dat->layout->pos)
 		mandoc_msg(MANDOCERR_TBLIGNDATA, tbl->parse,
 		    ln, pos, NULL);
 
 	return(0);
 }
 
 static struct tbl_span *
 newspan(struct tbl_node *tbl, int line, struct tbl_row *rp)
 {
 	struct tbl_span	*dp;
 
 	dp = mandoc_calloc(1, sizeof(struct tbl_span));
 	dp->line = line;
 	dp->opts = &tbl->opts;
 	dp->layout = rp;
 	dp->head = tbl->first_head;
 
 	if (tbl->last_span) {
 		tbl->last_span->next = dp;
 		tbl->last_span = dp;
 	} else {
 		tbl->last_span = tbl->first_span = dp;
 		tbl->current_span = NULL;
 		dp->flags |= TBL_SPAN_FIRST;
 	}
 
 	return(dp);
 }
 
 int
 tbl_data(struct tbl_node *tbl, int ln, const char *p)
 {
 	struct tbl_span	*dp;
 	struct tbl_row	*rp;
 	int		 pos;
 
 	pos = 0;
 
 	if ('\0' == p[pos]) {
 		mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, pos, NULL);
 		return(0);
 	}
 
 	/*
 	 * Choose a layout row: take the one following the last parsed
 	 * span's.  If that doesn't exist, use the last parsed span's.
 	 * If there's no last parsed span, use the first row.  Lastly,
 	 * if the last span was a horizontal line, use the same layout
 	 * (it doesn't "consume" the layout).
 	 */
 
 	if (tbl->last_span) {
 		assert(tbl->last_span->layout);
 		if (tbl->last_span->pos == TBL_SPAN_DATA) {
 			for (rp = tbl->last_span->layout->next;
 					rp && rp->first; rp = rp->next) {
 				switch (rp->first->pos) {
 				case TBL_CELL_HORIZ:
 					dp = newspan(tbl, ln, rp);
 					dp->pos = TBL_SPAN_HORIZ;
 					continue;
 				case TBL_CELL_DHORIZ:
 					dp = newspan(tbl, ln, rp);
 					dp->pos = TBL_SPAN_DHORIZ;
 					continue;
 				default:
 					break;
 				}
 				break;
 			}
 		} else
 			rp = tbl->last_span->layout;
 
 		if (NULL == rp)
 			rp = tbl->last_span->layout;
 	} else
 		rp = tbl->first_row;
 
 	assert(rp);
 
 	dp = newspan(tbl, ln, rp);
 
 	if ( ! strcmp(p, "_")) {
 		dp->pos = TBL_SPAN_HORIZ;
 		return(1);
 	} else if ( ! strcmp(p, "=")) {
 		dp->pos = TBL_SPAN_DHORIZ;
 		return(1);
 	}
 
 	dp->pos = TBL_SPAN_DATA;
 
 	/* This returns 0 when TBL_PART_CDATA is entered. */
 
 	while ('\0' != p[pos])
 		if ( ! getdata(tbl, dp, ln, p, &pos))
 			return(0);
 
 	return(1);
 }
Index: vendor/mdocml/dist/tbl_html.c
===================================================================
--- vendor/mdocml/dist/tbl_html.c	(revision 275396)
+++ vendor/mdocml/dist/tbl_html.c	(revision 275397)
@@ -1,142 +1,142 @@
-/*	$Id: tbl_html.c,v 1.11 2014/04/20 16:46:05 schwarze Exp $ */
+/*	$Id: tbl_html.c,v 1.13 2014/10/14 02:16:06 schwarze Exp $ */
 /*
  * Copyright (c) 2011 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "out.h"
 #include "html.h"
 
 static	void	 html_tblopen(struct html *, const struct tbl_span *);
 static	size_t	 html_tbl_len(size_t, void *);
 static	size_t	 html_tbl_strlen(const char *, void *);
 
 
 static size_t
 html_tbl_len(size_t sz, void *arg)
 {
 
 	return(sz);
 }
 
 static size_t
 html_tbl_strlen(const char *p, void *arg)
 {
 
 	return(strlen(p));
 }
 
 static void
 html_tblopen(struct html *h, const struct tbl_span *sp)
 {
 	const struct tbl_head *hp;
 	struct htmlpair	 tag;
 	struct roffsu	 su;
 	struct roffcol	*col;
 
 	if (TBL_SPAN_FIRST & sp->flags) {
 		h->tbl.len = html_tbl_len;
 		h->tbl.slen = html_tbl_strlen;
-		tblcalc(&h->tbl, sp);
+		tblcalc(&h->tbl, sp, 0);
 	}
 
 	assert(NULL == h->tblt);
 	PAIR_CLASS_INIT(&tag, "tbl");
 	h->tblt = print_otag(h, TAG_TABLE, 1, &tag);
 
 	for (hp = sp->head; hp; hp = hp->next) {
 		bufinit(h);
 		col = &h->tbl.cols[hp->ident];
 		SCALE_HS_INIT(&su, col->width);
 		bufcat_su(h, "width", &su);
 		PAIR_STYLE_INIT(&tag, h);
 		print_otag(h, TAG_COL, 1, &tag);
 	}
 
 	print_otag(h, TAG_TBODY, 0, NULL);
 }
 
 void
 print_tblclose(struct html *h)
 {
 
 	assert(h->tblt);
 	print_tagq(h, h->tblt);
 	h->tblt = NULL;
 }
 
 void
 print_tbl(struct html *h, const struct tbl_span *sp)
 {
 	const struct tbl_head *hp;
 	const struct tbl_dat *dp;
 	struct htmlpair	 tag;
 	struct tag	*tt;
 
 	/* Inhibit printing of spaces: we do padding ourselves. */
 
 	if (NULL == h->tblt)
 		html_tblopen(h, sp);
 
 	assert(h->tblt);
 
 	h->flags |= HTML_NONOSPACE;
 	h->flags |= HTML_NOSPACE;
 
 	tt = print_otag(h, TAG_TR, 0, NULL);
 
 	switch (sp->pos) {
 	case TBL_SPAN_HORIZ:
 		/* FALLTHROUGH */
 	case TBL_SPAN_DHORIZ:
 		PAIR_INIT(&tag, ATTR_COLSPAN, "0");
 		print_otag(h, TAG_TD, 1, &tag);
 		break;
 	default:
 		dp = sp->first;
 		for (hp = sp->head; hp; hp = hp->next) {
 			print_stagq(h, tt);
 			print_otag(h, TAG_TD, 0, NULL);
 
 			if (NULL == dp)
 				break;
 			if (TBL_CELL_DOWN != dp->layout->pos)
 				if (dp->string)
 					print_text(h, dp->string);
 			dp = dp->next;
 		}
 		break;
 	}
 
 	print_tagq(h, tt);
 
 	h->flags &= ~HTML_NONOSPACE;
 
 	if (TBL_SPAN_LAST & sp->flags) {
 		assert(h->tbl.cols);
 		free(h->tbl.cols);
 		h->tbl.cols = NULL;
 		print_tblclose(h);
 	}
 
 }
Index: vendor/mdocml/dist/tbl_layout.c
===================================================================
--- vendor/mdocml/dist/tbl_layout.c	(revision 275396)
+++ vendor/mdocml/dist/tbl_layout.c	(revision 275397)
@@ -1,400 +1,393 @@
-/*	$Id: tbl_layout.c,v 1.26 2014/04/20 16:46:05 schwarze Exp $ */
+/*	$Id: tbl_layout.c,v 1.30 2014/11/25 21:41:47 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2012, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "libmandoc.h"
 #include "libroff.h"
 
 struct	tbl_phrase {
 	char		 name;
 	enum tbl_cellt	 key;
 };
 
 /*
  * FIXME: we can make this parse a lot nicer by, when an error is
  * encountered in a layout key, bailing to the next key (i.e. to the
  * next whitespace then continuing).
  */
 
 #define	KEYS_MAX	 11
 
 static	const struct tbl_phrase keys[KEYS_MAX] = {
 	{ 'c',		 TBL_CELL_CENTRE },
 	{ 'r',		 TBL_CELL_RIGHT },
 	{ 'l',		 TBL_CELL_LEFT },
 	{ 'n',		 TBL_CELL_NUMBER },
 	{ 's',		 TBL_CELL_SPAN },
 	{ 'a',		 TBL_CELL_LONG },
 	{ '^',		 TBL_CELL_DOWN },
 	{ '-',		 TBL_CELL_HORIZ },
 	{ '_',		 TBL_CELL_HORIZ },
 	{ '=',		 TBL_CELL_DHORIZ }
 };
 
 static	int		 mods(struct tbl_node *, struct tbl_cell *,
 				int, const char *, int *);
 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, int vert);
 
 
 static int
 mods(struct tbl_node *tbl, struct tbl_cell *cp,
 		int ln, const char *p, int *pos)
 {
 	char		 buf[5];
 	int		 i;
 
 	/* Not all types accept modifiers. */
 
 	switch (cp->pos) {
 	case TBL_CELL_DOWN:
 		/* FALLTHROUGH */
 	case TBL_CELL_HORIZ:
 		/* FALLTHROUGH */
 	case TBL_CELL_DHORIZ:
 		return(1);
 	default:
 		break;
 	}
 
 mod:
 	/*
 	 * XXX: since, at least for now, modifiers are non-conflicting
 	 * (are separable by value, regardless of position), we let
 	 * modifiers come in any order.  The existing tbl doesn't let
 	 * this happen.
 	 */
 	switch (p[*pos]) {
 	case '\0':
 		/* FALLTHROUGH */
 	case ' ':
 		/* FALLTHROUGH */
 	case '\t':
 		/* FALLTHROUGH */
 	case ',':
 		/* FALLTHROUGH */
 	case '.':
 		/* FALLTHROUGH */
 	case '|':
 		return(1);
 	default:
 		break;
 	}
 
 	/* Throw away parenthesised expression. */
 
 	if ('(' == p[*pos]) {
 		(*pos)++;
 		while (p[*pos] && ')' != p[*pos])
 			(*pos)++;
 		if (')' == p[*pos]) {
 			(*pos)++;
 			goto mod;
 		}
 		mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 		    ln, *pos, NULL);
 		return(0);
 	}
 
 	/* Parse numerical spacing from modifier string. */
 
 	if (isdigit((unsigned char)p[*pos])) {
 		for (i = 0; i < 4; i++) {
 			if ( ! isdigit((unsigned char)p[*pos + i]))
 				break;
 			buf[i] = p[*pos + i];
 		}
 		buf[i] = '\0';
 
 		/* No greater than 4 digits. */
 
 		if (4 == i) {
 			mandoc_msg(MANDOCERR_TBLLAYOUT,
 			    tbl->parse, ln, *pos, NULL);
 			return(0);
 		}
 
 		*pos += i;
 		cp->spacing = (size_t)atoi(buf);
 
 		goto mod;
 		/* NOTREACHED */
 	}
 
 	/* TODO: GNU has many more extensions. */
 
 	switch (tolower((unsigned char)p[(*pos)++])) {
 	case 'z':
 		cp->flags |= TBL_CELL_WIGN;
 		goto mod;
 	case 'u':
 		cp->flags |= TBL_CELL_UP;
 		goto mod;
 	case 'e':
 		cp->flags |= TBL_CELL_EQUAL;
 		goto mod;
 	case 't':
 		cp->flags |= TBL_CELL_TALIGN;
 		goto mod;
 	case 'd':
 		cp->flags |= TBL_CELL_BALIGN;
 		goto mod;
 	case 'w':  /* XXX for now, ignore minimal column width */
 		goto mod;
+	case 'x':
+		cp->flags |= TBL_CELL_WMAX;
+		goto mod;
 	case 'f':
 		break;
 	case 'r':
 		/* FALLTHROUGH */
 	case 'b':
 		/* FALLTHROUGH */
 	case 'i':
 		(*pos)--;
 		break;
 	default:
 		mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 		    ln, *pos - 1, NULL);
 		return(0);
 	}
 
 	switch (tolower((unsigned char)p[(*pos)++])) {
 	case '3':
 		/* FALLTHROUGH */
 	case 'b':
 		cp->flags |= TBL_CELL_BOLD;
 		goto mod;
 	case '2':
 		/* FALLTHROUGH */
 	case 'i':
 		cp->flags |= TBL_CELL_ITALIC;
 		goto mod;
 	case '1':
 		/* FALLTHROUGH */
 	case 'r':
 		goto mod;
 	default:
 		break;
 	}
+	if (isalnum((unsigned char)p[*pos - 1])) {
+		mandoc_vmsg(MANDOCERR_FT_BAD, tbl->parse,
+		    ln, *pos - 1, "TS f%c", p[*pos - 1]);
+		goto mod;
+	}
 
 	mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 	    ln, *pos - 1, NULL);
 	return(0);
 }
 
 static int
 cell(struct tbl_node *tbl, struct tbl_row *rp,
 		int ln, const char *p, int *pos)
 {
 	int		 vert, i;
 	enum tbl_cellt	 c;
 
 	/* Handle vertical lines. */
 
 	for (vert = 0; '|' == p[*pos]; ++*pos)
 		vert++;
 	while (' ' == p[*pos])
 		(*pos)++;
 
 	/* Handle trailing vertical lines */
 
 	if ('.' == p[*pos] || '\0' == p[*pos]) {
 		rp->vert = vert;
 		return(1);
 	}
 
 	/* Parse the column position (`c', `l', `r', ...). */
 
 	for (i = 0; i < KEYS_MAX; i++)
 		if (tolower((unsigned char)p[*pos]) == keys[i].name)
 			break;
 
 	if (KEYS_MAX == i) {
 		mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 		    ln, *pos, NULL);
 		return(0);
 	}
 
 	c = keys[i].key;
 
 	/*
 	 * If a span cell is found first, raise a warning and abort the
 	 * parse.  If a span cell is found and the last layout element
 	 * isn't a "normal" layout, bail.
 	 *
 	 * FIXME: recover from this somehow?
 	 */
 
 	if (TBL_CELL_SPAN == c) {
 		if (NULL == rp->first) {
 			mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse,
 			    ln, *pos, NULL);
 			return(0);
 		} else if (rp->last)
 			switch (rp->last->pos) {
 			case TBL_CELL_HORIZ:
 				/* FALLTHROUGH */
 			case TBL_CELL_DHORIZ:
 				mandoc_msg(MANDOCERR_TBLLAYOUT,
 				    tbl->parse, ln, *pos, NULL);
 				return(0);
 			default:
 				break;
 			}
 	}
 
 	/*
 	 * If a vertical spanner is found, we may not be in the first
 	 * row.
 	 */
 
 	if (TBL_CELL_DOWN == c && rp == tbl->first_row) {
 		mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos, NULL);
 		return(0);
 	}
 
 	(*pos)++;
 
 	/* Disallow adjacent spacers. */
 
 	if (vert > 2) {
 		mandoc_msg(MANDOCERR_TBLLAYOUT, tbl->parse, ln, *pos - 1, NULL);
 		return(0);
 	}
 
 	/* Allocate cell then parse its modifiers. */
 
 	return(mods(tbl, cell_alloc(tbl, rp, c, vert), ln, p, pos));
 }
 
-static void
-row(struct tbl_node *tbl, int ln, const char *p, int *pos)
+int
+tbl_layout(struct tbl_node *tbl, int ln, const char *p)
 {
 	struct tbl_row	*rp;
+	int		 pos;
 
-row:	/*
-	 * EBNF describing this section:
-	 *
-	 * row		::= row_list [:space:]* [.]?[\n]
-	 * row_list	::= [:space:]* row_elem row_tail
-	 * row_tail	::= [:space:]*[,] row_list |
-	 *                  epsilon
-	 * row_elem	::= [\t\ ]*[:alpha:]+
-	 */
+	pos = 0;
+	rp = NULL;
 
-	rp = mandoc_calloc(1, sizeof(struct tbl_row));
-	if (tbl->last_row)
-		tbl->last_row->next = rp;
-	else
-		tbl->first_row = rp;
-	tbl->last_row = rp;
+	for (;;) {
+		/* Skip whitespace before and after each cell. */
 
-cell:
-	while (isspace((unsigned char)p[*pos]))
-		(*pos)++;
+		while (isspace((unsigned char)p[pos]))
+			pos++;
 
-	/* Safely exit layout context. */
-
-	if ('.' == p[*pos]) {
-		tbl->part = TBL_PART_DATA;
-		if (NULL == tbl->first_row)
+		switch (p[pos]) {
+		case ',':  /* Next row on this input line. */
+			pos++;
+			rp = NULL;
+			continue;
+		case '\0':  /* Next row on next input line. */
+			return(1);
+		case '.':  /* End of layout. */
+			pos++;
+			tbl->part = TBL_PART_DATA;
+			if (tbl->first_row != NULL)
+				return(1);
 			mandoc_msg(MANDOCERR_TBLNOLAYOUT,
-			    tbl->parse, ln, *pos, NULL);
-		(*pos)++;
-		return;
-	}
+			    tbl->parse, ln, pos, NULL);
+			rp = mandoc_calloc(1, sizeof(*rp));
+			cell_alloc(tbl, rp, TBL_CELL_LEFT, 0);
+			tbl->first_row = tbl->last_row = rp;
+			return(1);
+		default:  /* Cell. */
+			break;
+		}
 
-	/* End (and possibly restart) a row. */
-
-	if (',' == p[*pos]) {
-		(*pos)++;
-		goto row;
-	} else if ('\0' == p[*pos])
-		return;
-
-	if ( ! cell(tbl, rp, ln, p, pos))
-		return;
-
-	goto cell;
-	/* NOTREACHED */
-}
-
-int
-tbl_layout(struct tbl_node *tbl, int ln, const char *p)
-{
-	int		 pos;
-
-	pos = 0;
-	row(tbl, ln, p, &pos);
-
-	/* Always succeed. */
-	return(1);
+		if (rp == NULL) {  /* First cell on this line. */
+			rp = mandoc_calloc(1, sizeof(*rp));
+			if (tbl->last_row)
+				tbl->last_row->next = rp;
+			else
+				tbl->first_row = rp;
+			tbl->last_row = rp;
+		}
+		if ( ! cell(tbl, rp, ln, p, &pos))
+			return(1);
+	}
 }
 
 static struct tbl_cell *
 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;
 
 	p = mandoc_calloc(1, sizeof(struct tbl_cell));
 
 	if (NULL != (pp = rp->last)) {
 		pp->next = p;
 		h = pp->head->next;
 	} else {
 		rp->first = p;
 		h = tbl->first_head;
 	}
 	rp->last = p;
 
 	p->pos = pos;
 	p->vert = vert;
 
 	/* Re-use header. */
 
 	if (h) {
 		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;
 	} else
 		tbl->first_head = hp;
 	tbl->last_head = hp;
 
 	p->head = hp;
 	return(p);
 }
Index: vendor/mdocml/dist/tbl_opts.c
===================================================================
--- vendor/mdocml/dist/tbl_opts.c	(revision 275396)
+++ vendor/mdocml/dist/tbl_opts.c	(revision 275397)
@@ -1,271 +1,271 @@
-/*	$Id: tbl_opts.c,v 1.13 2014/04/20 16:46:05 schwarze Exp $ */
+/*	$Id: tbl_opts.c,v 1.15 2014/11/26 17:51:55 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "libmandoc.h"
 #include "libroff.h"
 
 enum	tbl_ident {
 	KEY_CENTRE = 0,
 	KEY_DELIM,
 	KEY_EXPAND,
 	KEY_BOX,
 	KEY_DBOX,
 	KEY_ALLBOX,
 	KEY_TAB,
 	KEY_LINESIZE,
 	KEY_NOKEEP,
 	KEY_DPOINT,
 	KEY_NOSPACE,
 	KEY_FRAME,
 	KEY_DFRAME,
 	KEY_MAX
 };
 
 struct	tbl_phrase {
 	const char	*name;
 	int		 key;
 	enum tbl_ident	 ident;
 };
 
 /* Handle Commonwealth/American spellings. */
 #define	KEY_MAXKEYS	 14
 
 /* Maximum length of key name string. */
 #define	KEY_MAXNAME	 13
 
 /* Maximum length of key number size. */
 #define	KEY_MAXNUMSZ	 10
 
 static	const struct tbl_phrase keys[KEY_MAXKEYS] = {
 	{ "center",	 TBL_OPT_CENTRE,	KEY_CENTRE},
 	{ "centre",	 TBL_OPT_CENTRE,	KEY_CENTRE},
 	{ "delim",	 0,			KEY_DELIM},
 	{ "expand",	 TBL_OPT_EXPAND,	KEY_EXPAND},
 	{ "box",	 TBL_OPT_BOX,		KEY_BOX},
 	{ "doublebox",	 TBL_OPT_DBOX,		KEY_DBOX},
 	{ "allbox",	 TBL_OPT_ALLBOX,	KEY_ALLBOX},
 	{ "frame",	 TBL_OPT_BOX,		KEY_FRAME},
 	{ "doubleframe", TBL_OPT_DBOX,		KEY_DFRAME},
 	{ "tab",	 0,			KEY_TAB},
 	{ "linesize",	 0,			KEY_LINESIZE},
 	{ "nokeep",	 TBL_OPT_NOKEEP,	KEY_NOKEEP},
 	{ "decimalpoint", 0,			KEY_DPOINT},
 	{ "nospaces",	 TBL_OPT_NOSPACE,	KEY_NOSPACE},
 };
 
 static	int		 arg(struct tbl_node *, int,
 				const char *, int *, enum tbl_ident);
 static	void		 opt(struct tbl_node *, int,
 				const char *, int *);
 
 
 static int
 arg(struct tbl_node *tbl, int ln, const char *p, int *pos, enum tbl_ident key)
 {
 	int		 i;
 	char		 buf[KEY_MAXNUMSZ];
 
 	while (isspace((unsigned char)p[*pos]))
 		(*pos)++;
 
 	/* Arguments always begin with a parenthesis. */
 
 	if ('(' != p[*pos]) {
 		mandoc_msg(MANDOCERR_TBL, tbl->parse,
 		    ln, *pos, NULL);
 		return(0);
 	}
 
 	(*pos)++;
 
 	/*
 	 * The arguments can be ANY value, so we can't just stop at the
 	 * next close parenthesis (the argument can be a closed
 	 * parenthesis itself).
 	 */
 
 	switch (key) {
 	case KEY_DELIM:
 		if ('\0' == p[(*pos)++]) {
 			mandoc_msg(MANDOCERR_TBL, tbl->parse,
 			    ln, *pos - 1, NULL);
 			return(0);
 		}
 
 		if ('\0' == p[(*pos)++]) {
 			mandoc_msg(MANDOCERR_TBL, tbl->parse,
 			    ln, *pos - 1, NULL);
 			return(0);
 		}
 		break;
 	case KEY_TAB:
 		if ('\0' != (tbl->opts.tab = p[(*pos)++]))
 			break;
 
 		mandoc_msg(MANDOCERR_TBL, tbl->parse,
 		    ln, *pos - 1, NULL);
 		return(0);
 	case KEY_LINESIZE:
 		for (i = 0; i < KEY_MAXNUMSZ && p[*pos]; i++, (*pos)++) {
 			buf[i] = p[*pos];
 			if ( ! isdigit((unsigned char)buf[i]))
 				break;
 		}
 
 		if (i < KEY_MAXNUMSZ) {
 			buf[i] = '\0';
 			tbl->opts.linesize = atoi(buf);
 			break;
 		}
 
 		mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
 		return(0);
 	case KEY_DPOINT:
 		if ('\0' != (tbl->opts.decimal = p[(*pos)++]))
 			break;
 
 		mandoc_msg(MANDOCERR_TBL, tbl->parse,
 		    ln, *pos - 1, NULL);
 		return(0);
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	/* End with a close parenthesis. */
 
 	if (')' == p[(*pos)++])
 		return(1);
 
 	mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos - 1, NULL);
 	return(0);
 }
 
 static void
 opt(struct tbl_node *tbl, int ln, const char *p, int *pos)
 {
 	int		 i, sv;
 	char		 buf[KEY_MAXNAME];
 
 	/*
 	 * Parse individual options from the stream as surrounded by
 	 * this goto.  Each pass through the routine parses out a single
 	 * option and registers it.  Option arguments are processed in
 	 * the arg() function.
 	 */
 
 again:	/*
 	 * EBNF describing this section:
 	 *
 	 * options	::= option_list [:space:]* [;][\n]
 	 * option_list	::= option option_tail
-	 * option_tail	::= [:space:]+ option_list |
+	 * option_tail	::= [,:space:]+ option_list |
 	 *		::= epsilon
 	 * option	::= [:alpha:]+ args
 	 * args		::= [:space:]* [(] [:alpha:]+ [)]
 	 */
 
 	while (isspace((unsigned char)p[*pos]))
 		(*pos)++;
 
 	/* Safe exit point. */
 
 	if (';' == p[*pos])
 		return;
 
 	/* Copy up to first non-alpha character. */
 
 	for (sv = *pos, i = 0; i < KEY_MAXNAME; i++, (*pos)++) {
 		buf[i] = (char)tolower((unsigned char)p[*pos]);
 		if ( ! isalpha((unsigned char)buf[i]))
 			break;
 	}
 
 	/* Exit if buffer is empty (or overrun). */
 
 	if (KEY_MAXNAME == i || 0 == i) {
 		mandoc_msg(MANDOCERR_TBL, tbl->parse, ln, *pos, NULL);
 		return;
 	}
 
 	buf[i] = '\0';
 
-	while (isspace((unsigned char)p[*pos]))
+	while (isspace((unsigned char)p[*pos]) || p[*pos] == ',')
 		(*pos)++;
 
 	/*
 	 * Look through all of the available keys to find one that
 	 * matches the input.  FIXME: hashtable this.
 	 */
 
 	for (i = 0; i < KEY_MAXKEYS; i++) {
 		if (strcmp(buf, keys[i].name))
 			continue;
 
 		/*
 		 * Note: this is more difficult to recover from, as we
 		 * can be anywhere in the option sequence and it's
 		 * harder to jump to the next.  Meanwhile, just bail out
 		 * of the sequence altogether.
 		 */
 
 		if (keys[i].key)
 			tbl->opts.opts |= keys[i].key;
 		else if ( ! arg(tbl, ln, p, pos, keys[i].ident))
 			return;
 
 		break;
 	}
 
 	/*
 	 * Allow us to recover from bad options by continuing to another
 	 * parse sequence.
 	 */
 
 	if (KEY_MAXKEYS == i)
 		mandoc_msg(MANDOCERR_TBLOPT, tbl->parse, ln, sv, NULL);
 
 	goto again;
 	/* NOTREACHED */
 }
 
 int
 tbl_option(struct tbl_node *tbl, int ln, const char *p)
 {
 	int		 pos;
 
 	/*
 	 * Table options are always on just one line, so automatically
 	 * switch into the next input mode here.
 	 */
 	tbl->part = TBL_PART_LAYOUT;
 
 	pos = 0;
 	opt(tbl, ln, p, &pos);
 
 	/* Always succeed. */
 	return(1);
 }
Index: vendor/mdocml/dist/tbl_term.c
===================================================================
--- vendor/mdocml/dist/tbl_term.c	(revision 275396)
+++ vendor/mdocml/dist/tbl_term.c	(revision 275397)
@@ -1,426 +1,442 @@
-/*	$Id: tbl_term.c,v 1.27 2014/04/20 16:46:05 schwarze Exp $ */
+/*	$Id: tbl_term.c,v 1.31 2014/10/14 18:18:05 schwarze Exp $ */
 /*
  * Copyright (c) 2009, 2011 Kristaps Dzonsons 
  * Copyright (c) 2011, 2012, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "out.h"
 #include "term.h"
 
 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_opts *,
 			const struct tbl_dat *,
 			const struct roffcol *);
 static	size_t	tbl_rulewidth(struct termp *, const struct tbl_head *);
 static	void	tbl_hframe(struct termp *, const struct tbl_span *, int);
 static	void	tbl_literal(struct termp *, const struct tbl_dat *,
 			const struct roffcol *);
 static	void	tbl_number(struct termp *, const struct tbl_opts *,
 			const struct tbl_dat *,
 			const struct roffcol *);
 static	void	tbl_hrule(struct termp *, const struct tbl_span *);
 static	void	tbl_vrule(struct termp *, const struct tbl_head *);
+static	void	tbl_word(struct termp *, const struct tbl_dat *);
 
 
 static size_t
 term_tbl_strlen(const char *p, void *arg)
 {
 
 	return(term_strlen((const struct termp *)arg, p));
 }
 
 static size_t
 term_tbl_len(size_t sz, void *arg)
 {
 
 	return(term_len((const struct termp *)arg, sz));
 }
 
 void
 term_tbl(struct termp *tp, const struct tbl_span *sp)
 {
 	const struct tbl_head	*hp;
 	const struct tbl_dat	*dp;
 	struct roffcol		*col;
 	int			 spans;
 	size_t			 rmargin, maxrmargin;
 
 	rmargin = tp->rmargin;
 	maxrmargin = tp->maxrmargin;
 
 	tp->rmargin = tp->maxrmargin = TERM_MAXMARGIN;
 
 	/* Inhibit printing of spaces: we do padding ourselves. */
 
 	tp->flags |= TERMP_NONOSPACE;
 	tp->flags |= TERMP_NOSPACE;
 
 	/*
 	 * The first time we're invoked for a given table block,
 	 * calculate the table widths and decimal positions.
 	 */
 
 	if (TBL_SPAN_FIRST & sp->flags) {
 		term_flushln(tp);
 
 		tp->tbl.len = term_tbl_len;
 		tp->tbl.slen = term_tbl_strlen;
 		tp->tbl.arg = tp;
 
-		tblcalc(&tp->tbl, sp);
+		tblcalc(&tp->tbl, sp, rmargin - tp->offset);
 	}
 
 	/* Horizontal frame at the start of boxed tables. */
 
 	if (TBL_SPAN_FIRST & sp->flags) {
 		if (TBL_OPT_DBOX & sp->opts->opts)
 			tbl_hframe(tp, sp, 1);
 		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 | TBL_OPT_DBOX) & sp->opts->opts ||
-	    sp->head->vert)
+	    (sp->head != NULL && sp->head->vert))
 		term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
 		    TBL_SPAN_DHORIZ == sp->pos ? "+" : "|");
 
 	/*
 	 * Now print the actual data itself depending on the span type.
 	 * Spanner spans get a horizontal rule; data spanners have their
 	 * data printed by matching data to header.
 	 */
 
 	switch (sp->pos) {
 	case TBL_SPAN_HORIZ:
 		/* FALLTHROUGH */
 	case TBL_SPAN_DHORIZ:
 		tbl_hrule(tp, sp);
 		break;
 	case TBL_SPAN_DATA:
 		/* Iterate over template headers. */
 		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.
 			 */
 
 			if (--spans >= 0)
 				continue;
 
 			/* Separate columns. */
 
 			if (NULL != hp->prev)
 				tbl_vrule(tp, hp);
 
 			col = &tp->tbl.cols[hp->ident];
 			tbl_data(tp, sp->opts, dp, col);
 
 			/*
 			 * Go to the next data cell and assign the
 			 * number of subsequent spans, if applicable.
 			 */
 
 			if (dp) {
 				spans = dp->spans;
 				dp = dp->next;
 			}
 		}
 		break;
 	}
 
 	/* Vertical frame at the end of each row. */
 
 	if ((TBL_OPT_BOX | TBL_OPT_DBOX) & sp->opts->opts ||
 	    sp->layout->vert)
 		term_word(tp, TBL_SPAN_HORIZ == sp->pos ||
 		    TBL_SPAN_DHORIZ == sp->pos ? "+" : " |");
 	term_flushln(tp);
 
 	/*
 	 * If we're the last row, clean up after ourselves: clear the
 	 * existing table configuration and set it to NULL.
 	 */
 
 	if (TBL_SPAN_LAST & sp->flags) {
 		if (TBL_OPT_DBOX & sp->opts->opts ||
 		    TBL_OPT_BOX  & sp->opts->opts) {
 			tbl_hframe(tp, sp, 0);
 			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;
 	}
 
 	tp->flags &= ~TERMP_NONOSPACE;
 	tp->rmargin = rmargin;
 	tp->maxrmargin = maxrmargin;
 
 }
 
 /*
  * Horizontal rules extend across the entire table.
  * Calculate the width by iterating over columns.
  */
 static size_t
 tbl_rulewidth(struct termp *tp, const struct tbl_head *hp)
 {
 	size_t		 width;
 
 	width = tp->tbl.cols[hp->ident].width;
 
 	/* Account for leading blanks. */
 	if (hp->prev)
 		width += 2 - hp->vert;
 
 	/* Account for trailing blank. */
 	width++;
 
 	return(width);
 }
 
 /*
  * Rules inside the table can be single or double
  * and have crossings with vertical rules marked with pluses.
  */
 static void
 tbl_hrule(struct termp *tp, const struct tbl_span *sp)
 {
 	const struct tbl_head *hp;
 	char		 c;
 
 	c = '-';
 	if (TBL_SPAN_DHORIZ == sp->pos)
 		c = '=';
 
 	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));
 	}
 }
 
 /*
  * Rules above and below the table are always single
  * and have an additional plus at the beginning and end.
  * For double frames, this function is called twice,
  * and the outer one does not have crossings.
  */
 static void
 tbl_hframe(struct termp *tp, const struct tbl_span *sp, int outer)
 {
 	const struct tbl_head *hp;
 
 	term_word(tp, "+");
 	for (hp = sp->head; hp; hp = hp->next) {
 		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_opts *opts,
 	const struct tbl_dat *dp,
 	const struct roffcol *col)
 {
 
 	if (NULL == dp) {
 		tbl_char(tp, ASCII_NBRSP, col->width);
 		return;
 	}
 	assert(dp->layout);
 
 	switch (dp->pos) {
 	case TBL_DATA_NONE:
 		tbl_char(tp, ASCII_NBRSP, col->width);
 		return;
 	case TBL_DATA_HORIZ:
 		/* FALLTHROUGH */
 	case TBL_DATA_NHORIZ:
 		tbl_char(tp, '-', col->width);
 		return;
 	case TBL_DATA_NDHORIZ:
 		/* FALLTHROUGH */
 	case TBL_DATA_DHORIZ:
 		tbl_char(tp, '=', col->width);
 		return;
 	default:
 		break;
 	}
 
 	switch (dp->layout->pos) {
 	case TBL_CELL_HORIZ:
 		tbl_char(tp, '-', col->width);
 		break;
 	case TBL_CELL_DHORIZ:
 		tbl_char(tp, '=', col->width);
 		break;
 	case TBL_CELL_LONG:
 		/* FALLTHROUGH */
 	case TBL_CELL_CENTRE:
 		/* FALLTHROUGH */
 	case TBL_CELL_LEFT:
 		/* FALLTHROUGH */
 	case TBL_CELL_RIGHT:
 		tbl_literal(tp, dp, col);
 		break;
 	case TBL_CELL_NUMBER:
 		tbl_number(tp, opts, dp, col);
 		break;
 	case TBL_CELL_DOWN:
 		tbl_char(tp, ASCII_NBRSP, col->width);
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 }
 
 static void
 tbl_vrule(struct termp *tp, const struct tbl_head *hp)
 {
 
 	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
 tbl_char(struct termp *tp, char c, size_t len)
 {
 	size_t		i, sz;
 	char		cp[2];
 
 	cp[0] = c;
 	cp[1] = '\0';
 
 	sz = term_strlen(tp, cp);
 
 	for (i = 0; i < len; i += sz)
 		term_word(tp, cp);
 }
 
 static void
 tbl_literal(struct termp *tp, const struct tbl_dat *dp,
 		const struct roffcol *col)
 {
 	struct tbl_head		*hp;
 	size_t			 width, len, padl, padr;
 	int			 spans;
 
 	assert(dp->string);
 	len = term_strlen(tp, dp->string);
 
 	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) {
 	case TBL_CELL_LONG:
 		padl = term_len(tp, 1);
 		padr = padr > padl ? padr - padl : 0;
 		break;
 	case TBL_CELL_CENTRE:
 		if (2 > padr)
 			break;
 		padl = padr / 2;
 		padr -= padl;
 		break;
 	case TBL_CELL_RIGHT:
 		padl = padr;
 		padr = 0;
 		break;
 	default:
 		break;
 	}
 
 	tbl_char(tp, ASCII_NBRSP, padl);
-	term_word(tp, dp->string);
+	tbl_word(tp, dp);
 	tbl_char(tp, ASCII_NBRSP, padr);
 }
 
 static void
 tbl_number(struct termp *tp, const struct tbl_opts *opts,
 		const struct tbl_dat *dp,
 		const struct roffcol *col)
 {
 	char		*cp;
 	char		 buf[2];
 	size_t		 sz, psz, ssz, d, padl;
 	int		 i;
 
 	/*
 	 * See calc_data_number().  Left-pad by taking the offset of our
 	 * and the maximum decimal; right-pad by the remaining amount.
 	 */
 
 	assert(dp->string);
 
 	sz = term_strlen(tp, dp->string);
 
 	buf[0] = opts->decimal;
 	buf[1] = '\0';
 
 	psz = term_strlen(tp, buf);
 
 	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];
 			ssz += term_strlen(tp, buf);
 		}
 		d = ssz + psz;
 	} else
 		d = sz + psz;
 
 	padl = col->decimal - d;
 
 	tbl_char(tp, ASCII_NBRSP, padl);
-	term_word(tp, dp->string);
+	tbl_word(tp, dp);
 	if (col->width > sz + padl)
 		tbl_char(tp, ASCII_NBRSP, col->width - sz - padl);
 }
 
+static void
+tbl_word(struct termp *tp, const struct tbl_dat *dp)
+{
+	const void	*prev_font;
+
+	prev_font = term_fontq(tp);
+	if (dp->layout->flags & TBL_CELL_BOLD)
+		term_fontpush(tp, TERMFONT_BOLD);
+	else if (dp->layout->flags & TBL_CELL_ITALIC)
+		term_fontpush(tp, TERMFONT_UNDER);
+
+	term_word(tp, dp->string);
+
+	term_fontpopq(tp, prev_font);
+}
Index: vendor/mdocml/dist/term.c
===================================================================
--- vendor/mdocml/dist/term.c	(revision 275396)
+++ vendor/mdocml/dist/term.c	(revision 275397)
@@ -1,808 +1,812 @@
-/*	$Id: term.c,v 1.226 2014/08/01 19:38:29 schwarze Exp $ */
+/*	$Id: term.c,v 1.236 2014/11/21 01:52:53 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2010-2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "out.h"
 #include "term.h"
 #include "main.h"
 
 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);
 
 
 void
 term_free(struct termp *p)
 {
 
-	if (p->buf)
-		free(p->buf);
-	if (p->symtab)
-		mchars_free(p->symtab);
-
+	free(p->buf);
 	free(p);
 }
 
 void
 term_begin(struct termp *p, term_margin head,
 		term_margin foot, const void *arg)
 {
 
 	p->headf = head;
 	p->footf = foot;
 	p->argf = arg;
 	(*p->begin)(p);
 }
 
 void
 term_end(struct termp *p)
 {
 
 	(*p->end)(p);
 }
 
 /*
  * Flush a chunk of text.  By default, break the output line each time
  * the right margin is reached, and continue output on the next line
  * at the same offset as the chunk itself.  By default, also break the
  * output line at the end of the chunk.
  * The following flags may be specified:
  *
  *  - TERMP_NOBREAK: Do not break the output line at the right margin,
  *    but only at the max right margin.  Also, do not break the output
  *    line at the end of the chunk, such that the next call can pad to
  *    the next column.  However, if less than p->trailspace blanks,
  *    which can be 0, 1, or 2, remain to the right margin, the line
  *    will be broken.
  *  - TERMP_BRIND: If the chunk does not fit and the output line has
  *    to be broken, start the next line at the right margin instead
  *    of at the offset.  Used together with TERMP_NOBREAK for the tags
  *    in various kinds of tagged lists.
  *  - TERMP_DANGLE: Do not break the output line at the right margin,
  *    append the next chunk after it even if this one is too long.
  *    To be used together with TERMP_NOBREAK.
  *  - TERMP_HANG: Like TERMP_DANGLE, and also suppress padding before
  *    the next chunk if this column is not full.
  */
 void
 term_flushln(struct termp *p)
 {
 	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 */
 	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 */
+	size_t		 rmargin; /* the rightmost of the two margins */
 
 	/*
 	 * 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.
 	 *
 	 * 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;
+	rmargin = p->rmargin > p->offset ? p->rmargin : p->offset;
+	dv = p->rmargin - p->offset;
 	maxvis = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
-	dv     = p->maxrmargin - p->offset;
-	mmax   = (int)dv > p->overstep ? dv - (size_t)p->overstep : 0;
 
-	bp = TERMP_NOBREAK & p->flags ? mmax : maxvis;
+	if (p->flags & TERMP_NOBREAK) {
+		dv = p->maxrmargin > p->offset ?
+		     p->maxrmargin - p->offset : 0;
+		bp = (int)dv > p->overstep ?
+		     dv - (size_t)p->overstep : 0;
+	} else
+		bp = maxvis;
 
 	/*
 	 * Calculate the required amount of padding.
 	 */
 	vbl = p->offset + p->overstep > p->viscol ?
 	      p->offset + p->overstep - p->viscol : 0;
 
 	vis = vend = 0;
 	i = 0;
 
 	while (i < p->col) {
 		/*
 		 * 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++;
 		}
 
 		/*
 		 * Count up visible word characters.  Control sequences
 		 * (starting with the CSI) aren't counted.  A space
 		 * generates a non-printing word, which is valid (the
 		 * space is printed according to regular spacing rules).
 		 */
 
 		for (j = i, jhy = 0; j < p->col; j++) {
 			if (' ' == p->buf[j] || '\t' == p->buf[j])
 				break;
 
 			/* Back over the the last printed character. */
 			if (8 == p->buf[j]) {
 				assert(j);
 				vend -= (*p->width)(p, p->buf[j - 1]);
 				continue;
 			}
 
 			/* Regular word. */
 			/* Break at the hyphen point if we overrun. */
 			if (vend > vis && vend < bp &&
 			    (ASCII_HYPH == p->buf[j] ||
 			     ASCII_BREAK == p->buf[j]))
 				jhy = j;
 
 			/*
 			 * Hyphenation now decided, put back a real
 			 * hyphen such that we get the correct width.
 			 */
 			if (ASCII_HYPH == p->buf[j])
 				p->buf[j] = '-';
 
 			vend += (*p->width)(p, p->buf[j]);
 		}
 
 		/*
 		 * Find out whether we would exceed the right margin.
 		 * If so, break to the next line.
 		 */
 		if (vend > bp && 0 == jhy && vis > 0) {
 			vend -= vis;
 			(*p->endline)(p);
 			p->viscol = 0;
 			if (TERMP_BRIND & p->flags) {
-				vbl = p->rmargin;
-				vend += p->rmargin - p->offset;
+				vbl = rmargin;
+				vend += rmargin - p->offset;
 			} else
 				vbl = p->offset;
 
 			/* 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;
 		}
 
 		/* Write out the [remaining] word. */
 		for ( ; i < p->col; i++) {
 			if (vend > bp && jhy > 0 && i > jhy)
 				break;
 			if ('\t' == p->buf[i])
 				break;
 			if (' ' == p->buf[i]) {
 				j = i;
-				while (' ' == p->buf[i])
+				while (i < p->col && ' ' == p->buf[i])
 					i++;
 				dv = (i - j) * (*p->width)(p, ' ');
 				vbl += dv;
 				vend += dv;
 				break;
 			}
 			if (ASCII_NBRSP == p->buf[i]) {
 				vbl += (*p->width)(p, ' ');
 				continue;
 			}
 			if (ASCII_BREAK == p->buf[i])
 				continue;
 
 			/*
 			 * Now we definitely know there will be
 			 * printable characters to output,
 			 * so write preceding white space now.
 			 */
 			if (vbl) {
 				(*p->advance)(p, vbl);
 				p->viscol += vbl;
 				vbl = 0;
 			}
 
 			(*p->letter)(p, p->buf[i]);
 			if (8 == p->buf[i])
 				p->viscol -= (*p->width)(p, p->buf[i-1]);
 			else
 				p->viscol += (*p->width)(p, p->buf[i]);
 		}
 		vis = vend;
 	}
 
 	/*
 	 * If there was trailing white space, it was not printed;
 	 * so reset the cursor position accordingly.
 	 */
-	if (vis)
+	if (vis > vbl)
 		vis -= vbl;
+	else
+		vis = 0;
 
 	p->col = 0;
 	p->overstep = 0;
 
 	if ( ! (TERMP_NOBREAK & p->flags)) {
 		p->viscol = 0;
 		(*p->endline)(p);
 		return;
 	}
 
 	if (TERMP_HANG & p->flags) {
 		p->overstep = (int)(vis - maxvis +
 		    p->trailspace * (*p->width)(p, ' '));
 
 		/*
 		 * If we have overstepped the margin, temporarily move
 		 * it to the right and flag the rest of the line to be
 		 * shorter.
 		 * If there is a request to keep the columns together,
 		 * allow negative overstep when the column is not full.
 		 */
 		if (p->trailspace && p->overstep < 0)
 			p->overstep = 0;
 		return;
 
 	} else if (TERMP_DANGLE & p->flags)
 		return;
 
 	/* If the column was overrun, break the line. */
 	if (maxvis < vis + p->trailspace * (*p->width)(p, ' ')) {
 		(*p->endline)(p);
 		p->viscol = 0;
 	}
 }
 
 /*
  * A newline only breaks an existing line; it won't assert vertical
  * space.  All data in the output buffer is flushed prior to the newline
  * assertion.
  */
 void
 term_newln(struct termp *p)
 {
 
 	p->flags |= TERMP_NOSPACE;
 	if (p->col || p->viscol)
 		term_flushln(p);
 }
 
 /*
  * Asserts a vertical space (a full, empty line-break between lines).
  * Note that if used twice, this will cause two blank spaces and so on.
  * All data in the output buffer is flushed prior to the newline
  * assertion.
  */
 void
 term_vspace(struct termp *p)
 {
 
 	term_newln(p);
 	p->viscol = 0;
 	if (0 < p->skipvsp)
 		p->skipvsp--;
 	else
 		(*p->endline)(p);
 }
 
 void
 term_fontlast(struct termp *p)
 {
 	enum termfont	 f;
 
 	f = p->fontl;
 	p->fontl = p->fontq[p->fonti];
 	p->fontq[p->fonti] = f;
 }
 
 void
 term_fontrepl(struct termp *p, enum termfont f)
 {
 
 	p->fontl = p->fontq[p->fonti];
 	p->fontq[p->fonti] = f;
 }
 
 void
 term_fontpush(struct termp *p, enum termfont f)
 {
 
 	assert(p->fonti + 1 < 10);
 	p->fontl = p->fontq[p->fonti];
 	p->fontq[++p->fonti] = f;
 }
 
 const void *
 term_fontq(struct termp *p)
 {
 
 	return(&p->fontq[p->fonti]);
 }
 
 enum termfont
 term_fonttop(struct termp *p)
 {
 
 	return(p->fontq[p->fonti]);
 }
 
 void
 term_fontpopq(struct termp *p, const void *key)
 {
 
 	while (p->fonti >= 0 && key < (void *)(p->fontq + p->fonti))
 		p->fonti--;
 	assert(p->fonti >= 0);
 }
 
 void
 term_fontpop(struct termp *p)
 {
 
 	assert(p->fonti);
 	p->fonti--;
 }
 
 /*
  * Handle pwords, partial words, which may be either a single word or a
  * phrase that cannot be broken down (such as a literal string).  This
  * handles word styling.
  */
 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;
 	size_t		 ssz;
 	enum mandoc_esc	 esc;
 
 	if ( ! (TERMP_NOSPACE & p->flags)) {
 		if ( ! (TERMP_KEEP & p->flags)) {
 			bufferc(p, ' ');
 			if (TERMP_SENTENCE & p->flags)
 				bufferc(p, ' ');
 		} else
 			bufferc(p, ASCII_NBRSP);
 	}
 	if (TERMP_PREKEEP & p->flags)
 		p->flags |= TERMP_KEEP;
 
 	if ( ! (p->flags & TERMP_NONOSPACE))
 		p->flags &= ~TERMP_NOSPACE;
 	else
 		p->flags |= TERMP_NOSPACE;
 
 	p->flags &= ~TERMP_SENTENCE;
 
 	while ('\0' != *word) {
 		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;
 			continue;
 		}
 
 		word++;
 		esc = mandoc_escape(&word, &seq, &sz);
 		if (ESCAPE_ERROR == esc)
 			continue;
 
-		if (TERMENC_ASCII != p->enc)
-			switch (esc) {
-			case ESCAPE_UNICODE:
-				uc = mchars_num2uc(seq + 1, sz - 1);
-				if ('\0' == uc)
-					break;
-				encode1(p, uc);
-				continue;
-			case ESCAPE_SPECIAL:
-				uc = mchars_spec2cp(p->symtab, seq, sz);
-				if (uc <= 0)
-					break;
-				encode1(p, uc);
-				continue;
-			default:
-				break;
-			}
-
 		switch (esc) {
 		case ESCAPE_UNICODE:
-			encode1(p, '?');
+			uc = mchars_num2uc(seq + 1, sz - 1);
 			break;
 		case ESCAPE_NUMBERED:
-			c = mchars_num2char(seq, sz);
-			if ('\0' != c)
-				encode(p, &c, 1);
+			uc = mchars_num2char(seq, sz);
+			if (uc < 0)
+				continue;
 			break;
 		case ESCAPE_SPECIAL:
-			cp = mchars_spec2str(p->symtab, seq, sz, &ssz);
-			if (NULL != cp)
-				encode(p, cp, ssz);
-			else if (1 == ssz)
-				encode(p, seq, sz);
-			break;
+			if (p->enc == TERMENC_ASCII) {
+				cp = mchars_spec2str(p->symtab,
+				    seq, sz, &ssz);
+				if (cp != NULL)
+					encode(p, cp, ssz);
+			} else {
+				uc = mchars_spec2cp(p->symtab, seq, sz);
+				if (uc > 0)
+					encode1(p, uc);
+			}
+			continue;
 		case ESCAPE_FONTBOLD:
 			term_fontrepl(p, TERMFONT_BOLD);
-			break;
+			continue;
 		case ESCAPE_FONTITALIC:
 			term_fontrepl(p, TERMFONT_UNDER);
-			break;
+			continue;
 		case ESCAPE_FONTBI:
 			term_fontrepl(p, TERMFONT_BI);
-			break;
+			continue;
 		case ESCAPE_FONT:
 			/* FALLTHROUGH */
 		case ESCAPE_FONTROMAN:
 			term_fontrepl(p, TERMFONT_NONE);
-			break;
+			continue;
 		case ESCAPE_FONTPREV:
 			term_fontlast(p);
-			break;
+			continue;
 		case ESCAPE_NOSPACE:
 			if (TERMP_SKIPCHAR & p->flags)
 				p->flags &= ~TERMP_SKIPCHAR;
 			else if ('\0' == *word)
 				p->flags |= TERMP_NOSPACE;
-			break;
+			continue;
 		case ESCAPE_SKIPCHAR:
 			p->flags |= TERMP_SKIPCHAR;
-			break;
+			continue;
 		default:
-			break;
+			continue;
 		}
+
+		/*
+		 * Common handling for Unicode and numbered
+		 * character escape sequences.
+		 */
+
+		if (p->enc == TERMENC_ASCII) {
+			cp = ascii_uc2str(uc);
+			encode(p, cp, strlen(cp));
+		} else {
+			if ((uc < 0x20 && uc != 0x09) ||
+			    (uc > 0x7E && uc < 0xA0))
+				uc = 0xFFFD;
+			encode1(p, uc);
+		}
 	}
 	p->flags &= ~TERMP_NBRWORD;
 }
 
 static void
 adjbuf(struct termp *p, size_t sz)
 {
 
 	if (0 == p->maxcols)
 		p->maxcols = 1024;
 	while (sz >= p->maxcols)
 		p->maxcols <<= 2;
 
 	p->buf = mandoc_reallocarray(p->buf, p->maxcols, sizeof(int));
 }
 
 static void
 bufferc(struct termp *p, char c)
 {
 
 	if (p->col + 1 >= p->maxcols)
 		adjbuf(p, p->col + 1);
 
 	p->buf[p->col++] = c;
 }
 
 /*
  * See encode().
  * Do this for a single (probably unicode) value.
  * Does not check for non-decorated glyphs.
  */
 static void
 encode1(struct termp *p, int c)
 {
 	enum termfont	  f;
 
 	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_UNDER == f || TERMFONT_BI == f) {
 		p->buf[p->col++] = '_';
 		p->buf[p->col++] = 8;
 	}
 	if (TERMFONT_BOLD == f || TERMFONT_BI == f) {
 		if (ASCII_HYPH == c)
 			p->buf[p->col++] = '-';
 		else
 			p->buf[p->col++] = c;
 		p->buf[p->col++] = 8;
 	}
 	p->buf[p->col++] = c;
 }
 
 static void
 encode(struct termp *p, const char *word, size_t sz)
 {
 	size_t		  i;
 
 	if (TERMP_SKIPCHAR & p->flags) {
 		p->flags &= ~TERMP_SKIPCHAR;
 		return;
 	}
 
 	/*
 	 * Encode and buffer a string of characters.  If the current
 	 * font mode is unset, buffer directly, else encode then buffer
 	 * character by character.
 	 */
 
 	if (TERMFONT_NONE == term_fonttop(p)) {
 		if (p->col + sz >= p->maxcols)
 			adjbuf(p, p->col + sz);
 		for (i = 0; i < sz; i++)
 			p->buf[p->col++] = word[i];
 		return;
 	}
 
 	/* Pre-buffer, assuming worst-case. */
 
 	if (p->col + 1 + (sz * 5) >= p->maxcols)
 		adjbuf(p, p->col + 1 + (sz * 5));
 
 	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];
 	}
 }
 
 void
 term_setwidth(struct termp *p, const char *wstr)
 {
 	struct roffsu	 su;
 	size_t		 width;
 	int		 iop;
 
 	iop = 0;
 	width = 0;
 	if (NULL != wstr) {
 		switch (*wstr) {
 		case '+':
 			iop = 1;
 			wstr++;
 			break;
 		case '-':
 			iop = -1;
 			wstr++;
 			break;
 		default:
 			break;
 		}
 		if (a2roffsu(wstr, &su, SCALE_MAX))
 			width = term_hspan(p, &su);
 		else
 			iop = 0;
 	}
 	(*p->setwidth)(p, iop, width);
 }
 
 size_t
 term_len(const struct termp *p, size_t sz)
 {
 
 	return((*p->width)(p, ' ') * sz);
 }
 
 static size_t
 cond_width(const struct termp *p, int c, int *skip)
 {
 
 	if (*skip) {
 		(*skip) = 0;
 		return(0);
 	} else
 		return((*p->width)(p, c));
 }
 
 size_t
 term_strlen(const struct termp *p, const char *cp)
 {
 	size_t		 sz, rsz, i;
-	int		 ssz, skip, c;
+	int		 ssz, skip, uc;
 	const char	*seq, *rhs;
 	enum mandoc_esc	 esc;
 	static const char rej[] = { '\\', ASCII_NBRSP, ASCII_HYPH,
 			ASCII_BREAK, '\0' };
 
 	/*
 	 * Account for escaped sequences within string length
 	 * calculations.  This follows the logic in term_word() as we
 	 * must calculate the width of produced strings.
 	 */
 
 	sz = 0;
 	skip = 0;
 	while ('\0' != *cp) {
 		rsz = strcspn(cp, rej);
 		for (i = 0; i < rsz; i++)
 			sz += cond_width(p, *cp++, &skip);
 
 		switch (*cp) {
 		case '\\':
 			cp++;
 			esc = mandoc_escape(&cp, &seq, &ssz);
 			if (ESCAPE_ERROR == esc)
 				continue;
 
-			if (TERMENC_ASCII != p->enc)
-				switch (esc) {
-				case ESCAPE_UNICODE:
-					c = mchars_num2uc(seq + 1,
-					    ssz - 1);
-					if ('\0' == c)
-						break;
-					sz += cond_width(p, c, &skip);
-					continue;
-				case ESCAPE_SPECIAL:
-					c = mchars_spec2cp(p->symtab,
-					    seq, ssz);
-					if (c <= 0)
-						break;
-					sz += cond_width(p, c, &skip);
-					continue;
-				default:
-					break;
-				}
-
 			rhs = NULL;
 
 			switch (esc) {
 			case ESCAPE_UNICODE:
-				sz += cond_width(p, '?', &skip);
+				uc = mchars_num2uc(seq + 1, ssz - 1);
 				break;
 			case ESCAPE_NUMBERED:
-				c = mchars_num2char(seq, ssz);
-				if ('\0' != c)
-					sz += cond_width(p, c, &skip);
+				uc = mchars_num2char(seq, ssz);
+				if (uc < 0)
+					continue;
 				break;
 			case ESCAPE_SPECIAL:
-				rhs = mchars_spec2str(p->symtab,
-				    seq, ssz, &rsz);
-
-				if (ssz != 1 || rhs)
-					break;
-
-				rhs = seq;
-				rsz = ssz;
-				break;
+				if (p->enc == TERMENC_ASCII) {
+					rhs = mchars_spec2str(p->symtab,
+					    seq, ssz, &rsz);
+					if (rhs != NULL)
+						break;
+				} else {
+					uc = mchars_spec2cp(p->symtab,
+					    seq, ssz);
+					if (uc > 0)
+						sz += cond_width(p, uc, &skip);
+				}
+				continue;
 			case ESCAPE_SKIPCHAR:
 				skip = 1;
-				break;
+				continue;
 			default:
-				break;
+				continue;
 			}
 
-			if (NULL == rhs)
-				break;
+			/*
+			 * Common handling for Unicode and numbered
+			 * character escape sequences.
+			 */
 
+			if (rhs == NULL) {
+				if (p->enc == TERMENC_ASCII) {
+					rhs = ascii_uc2str(uc);
+					rsz = strlen(rhs);
+				} else {
+					if ((uc < 0x20 && uc != 0x09) ||
+					    (uc > 0x7E && uc < 0xA0))
+						uc = 0xFFFD;
+					sz += cond_width(p, uc, &skip);
+					continue;
+				}
+			}
+
 			if (skip) {
 				skip = 0;
 				break;
 			}
+
+			/*
+			 * Common handling for all escape sequences
+			 * printing more than one character.
+			 */
 
 			for (i = 0; i < rsz; i++)
 				sz += (*p->width)(p, *rhs++);
 			break;
 		case ASCII_NBRSP:
 			sz += cond_width(p, ' ', &skip);
 			cp++;
 			break;
 		case ASCII_HYPH:
 			sz += cond_width(p, '-', &skip);
 			cp++;
 			/* FALLTHROUGH */
 		case ASCII_BREAK:
 			break;
 		default:
 			break;
 		}
 	}
 
 	return(sz);
 }
 
 size_t
 term_vspan(const struct termp *p, const struct roffsu *su)
 {
 	double		 r;
 
 	switch (su->unit) {
 	case SCALE_CM:
 		r = su->scale * 2.0;
 		break;
 	case SCALE_IN:
 		r = su->scale * 6.0;
 		break;
 	case SCALE_PC:
 		r = su->scale;
 		break;
 	case SCALE_PT:
 		r = su->scale / 8.0;
 		break;
 	case SCALE_MM:
 		r = su->scale / 1000.0;
 		break;
 	case SCALE_VS:
 		r = su->scale;
 		break;
 	default:
 		r = su->scale - 1.0;
 		break;
 	}
 
 	if (r < 0.0)
 		r = 0.0;
 	return((size_t)(r + 0.0005));
 }
 
 size_t
 term_hspan(const struct termp *p, const struct roffsu *su)
 {
 	double		 v;
 
 	v = (*p->hspan)(p, su);
 	if (v < 0.0)
 		v = 0.0;
 	return((size_t)(v + 0.0005));
 }
Index: vendor/mdocml/dist/term.h
===================================================================
--- vendor/mdocml/dist/term.h	(revision 275396)
+++ vendor/mdocml/dist/term.h	(revision 275397)
@@ -1,136 +1,138 @@
-/*	$Id: term.h,v 1.101 2014/04/20 16:46:05 schwarze Exp $ */
+/*	$Id: term.h,v 1.105 2014/10/28 17:36:19 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2011, 2012, 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 #ifndef TERM_H
 #define TERM_H
 
 __BEGIN_DECLS
 
 struct	termp;
 
 enum	termenc {
 	TERMENC_ASCII,
 	TERMENC_LOCALE,
 	TERMENC_UTF8
 };
 
 enum	termtype {
 	TERMTYPE_CHAR,
 	TERMTYPE_PS,
 	TERMTYPE_PDF
 };
 
 enum	termfont {
 	TERMFONT_NONE = 0,
 	TERMFONT_BOLD,
 	TERMFONT_UNDER,
 	TERMFONT_BI,
 	TERMFONT__MAX
 };
 
 #define	TERM_MAXMARGIN	  100000 /* FIXME */
 
 typedef void	(*term_margin)(struct termp *, const void *);
 
 struct	termp_tbl {
 	int		  width;	/* width in fixed chars */
 	int		  decimal;	/* decimal point position */
 };
 
 struct	termp {
 	enum termtype	  type;
 	struct rofftbl	  tbl;		/* table configuration */
+	int		  synopsisonly; /* print the synopsis only */
 	int		  mdocstyle;	/* imitate mdoc(7) output */
 	size_t		  defindent;	/* Default indent for text. */
 	size_t		  defrmargin;	/* Right margin of the device. */
 	size_t		  lastrmargin;	/* Right margin before the last ll. */
 	size_t		  rmargin;	/* Current right margin. */
 	size_t		  maxrmargin;	/* Max right margin. */
 	size_t		  maxcols;	/* Max size of buf. */
 	size_t		  offset;	/* Margin offest. */
 	size_t		  tabwidth;	/* Distance of tab positions. */
 	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_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_BRIND	 (1 << 9)	/* See term_flushln(). */
 #define	TERMP_DANGLE	 (1 << 10)	/* See term_flushln(). */
 #define	TERMP_HANG	 (1 << 11)	/* See term_flushln(). */
-#define	TERMP_NOSPLIT	 (1 << 12)	/* See termp_an_pre/post(). */
-#define	TERMP_SPLIT	 (1 << 13)	/* See termp_an_pre/post(). */
-#define	TERMP_ANPREC	 (1 << 14)	/* See termp_an_pre(). */
+#define	TERMP_NOSPLIT	 (1 << 12)	/* Do not break line before .An. */
+#define	TERMP_SPLIT	 (1 << 13)	/* Break line before .An. */
 	int		 *buf;		/* Output buffer. */
 	enum termenc	  enc;		/* Type of encoding. */
-	struct mchars	 *symtab;	/* Encoded-symbol table. */
+	const struct mchars *symtab;	/* Character table. */
 	enum termfont	  fontl;	/* Last font set. */
 	enum termfont	  fontq[10];	/* Symmetric fonts. */
 	int		  fonti;	/* Index of font stack. */
 	term_margin	  headf;	/* invoked to print head */
 	term_margin	  footf;	/* invoked to print foot */
 	void		(*letter)(struct termp *, int);
 	void		(*begin)(struct termp *);
 	void		(*end)(struct termp *);
 	void		(*endline)(struct termp *);
 	void		(*advance)(struct termp *, size_t);
 	void		(*setwidth)(struct termp *, int, size_t);
 	size_t		(*width)(const struct termp *, int);
 	double		(*hspan)(const struct termp *,
 				const struct roffsu *);
 	const void	 *argf;		/* arg for headf/footf */
 	struct termp_ps	 *ps;
 };
+
+const char	 *ascii_uc2str(int);
 
 void		  term_eqn(struct termp *, const struct eqn *);
 void		  term_tbl(struct termp *, const struct tbl_span *);
 void		  term_free(struct termp *);
 void		  term_newln(struct termp *);
 void		  term_vspace(struct termp *);
 void		  term_word(struct termp *, const char *);
 void		  term_flushln(struct termp *);
 void		  term_begin(struct termp *, term_margin,
 			term_margin, const void *);
 void		  term_end(struct termp *);
 
 void		  term_setwidth(struct termp *, const char *);
 size_t		  term_hspan(const struct termp *,
 			const struct roffsu *);
 size_t		  term_vspan(const struct termp *,
 			const struct roffsu *);
 size_t		  term_strlen(const struct termp *, const char *);
 size_t		  term_len(const struct termp *, size_t);
 
 enum termfont	  term_fonttop(struct termp *);
 const void	 *term_fontq(struct termp *);
 void		  term_fontpush(struct termp *, enum termfont);
 void		  term_fontpop(struct termp *);
 void		  term_fontpopq(struct termp *, const void *);
 void		  term_fontrepl(struct termp *, enum termfont);
 void		  term_fontlast(struct termp *);
 
 __END_DECLS
 
 #endif /*!TERM_H*/
Index: vendor/mdocml/dist/term_ascii.c
===================================================================
--- vendor/mdocml/dist/term_ascii.c	(revision 275396)
+++ vendor/mdocml/dist/term_ascii.c	(revision 275397)
@@ -1,302 +1,385 @@
-/*	$Id: term_ascii.c,v 1.27 2014/08/01 19:25:52 schwarze Exp $ */
+/*	$Id: term_ascii.c,v 1.40 2014/11/20 13:56:20 schwarze Exp $ */
 /*
  * Copyright (c) 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
-#ifdef USE_WCHAR
-# include 
+#include 
+#if HAVE_WCHAR
+#include 
 #endif
 #include 
 #include 
 #include 
 #include 
-#ifdef USE_WCHAR
-# include 
+#if HAVE_WCHAR
+#include 
 #endif
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "out.h"
 #include "term.h"
 #include "main.h"
 
-/* 
- * Sadly, this doesn't seem to be defined on systems even when they
- * support it.  For the time being, remove it and let those compiling
- * the software decide for themselves what to use.
- */
-#if 0
-#if ! defined(__STDC_ISO_10646__)
-# undef USE_WCHAR
-#endif
-#endif
-
-static	struct termp	 *ascii_init(enum termenc, char *);
+static	struct termp	 *ascii_init(enum termenc,
+				const struct mchars *, char *);
 static	double		  ascii_hspan(const struct termp *,
 				const struct roffsu *);
 static	size_t		  ascii_width(const struct termp *, int);
 static	void		  ascii_advance(struct termp *, size_t);
 static	void		  ascii_begin(struct termp *);
 static	void		  ascii_end(struct termp *);
 static	void		  ascii_endline(struct termp *);
 static	void		  ascii_letter(struct termp *, int);
 static	void		  ascii_setwidth(struct termp *, int, size_t);
 
-#ifdef	USE_WCHAR
+#if HAVE_WCHAR
 static	void		  locale_advance(struct termp *, size_t);
 static	void		  locale_endline(struct termp *);
 static	void		  locale_letter(struct termp *, int);
 static	size_t		  locale_width(const struct termp *, int);
 #endif
 
 
 static struct termp *
-ascii_init(enum termenc enc, char *outopts)
+ascii_init(enum termenc enc, const struct mchars *mchars, char *outopts)
 {
-	const char	*toks[4];
+	const char	*toks[5];
 	char		*v;
 	struct termp	*p;
 
 	p = mandoc_calloc(1, sizeof(struct termp));
 
+	p->symtab = mchars;
 	p->tabwidth = 5;
 	p->defrmargin = p->lastrmargin = 78;
 
 	p->begin = ascii_begin;
 	p->end = ascii_end;
 	p->hspan = ascii_hspan;
 	p->type = TERMTYPE_CHAR;
 
 	p->enc = TERMENC_ASCII;
 	p->advance = ascii_advance;
 	p->endline = ascii_endline;
 	p->letter = ascii_letter;
 	p->setwidth = ascii_setwidth;
 	p->width = ascii_width;
 
-#ifdef	USE_WCHAR
+#if HAVE_WCHAR
 	if (TERMENC_ASCII != enc) {
 		v = TERMENC_LOCALE == enc ?
 		    setlocale(LC_ALL, "") :
 		    setlocale(LC_CTYPE, "en_US.UTF-8");
 		if (NULL != v && MB_CUR_MAX > 1) {
 			p->enc = enc;
 			p->advance = locale_advance;
 			p->endline = locale_endline;
 			p->letter = locale_letter;
 			p->width = locale_width;
 		}
 	}
 #endif
 
 	toks[0] = "indent";
 	toks[1] = "width";
 	toks[2] = "mdoc";
-	toks[3] = NULL;
+	toks[3] = "synopsis";
+	toks[4] = NULL;
 
 	while (outopts && *outopts)
 		switch (getsubopt(&outopts, UNCONST(toks), &v)) {
 		case 0:
 			p->defindent = (size_t)atoi(v);
 			break;
 		case 1:
 			p->defrmargin = (size_t)atoi(v);
 			break;
 		case 2:
 			/*
 			 * Temporary, undocumented mode
 			 * to imitate mdoc(7) output style.
 			 */
 			p->mdocstyle = 1;
 			p->defindent = 5;
 			break;
+		case 3:
+			p->synopsisonly = 1;
+			break;
 		default:
 			break;
 		}
 
 	/* Enforce a lower boundary. */
 	if (p->defrmargin < 58)
 		p->defrmargin = 58;
 
 	return(p);
 }
 
 void *
-ascii_alloc(char *outopts)
+ascii_alloc(const struct mchars *mchars, char *outopts)
 {
 
-	return(ascii_init(TERMENC_ASCII, outopts));
+	return(ascii_init(TERMENC_ASCII, mchars, outopts));
 }
 
 void *
-utf8_alloc(char *outopts)
+utf8_alloc(const struct mchars *mchars, char *outopts)
 {
 
-	return(ascii_init(TERMENC_UTF8, outopts));
+	return(ascii_init(TERMENC_UTF8, mchars, outopts));
 }
 
 void *
-locale_alloc(char *outopts)
+locale_alloc(const struct mchars *mchars, char *outopts)
 {
 
-	return(ascii_init(TERMENC_LOCALE, outopts));
+	return(ascii_init(TERMENC_LOCALE, mchars, outopts));
 }
 
 static void
 ascii_setwidth(struct termp *p, int iop, size_t width)
 {
 
 	p->rmargin = p->defrmargin;
-	if (0 < iop)
+	if (iop > 0)
 		p->defrmargin += width;
-	else if (0 > iop)
+	else if (iop == 0)
+		p->defrmargin = width ? width : p->lastrmargin;
+	else if (p->defrmargin > width)
 		p->defrmargin -= width;
 	else
-		p->defrmargin = width ? width : p->lastrmargin;
+		p->defrmargin = 0;
 	p->lastrmargin = p->rmargin;
 	p->rmargin = p->maxrmargin = p->defrmargin;
 }
 
 static size_t
 ascii_width(const struct termp *p, int c)
 {
 
 	return(1);
 }
 
 void
 ascii_free(void *arg)
 {
 
 	term_free((struct termp *)arg);
 }
 
 static void
 ascii_letter(struct termp *p, int c)
 {
 
 	putchar(c);
 }
 
 static void
 ascii_begin(struct termp *p)
 {
 
 	(*p->headf)(p, p->argf);
 }
 
 static void
 ascii_end(struct termp *p)
 {
 
 	(*p->footf)(p, p->argf);
 }
 
 static void
 ascii_endline(struct termp *p)
 {
 
 	putchar('\n');
 }
 
 static void
 ascii_advance(struct termp *p, size_t len)
 {
 	size_t		i;
 
 	for (i = 0; i < len; i++)
 		putchar(' ');
 }
 
 static double
 ascii_hspan(const struct termp *p, const struct roffsu *su)
 {
 	double		 r;
 
 	/*
-	 * Approximate based on character width.  These are generated
-	 * entirely by eyeballing the screen, but appear to be correct.
+	 * Approximate based on character width.
+	 * None of these will be actually correct given that an inch on
+	 * the screen depends on character size, terminal, etc., etc.
 	 */
-
 	switch (su->unit) {
+	case SCALE_BU:
+		r = su->scale * 10.0 / 240.0;
+		break;
 	case SCALE_CM:
-		r = su->scale * 4.0;
+		r = su->scale * 10.0 / 2.54;
 		break;
+	case SCALE_FS:
+		r = su->scale * 2730.666;
+		break;
 	case SCALE_IN:
 		r = su->scale * 10.0;
 		break;
+	case SCALE_MM:
+		r = su->scale / 100.0;
+		break;
 	case SCALE_PC:
-		r = (su->scale * 10.0) / 6.0;
+		r = su->scale * 10.0 / 6.0;
 		break;
 	case SCALE_PT:
-		r = (su->scale * 10.0) / 72.0;
+		r = su->scale * 10.0 / 72.0;
 		break;
-	case SCALE_MM:
-		r = su->scale / 1000.0;
-		break;
 	case SCALE_VS:
 		r = su->scale * 2.0 - 1.0;
 		break;
-	default:
+	case SCALE_EN:
+		/* FALLTHROUGH */
+	case SCALE_EM:
 		r = su->scale;
 		break;
+	default:
+		abort();
+		/* NOTREACHED */
 	}
 
 	return(r);
 }
 
-#ifdef USE_WCHAR
+const char *
+ascii_uc2str(int uc)
+{
+	static const char nbrsp[2] = { ASCII_NBRSP, '\0' };
+	static const char *tab[] = {
+	"","","","","","","","",
+	"",	"\t",	"",	"",	"",	"",	"",	"",
+	"","","","","","","","",
+	"","",	"","","",	"",	"",	"",
+	" ",	"!",	"\"",	"#",	"$",	"%",	"&",	"'",
+	"(",	")",	"*",	"+",	",",	"-",	".",	"/",
+	"0",	"1",	"2",	"3",	"4",	"5",	"6",	"7",
+	"8",	"9",	":",	";",	"<",	"=",	">",	"?",
+	"@",	"A",	"B",	"C",	"D",	"E",	"F",	"G",
+	"H",	"I",	"J",	"K",	"L",	"M",	"N",	"O",
+	"P",	"Q",	"R",	"S",	"T",	"U",	"V",	"W",
+	"X",	"Y",	"Z",	"[",	"\\",	"]",	"^",	"_",
+	"`",	"a",	"b",	"c",	"d",	"e",	"f",	"g",
+	"h",	"i",	"j",	"k",	"l",	"m",	"n",	"o",
+	"p",	"q",	"r",	"s",	"t",	"u",	"v",	"w",
+	"x",	"y",	"z",	"{",	"|",	"}",	"~",	"",
+	"<80>",	"<81>",	"<82>",	"<83>",	"<84>",	"<85>",	"<86>",	"<87>",
+	"<88>",	"<89>",	"<8A>",	"<8B>",	"<8C>",	"<8D>",	"<8E>",	"<8F>",
+	"<90>",	"<91>",	"<92>",	"<93>",	"<94>",	"<95>",	"<96>",	"<97>",
+	"<99>",	"<99>",	"<9A>",	"<9B>",	"<9C>",	"<9D>",	"<9E>",	"<9F>",
+	nbrsp,	"!",	"/\bc",	"GBP",	"o\bx",	"=\bY",	"|",	"",
+	"\"",	"(C)",	"_\ba",	"<<",	"~",	"",	"(R)",	"-",
+	"","+-",	"2",	"3",	"'",	",\bu",	"",".",
+	",",	"1",	"_\bo",	">>",	"1/4",	"1/2",	"3/4",	"?",
+	"`\bA",	"'\bA",	"^\bA",	"~\bA",	"\"\bA","o\bA",	"AE",	",\bC",
+	"`\bE",	"'\bE",	"^\bE",	"\"\bE","`\bI",	"'\bI",	"^\bI",	"\"\bI",
+	"-\bD",	"~\bN",	"`\bO",	"'\bO",	"^\bO",	"~\bO",	"\"\bO","x",
+	"/\bO",	"`\bU",	"'\bU",	"^\bU",	"\"\bU","'\bY",	"Th",	"ss",
+	"`\ba",	"'\ba",	"^\ba",	"~\ba",	"\"\ba","o\ba",	"ae",	",\bc",
+	"`\be",	"'\be",	"^\be",	"\"\be","`\bi",	"'\bi",	"^\bi",	"\"\bi",
+	"d",	"~\bn",	"`\bo",	"'\bo",	"^\bo",	"~\bo",	"\"\bo","-:-",
+	"/\bo",	"`\bu",	"'\bu",	"^\bu",	"\"\bu","'\by",	"th",	"\"\by",
+	"A",	"a",	"A",	"a",	"A",	"a",	"'\bC",	"'\bc",
+	"^\bC",	"^\bc",	"C",	"c",	"C",	"c",	"D",	"d",
+	"/\bD",	"/\bd",	"E",	"e",	"E",	"e",	"E",	"e",
+	"E",	"e",	"E",	"e",	"^\bG",	"^\bg",	"G",	"g",
+	"G",	"g",	",\bG",	",\bg",	"^\bH",	"^\bh",	"/\bH",	"/\bh",
+	"~\bI",	"~\bi",	"I",	"i",	"I",	"i",	"I",	"i",
+	"I",	"i",	"IJ",	"ij",	"^\bJ",	"^\bj",	",\bK",	",\bk",
+	"q",	"'\bL",	"'\bl",	",\bL",	",\bl",	"L",	"l",	"L",
+	"l",	"/\bL",	"/\bl",	"'\bN",	"'\bn",	",\bN",	",\bn",	"N",
+	"n",	"'n",	"Ng",	"ng",	"O",	"o",	"O",	"o",
+	"O",	"o",	"OE",	"oe",	"'\bR",	"'\br",	",\bR",	",\br",
+	"R",	"r",	"'\bS",	"'\bs",	"^\bS",	"^\bs",	",\bS",	",\bs",
+	"S",	"s",	",\bT",	",\bt",	"T",	"t",	"/\bT",	"/\bt",
+	"~\bU",	"~\bu",	"U",	"u",	"U",	"u",	"U",	"u",
+	"U",	"u",	"U",	"u",	"^\bW",	"^\bw",	"^\bY",	"^\by",
+	"\"\bY","'\bZ",	"'\bz",	"Z",	"z",	"Z",	"z",	"s",
+	"b",	"B",	"B",	"b",	"6",	"6",	"O",	"C",
+	"c",	"D",	"D",	"D",	"d",	"d",	"3",	"@",
+	"E",	"F",	",\bf",	"G",	"G",	"hv",	"I",	"/\bI",
+	"K",	"k",	"/\bl",	"l",	"W",	"N",	"n",	"~\bO",
+	"O",	"o",	"OI",	"oi",	"P",	"p",	"YR",	"2",
+	"2",	"SH",	"sh",	"t",	"T",	"t",	"T",	"U",
+	"u",	"Y",	"V",	"Y",	"y",	"/\bZ",	"/\bz",	"ZH",
+	"ZH",	"zh",	"zh",	"/\b2",	"5",	"5",	"ts",	"w",
+	"|",	"||",	"|=",	"!",	"DZ",	"Dz",	"dz",	"LJ",
+	"Lj",	"lj",	"NJ",	"Nj",	"nj",	"A",	"a",	"I",
+	"i",	"O",	"o",	"U",	"u",	"U",	"u",	"U",
+	"u",	"U",	"u",	"U",	"u",	"@",	"A",	"a",
+	"A",	"a",	"AE",	"ae",	"/\bG",	"/\bg",	"G",	"g",
+	"K",	"k",	"O",	"o",	"O",	"o",	"ZH",	"zh",
+	"j",	"DZ",	"Dz",	"dz",	"'\bG",	"'\bg",	"HV",	"W",
+	"`\bN",	"`\bn",	"A",	"a",	"'\bAE","'\bae","O",	"o"};
+
+	assert(uc >= 0);
+	if ((size_t)uc < sizeof(tab)/sizeof(tab[0]))
+		return(tab[uc]);
+	return(mchars_uc2str(uc));
+}
+
+#if HAVE_WCHAR
 static size_t
 locale_width(const struct termp *p, int c)
 {
 	int		rc;
 
 	if (c == ASCII_NBRSP)
 		c = ' ';
 	rc = wcwidth(c);
 	if (rc < 0)
 		rc = 0;
 	return(rc);
 }
 
 static void
 locale_advance(struct termp *p, size_t len)
 {
 	size_t		i;
 
 	for (i = 0; i < len; i++)
 		putwchar(L' ');
 }
 
 static void
 locale_endline(struct termp *p)
 {
 
 	putwchar(L'\n');
 }
 
 static void
 locale_letter(struct termp *p, int c)
 {
 
 	putwchar(c);
 }
 #endif
Index: vendor/mdocml/dist/term_ps.c
===================================================================
--- vendor/mdocml/dist/term_ps.c	(revision 275396)
+++ vendor/mdocml/dist/term_ps.c	(revision 275397)
@@ -1,1167 +1,1323 @@
-/*	$Id: term_ps.c,v 1.62 2014/08/01 19:25:52 schwarze Exp $ */
+/*	$Id: term_ps.c,v 1.69 2014/11/20 13:56:20 schwarze Exp $ */
 /*
  * Copyright (c) 2010, 2011 Kristaps Dzonsons 
  * Copyright (c) 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
 #include 
 
 #include 
 #include 
 #include 
 #include 
 #include 
 #include 
-#include 
 #include 
 
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "out.h"
 #include "main.h"
 #include "term.h"
 
 /* These work the buffer used by the header and footer. */
 #define	PS_BUFSLOP	  128
 
 /* Convert PostScript point "x" to an AFM unit. */
 #define	PNT2AFM(p, x) \
 	(size_t)((double)(x) * (1000.0 / (double)(p)->ps->scale))
 
 /* Convert an AFM unit "x" to a PostScript points */
 #define	AFM2PNT(p, x) \
 	((double)(x) / (1000.0 / (double)(p)->ps->scale))
 
 struct	glyph {
 	unsigned short	  wx; /* WX in AFM */
 };
 
 struct	font {
 	const char	 *name; /* FontName in AFM */
 #define	MAXCHAR		  95 /* total characters we can handle */
 	struct glyph	  gly[MAXCHAR]; /* glyph metrics */
 };
 
 struct	termp_ps {
 	int		  flags;
 #define	PS_INLINE	 (1 << 0)	/* we're in a word */
 #define	PS_MARGINS	 (1 << 1)	/* we're in the margins */
 #define	PS_NEWPAGE	 (1 << 2)	/* new page, no words yet */
+#define	PS_BACKSP	 (1 << 3)	/* last character was backspace */
 	size_t		  pscol;	/* visible column (AFM units) */
 	size_t		  psrow;	/* visible row (AFM units) */
 	char		 *psmarg;	/* margin buf */
 	size_t		  psmargsz;	/* margin buf size */
 	size_t		  psmargcur;	/* cur index in margin buf */
-	char		  last;		/* character buffer */
+	char		  last;		/* last non-backspace seen */
 	enum termfont	  lastf;	/* last set font */
+	enum termfont	  nextf;	/* building next font here */
 	size_t		  scale;	/* font scaling factor */
 	size_t		  pages;	/* number of pages shown */
 	size_t		  lineheight;	/* line height (AFM units) */
 	size_t		  top;		/* body top (AFM units) */
 	size_t		  bottom;	/* body bottom (AFM units) */
 	size_t		  height;	/* page height (AFM units */
 	size_t		  width;	/* page width (AFM units) */
 	size_t		  lastwidth;	/* page width before last ll */
 	size_t		  left;		/* body left (AFM units) */
 	size_t		  header;	/* header pos (AFM units) */
 	size_t		  footer;	/* footer pos (AFM units) */
 	size_t		  pdfbytes;	/* current output byte */
 	size_t		  pdflastpg;	/* byte of last page mark */
 	size_t		  pdfbody;	/* start of body object */
 	size_t		 *pdfobjs;	/* table of object offsets */
 	size_t		  pdfobjsz;	/* size of pdfobjs */
 };
 
 static	double		  ps_hspan(const struct termp *,
 				const struct roffsu *);
 static	size_t		  ps_width(const struct termp *, int);
 static	void		  ps_advance(struct termp *, size_t);
 static	void		  ps_begin(struct termp *);
 static	void		  ps_closepage(struct termp *);
 static	void		  ps_end(struct termp *);
 static	void		  ps_endline(struct termp *);
 static	void		  ps_fclose(struct termp *);
 static	void		  ps_growbuf(struct termp *, size_t);
 static	void		  ps_letter(struct termp *, int);
 static	void		  ps_pclose(struct termp *);
 static	void		  ps_pletter(struct termp *, int);
 #if __GNUC__ - 0 >= 4
 __attribute__((__format__ (__printf__, 2, 3)))
 #endif
 static	void		  ps_printf(struct termp *, const char *, ...);
 static	void		  ps_putchar(struct termp *, char);
 static	void		  ps_setfont(struct termp *, enum termfont);
 static	void		  ps_setwidth(struct termp *, int, size_t);
-static	struct termp	 *pspdf_alloc(char *);
+static	struct termp	 *pspdf_alloc(const struct mchars *, char *);
 static	void		  pdf_obj(struct termp *, size_t);
 
 /*
  * We define, for the time being, three fonts: bold, oblique/italic, and
  * normal (roman).  The following table hard-codes the font metrics for
  * ASCII, i.e., 32--127.
  */
 
 static	const struct font fonts[TERMFONT__MAX] = {
 	{ "Times-Roman", {
 		{ 250 },
 		{ 333 },
 		{ 408 },
 		{ 500 },
 		{ 500 },
 		{ 833 },
 		{ 778 },
 		{ 333 },
 		{ 333 },
 		{ 333 },
 		{ 500 },
 		{ 564 },
 		{ 250 },
 		{ 333 },
 		{ 250 },
 		{ 278 },
 		{ 500 },
 		{ 500 },
 		{ 500 },
 		{ 500 },
 		{ 500 },
 		{ 500 },
 		{ 500 },
 		{ 500 },
 		{ 500 },
 		{ 500 },
 		{ 278 },
 		{ 278 },
 		{ 564 },
 		{ 564 },
 		{ 564 },
 		{ 444 },
 		{ 921 },
 		{ 722 },
 		{ 667 },
 		{ 667 },
 		{ 722 },
 		{ 611 },
 		{ 556 },
 		{ 722 },
 		{ 722 },
 		{ 333 },
 		{ 389 },
 		{ 722 },
 		{ 611 },
 		{ 889 },
 		{ 722 },
 		{ 722 },
 		{ 556 },
 		{ 722 },
 		{ 667 },
 		{ 556 },
 		{ 611 },
 		{ 722 },
 		{ 722 },
 		{ 944 },
 		{ 722 },
 		{ 722 },
 		{ 611 },
 		{ 333 },
 		{ 278 },
 		{ 333 },
 		{ 469 },
 		{ 500 },
 		{ 333 },
 		{ 444 },
 		{ 500 },
 		{ 444 },
 		{  500},
 		{  444},
 		{  333},
 		{  500},
 		{  500},
 		{  278},
 		{  278},
 		{  500},
 		{  278},
 		{  778},
 		{  500},
 		{  500},
 		{  500},
 		{  500},
 		{  333},
 		{  389},
 		{  278},
 		{  500},
 		{  500},
 		{  722},
 		{  500},
 		{  500},
 		{  444},
 		{  480},
 		{  200},
 		{  480},
 		{  541},
 	} },
 	{ "Times-Bold", {
 		{ 250  },
 		{ 333  },
 		{ 555  },
 		{ 500  },
 		{ 500  },
 		{ 1000 },
 		{ 833  },
 		{ 333  },
 		{ 333  },
 		{ 333  },
 		{ 500  },
 		{ 570  },
 		{ 250  },
 		{ 333  },
 		{ 250  },
 		{ 278  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 333  },
 		{ 333  },
 		{ 570  },
 		{ 570  },
 		{ 570  },
 		{ 500  },
 		{ 930  },
 		{ 722  },
 		{ 667  },
 		{ 722  },
 		{ 722  },
 		{ 667  },
 		{ 611  },
 		{ 778  },
 		{ 778  },
 		{ 389  },
 		{ 500  },
 		{ 778  },
 		{ 667  },
 		{ 944  },
 		{ 722  },
 		{ 778  },
 		{ 611  },
 		{ 778  },
 		{ 722  },
 		{ 556  },
 		{ 667  },
 		{ 722  },
 		{ 722  },
 		{ 1000 },
 		{ 722  },
 		{ 722  },
 		{ 667  },
 		{ 333  },
 		{ 278  },
 		{ 333  },
 		{ 581  },
 		{ 500  },
 		{ 333  },
 		{ 500  },
 		{ 556  },
 		{ 444  },
 		{  556 },
 		{  444 },
 		{  333 },
 		{  500 },
 		{  556 },
 		{  278 },
 		{  333 },
 		{  556 },
 		{  278 },
 		{  833 },
 		{  556 },
 		{  500 },
 		{  556 },
 		{  556 },
 		{  444 },
 		{  389 },
 		{  333 },
 		{  556 },
 		{  500 },
 		{  722 },
 		{  500 },
 		{  500 },
 		{  444 },
 		{  394 },
 		{  220 },
 		{  394 },
 		{  520 },
 	} },
 	{ "Times-Italic", {
 		{ 250  },
 		{ 333  },
 		{ 420  },
 		{ 500  },
 		{ 500  },
 		{ 833  },
 		{ 778  },
 		{ 333  },
 		{ 333  },
 		{ 333  },
 		{ 500  },
 		{ 675  },
 		{ 250  },
 		{ 333  },
 		{ 250  },
 		{ 278  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 500  },
 		{ 333  },
 		{ 333  },
 		{ 675  },
 		{ 675  },
 		{ 675  },
 		{ 500  },
 		{ 920  },
 		{ 611  },
 		{ 611  },
 		{ 667  },
 		{ 722  },
 		{ 611  },
 		{ 611  },
 		{ 722  },
 		{ 722  },
 		{ 333  },
 		{ 444  },
 		{ 667  },
 		{ 556  },
 		{ 833  },
 		{ 667  },
 		{ 722  },
 		{ 611  },
 		{ 722  },
 		{ 611  },
 		{ 500  },
 		{ 556  },
 		{ 722  },
 		{ 611  },
 		{ 833  },
 		{ 611  },
 		{ 556  },
 		{ 556  },
 		{ 389  },
 		{ 278  },
 		{ 389  },
 		{ 422  },
 		{ 500  },
 		{ 333  },
 		{ 500  },
 		{ 500  },
 		{ 444  },
 		{  500 },
 		{  444 },
 		{  278 },
 		{  500 },
 		{  500 },
 		{  278 },
 		{  278 },
 		{  444 },
 		{  278 },
 		{  722 },
 		{  500 },
 		{  500 },
 		{  500 },
 		{  500 },
 		{  389 },
 		{  389 },
 		{  278 },
 		{  500 },
 		{  444 },
 		{  667 },
 		{  444 },
 		{  444 },
 		{  389 },
 		{  400 },
 		{  275 },
 		{  400 },
 		{  541 },
 	} },
+	{ "Times-BoldItalic", {
+		{  250 },
+		{  389 },
+		{  555 },
+		{  500 },
+		{  500 },
+		{  833 },
+		{  778 },
+		{  333 },
+		{  333 },
+		{  333 },
+		{  500 },
+		{  570 },
+		{  250 },
+		{  333 },
+		{  250 },
+		{  278 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  333 },
+		{  333 },
+		{  570 },
+		{  570 },
+		{  570 },
+		{  500 },
+		{  832 },
+		{  667 },
+		{  667 },
+		{  667 },
+		{  722 },
+		{  667 },
+		{  667 },
+		{  722 },
+		{  778 },
+		{  389 },
+		{  500 },
+		{  667 },
+		{  611 },
+		{  889 },
+		{  722 },
+		{  722 },
+		{  611 },
+		{  722 },
+		{  667 },
+		{  556 },
+		{  611 },
+		{  722 },
+		{  667 },
+		{  889 },
+		{  667 },
+		{  611 },
+		{  611 },
+		{  333 },
+		{  278 },
+		{  333 },
+		{  570 },
+		{  500 },
+		{  333 },
+		{  500 },
+		{  500 },
+		{  444 },
+		{  500 },
+		{  444 },
+		{  333 },
+		{  500 },
+		{  556 },
+		{  278 },
+		{  278 },
+		{  500 },
+		{  278 },
+		{  778 },
+		{  556 },
+		{  500 },
+		{  500 },
+		{  500 },
+		{  389 },
+		{  389 },
+		{  278 },
+		{  556 },
+		{  444 },
+		{  667 },
+		{  500 },
+		{  444 },
+		{  389 },
+		{  348 },
+		{  220 },
+		{  348 },
+		{  570 },
+	} },
 };
 
 void *
-pdf_alloc(char *outopts)
+pdf_alloc(const struct mchars *mchars, char *outopts)
 {
 	struct termp	*p;
 
-	if (NULL != (p = pspdf_alloc(outopts)))
+	if (NULL != (p = pspdf_alloc(mchars, outopts)))
 		p->type = TERMTYPE_PDF;
 
 	return(p);
 }
 
 void *
-ps_alloc(char *outopts)
+ps_alloc(const struct mchars *mchars, char *outopts)
 {
 	struct termp	*p;
 
-	if (NULL != (p = pspdf_alloc(outopts)))
+	if (NULL != (p = pspdf_alloc(mchars, outopts)))
 		p->type = TERMTYPE_PS;
 
 	return(p);
 }
 
 static struct termp *
-pspdf_alloc(char *outopts)
+pspdf_alloc(const struct mchars *mchars, char *outopts)
 {
 	struct termp	*p;
 	unsigned int	 pagex, pagey;
 	size_t		 marginx, marginy, lineheight;
 	const char	*toks[2];
 	const char	*pp;
 	char		*v;
 
 	p = mandoc_calloc(1, sizeof(struct termp));
+	p->symtab = mchars;
 	p->enc = TERMENC_ASCII;
 	p->ps = mandoc_calloc(1, sizeof(struct termp_ps));
 
 	p->advance = ps_advance;
 	p->begin = ps_begin;
 	p->end = ps_end;
 	p->endline = ps_endline;
 	p->hspan = ps_hspan;
 	p->letter = ps_letter;
 	p->setwidth = ps_setwidth;
 	p->width = ps_width;
 
 	toks[0] = "paper";
 	toks[1] = NULL;
 
 	pp = NULL;
 
 	while (outopts && *outopts)
 		switch (getsubopt(&outopts, UNCONST(toks), &v)) {
 		case 0:
 			pp = v;
 			break;
 		default:
 			break;
 		}
 
 	/* Default to US letter (millimetres). */
 
 	pagex = 216;
 	pagey = 279;
 
 	/*
 	 * The ISO-269 paper sizes can be calculated automatically, but
 	 * it would require bringing in -lm for pow() and I'd rather not
 	 * do that.  So just do it the easy way for now.  Since this
 	 * only happens once, I'm not terribly concerned.
 	 */
 
 	if (pp && strcasecmp(pp, "letter")) {
 		if (0 == strcasecmp(pp, "a3")) {
 			pagex = 297;
 			pagey = 420;
 		} else if (0 == strcasecmp(pp, "a4")) {
 			pagex = 210;
 			pagey = 297;
 		} else if (0 == strcasecmp(pp, "a5")) {
 			pagex = 148;
 			pagey = 210;
 		} else if (0 == strcasecmp(pp, "legal")) {
 			pagex = 216;
 			pagey = 356;
 		} else if (2 != sscanf(pp, "%ux%u", &pagex, &pagey))
 			fprintf(stderr, "%s: Unknown paper\n", pp);
 	}
 
 	/*
 	 * This MUST be defined before any PNT2AFM or AFM2PNT
 	 * calculations occur.
 	 */
 
 	p->ps->scale = 11;
 
 	/* Remember millimetres -> AFM units. */
 
 	pagex = PNT2AFM(p, ((double)pagex * 2.834));
 	pagey = PNT2AFM(p, ((double)pagey * 2.834));
 
 	/* Margins are 1/9 the page x and y. */
 
 	marginx = (size_t)((double)pagex / 9.0);
 	marginy = (size_t)((double)pagey / 9.0);
 
 	/* Line-height is 1.4em. */
 
 	lineheight = PNT2AFM(p, ((double)p->ps->scale * 1.4));
 
 	p->ps->width = p->ps->lastwidth = (size_t)pagex;
 	p->ps->height = (size_t)pagey;
 	p->ps->header = pagey - (marginy / 2) - (lineheight / 2);
 	p->ps->top = pagey - marginy;
 	p->ps->footer = (marginy / 2) - (lineheight / 2);
 	p->ps->bottom = marginy;
 	p->ps->left = marginx;
 	p->ps->lineheight = lineheight;
 
 	p->defrmargin = pagex - (marginx * 2);
 	return(p);
 }
 
 static void
 ps_setwidth(struct termp *p, int iop, size_t width)
 {
 	size_t	 lastwidth;
 
 	lastwidth = p->ps->width;
-	if (0 < iop)
+	if (iop > 0)
 		p->ps->width += width;
-	else if (0 > iop)
+	else if (iop == 0)
+		p->ps->width = width ? width : p->ps->lastwidth;
+	else if (p->ps->width > width)
 		p->ps->width -= width;
 	else
-		p->ps->width = width ? width : p->ps->lastwidth;
+		p->ps->width = 0;
 	p->ps->lastwidth = lastwidth;
 }
 
 void
 pspdf_free(void *arg)
 {
 	struct termp	*p;
 
 	p = (struct termp *)arg;
 
 	if (p->ps->psmarg)
 		free(p->ps->psmarg);
 	if (p->ps->pdfobjs)
 		free(p->ps->pdfobjs);
 
 	free(p->ps);
 	term_free(p);
 }
 
 static void
 ps_printf(struct termp *p, const char *fmt, ...)
 {
 	va_list		 ap;
 	int		 pos, len;
 
 	va_start(ap, fmt);
 
 	/*
 	 * If we're running in regular mode, then pipe directly into
 	 * vprintf().  If we're processing margins, then push the data
 	 * into our growable margin buffer.
 	 */
 
 	if ( ! (PS_MARGINS & p->ps->flags)) {
 		len = vprintf(fmt, ap);
 		va_end(ap);
 		p->ps->pdfbytes += len < 0 ? 0 : (size_t)len;
 		return;
 	}
 
 	/*
 	 * XXX: I assume that the in-margin print won't exceed
 	 * PS_BUFSLOP (128 bytes), which is reasonable but still an
 	 * assumption that will cause pukeage if it's not the case.
 	 */
 
 	ps_growbuf(p, PS_BUFSLOP);
 
 	pos = (int)p->ps->psmargcur;
 	vsnprintf(&p->ps->psmarg[pos], PS_BUFSLOP, fmt, ap);
 
 	va_end(ap);
 
 	p->ps->psmargcur = strlen(p->ps->psmarg);
 }
 
 static void
 ps_putchar(struct termp *p, char c)
 {
 	int		 pos;
 
 	/* See ps_printf(). */
 
 	if ( ! (PS_MARGINS & p->ps->flags)) {
 		putchar(c);
 		p->ps->pdfbytes++;
 		return;
 	}
 
 	ps_growbuf(p, 2);
 
 	pos = (int)p->ps->psmargcur++;
 	p->ps->psmarg[pos++] = c;
 	p->ps->psmarg[pos] = '\0';
 }
 
 static void
 pdf_obj(struct termp *p, size_t obj)
 {
 
 	assert(obj > 0);
 
 	if ((obj - 1) >= p->ps->pdfobjsz) {
 		p->ps->pdfobjsz = obj + 128;
 		p->ps->pdfobjs = mandoc_reallocarray(p->ps->pdfobjs,
 		    p->ps->pdfobjsz, sizeof(size_t));
 	}
 
 	p->ps->pdfobjs[(int)obj - 1] = p->ps->pdfbytes;
 	ps_printf(p, "%zu 0 obj\n", obj);
 }
 
 static void
 ps_closepage(struct termp *p)
 {
 	int		 i;
 	size_t		 len, base;
 
 	/*
 	 * Close out a page that we've already flushed to output.  In
 	 * PostScript, we simply note that the page must be showed.  In
 	 * PDF, we must now create the Length, Resource, and Page node
 	 * for the page contents.
 	 */
 
 	assert(p->ps->psmarg && p->ps->psmarg[0]);
 	ps_printf(p, "%s", p->ps->psmarg);
 
 	if (TERMTYPE_PS != p->type) {
 		ps_printf(p, "ET\n");
 
 		len = p->ps->pdfbytes - p->ps->pdflastpg;
 		base = p->ps->pages * 4 + p->ps->pdfbody;
 
 		ps_printf(p, "endstream\nendobj\n");
 
 		/* Length of content. */
 		pdf_obj(p, base + 1);
 		ps_printf(p, "%zu\nendobj\n", len);
 
 		/* Resource for content. */
 		pdf_obj(p, base + 2);
 		ps_printf(p, "<<\n/ProcSet [/PDF /Text]\n");
 		ps_printf(p, "/Font <<\n");
 		for (i = 0; i < (int)TERMFONT__MAX; i++)
 			ps_printf(p, "/F%d %d 0 R\n", i, 3 + i);
 		ps_printf(p, ">>\n>>\n");
 
 		/* Page node. */
 		pdf_obj(p, base + 3);
 		ps_printf(p, "<<\n");
 		ps_printf(p, "/Type /Page\n");
 		ps_printf(p, "/Parent 2 0 R\n");
 		ps_printf(p, "/Resources %zu 0 R\n", base + 2);
 		ps_printf(p, "/Contents %zu 0 R\n", base);
 		ps_printf(p, ">>\nendobj\n");
 	} else
 		ps_printf(p, "showpage\n");
 
 	p->ps->pages++;
 	p->ps->psrow = p->ps->top;
 	assert( ! (PS_NEWPAGE & p->ps->flags));
 	p->ps->flags |= PS_NEWPAGE;
 }
 
 static void
 ps_end(struct termp *p)
 {
 	size_t		 i, xref, base;
 
 	/*
 	 * At the end of the file, do one last showpage.  This is the
 	 * same behaviour as groff(1) and works for multiple pages as
 	 * well as just one.
 	 */
 
 	if ( ! (PS_NEWPAGE & p->ps->flags)) {
 		assert(0 == p->ps->flags);
 		assert('\0' == p->ps->last);
 		ps_closepage(p);
 	}
 
 	if (TERMTYPE_PS == p->type) {
 		ps_printf(p, "%%%%Trailer\n");
 		ps_printf(p, "%%%%Pages: %zu\n", p->ps->pages);
 		ps_printf(p, "%%%%EOF\n");
 		return;
 	}
 
 	pdf_obj(p, 2);
 	ps_printf(p, "<<\n/Type /Pages\n");
 	ps_printf(p, "/MediaBox [0 0 %zu %zu]\n",
 			(size_t)AFM2PNT(p, p->ps->width),
 			(size_t)AFM2PNT(p, p->ps->height));
 
 	ps_printf(p, "/Count %zu\n", p->ps->pages);
 	ps_printf(p, "/Kids [");
 
 	for (i = 0; i < p->ps->pages; i++)
 		ps_printf(p, " %zu 0 R", i * 4 + p->ps->pdfbody + 3);
 
 	base = (p->ps->pages - 1) * 4 + p->ps->pdfbody + 4;
 
 	ps_printf(p, "]\n>>\nendobj\n");
 	pdf_obj(p, base);
 	ps_printf(p, "<<\n");
 	ps_printf(p, "/Type /Catalog\n");
 	ps_printf(p, "/Pages 2 0 R\n");
 	ps_printf(p, ">>\n");
 	xref = p->ps->pdfbytes;
 	ps_printf(p, "xref\n");
 	ps_printf(p, "0 %zu\n", base + 1);
 	ps_printf(p, "0000000000 65535 f \n");
 
 	for (i = 0; i < base; i++)
 		ps_printf(p, "%.10zu 00000 n \n",
 		    p->ps->pdfobjs[(int)i]);
 
 	ps_printf(p, "trailer\n");
 	ps_printf(p, "<<\n");
 	ps_printf(p, "/Size %zu\n", base + 1);
 	ps_printf(p, "/Root %zu 0 R\n", base);
 	ps_printf(p, "/Info 1 0 R\n");
 	ps_printf(p, ">>\n");
 	ps_printf(p, "startxref\n");
 	ps_printf(p, "%zu\n", xref);
 	ps_printf(p, "%%%%EOF\n");
 }
 
 static void
 ps_begin(struct termp *p)
 {
-	time_t		 t;
 	int		 i;
 
 	/*
 	 * Print margins into margin buffer.  Nothing gets output to the
 	 * screen yet, so we don't need to initialise the primary state.
 	 */
 
 	if (p->ps->psmarg) {
 		assert(p->ps->psmargsz);
 		p->ps->psmarg[0] = '\0';
 	}
 
 	/*p->ps->pdfbytes = 0;*/
 	p->ps->psmargcur = 0;
 	p->ps->flags = PS_MARGINS;
 	p->ps->pscol = p->ps->left;
 	p->ps->psrow = p->ps->header;
 
 	ps_setfont(p, TERMFONT_NONE);
 
 	(*p->headf)(p, p->argf);
 	(*p->endline)(p);
 
 	p->ps->pscol = p->ps->left;
 	p->ps->psrow = p->ps->footer;
 
 	(*p->footf)(p, p->argf);
 	(*p->endline)(p);
 
 	p->ps->flags &= ~PS_MARGINS;
 
 	assert(0 == p->ps->flags);
 	assert(p->ps->psmarg);
 	assert('\0' != p->ps->psmarg[0]);
 
 	/*
 	 * Print header and initialise page state.  Following this,
 	 * stuff gets printed to the screen, so make sure we're sane.
 	 */
 
-	t = time(NULL);
-
 	if (TERMTYPE_PS == p->type) {
 		ps_printf(p, "%%!PS-Adobe-3.0\n");
-		ps_printf(p, "%%%%CreationDate: %s", ctime(&t));
 		ps_printf(p, "%%%%DocumentData: Clean7Bit\n");
 		ps_printf(p, "%%%%Orientation: Portrait\n");
 		ps_printf(p, "%%%%Pages: (atend)\n");
 		ps_printf(p, "%%%%PageOrder: Ascend\n");
 		ps_printf(p, "%%%%DocumentMedia: "
 		    "Default %zu %zu 0 () ()\n",
 		    (size_t)AFM2PNT(p, p->ps->width),
 		    (size_t)AFM2PNT(p, p->ps->height));
 		ps_printf(p, "%%%%DocumentNeededResources: font");
 
 		for (i = 0; i < (int)TERMFONT__MAX; i++)
 			ps_printf(p, " %s", fonts[i].name);
 
 		ps_printf(p, "\n%%%%EndComments\n");
 	} else {
 		ps_printf(p, "%%PDF-1.1\n");
 		pdf_obj(p, 1);
 		ps_printf(p, "<<\n");
 		ps_printf(p, ">>\n");
 		ps_printf(p, "endobj\n");
 
 		for (i = 0; i < (int)TERMFONT__MAX; i++) {
 			pdf_obj(p, (size_t)i + 3);
 			ps_printf(p, "<<\n");
 			ps_printf(p, "/Type /Font\n");
 			ps_printf(p, "/Subtype /Type1\n");
 			ps_printf(p, "/Name /F%d\n", i);
 			ps_printf(p, "/BaseFont /%s\n", fonts[i].name);
 			ps_printf(p, ">>\n");
 		}
 	}
 
 	p->ps->pdfbody = (size_t)TERMFONT__MAX + 3;
 	p->ps->pscol = p->ps->left;
 	p->ps->psrow = p->ps->top;
 	p->ps->flags |= PS_NEWPAGE;
 	ps_setfont(p, TERMFONT_NONE);
 }
 
 static void
 ps_pletter(struct termp *p, int c)
 {
 	int		 f;
 
 	/*
 	 * If we haven't opened a page context, then output that we're
 	 * in a new page and make sure the font is correctly set.
 	 */
 
 	if (PS_NEWPAGE & p->ps->flags) {
 		if (TERMTYPE_PS == p->type) {
 			ps_printf(p, "%%%%Page: %zu %zu\n",
 			    p->ps->pages + 1, p->ps->pages + 1);
 			ps_printf(p, "/%s %zu selectfont\n",
 			    fonts[(int)p->ps->lastf].name,
 			    p->ps->scale);
 		} else {
 			pdf_obj(p, p->ps->pdfbody +
 			    p->ps->pages * 4);
 			ps_printf(p, "<<\n");
 			ps_printf(p, "/Length %zu 0 R\n",
 			    p->ps->pdfbody + 1 + p->ps->pages * 4);
 			ps_printf(p, ">>\nstream\n");
 		}
 		p->ps->pdflastpg = p->ps->pdfbytes;
 		p->ps->flags &= ~PS_NEWPAGE;
 	}
 
 	/*
 	 * If we're not in a PostScript "word" context, then open one
 	 * now at the current cursor.
 	 */
 
 	if ( ! (PS_INLINE & p->ps->flags)) {
 		if (TERMTYPE_PS != p->type) {
 			ps_printf(p, "BT\n/F%d %zu Tf\n",
 			    (int)p->ps->lastf, p->ps->scale);
 			ps_printf(p, "%.3f %.3f Td\n(",
 			    AFM2PNT(p, p->ps->pscol),
 			    AFM2PNT(p, p->ps->psrow));
 		} else
 			ps_printf(p, "%.3f %.3f moveto\n(",
 			    AFM2PNT(p, p->ps->pscol),
 			    AFM2PNT(p, p->ps->psrow));
 		p->ps->flags |= PS_INLINE;
 	}
 
 	assert( ! (PS_NEWPAGE & p->ps->flags));
 
 	/*
 	 * We need to escape these characters as per the PostScript
 	 * specification.  We would also escape non-graphable characters
 	 * (like tabs), but none of them would get to this point and
 	 * it's superfluous to abort() on them.
 	 */
 
 	switch (c) {
 	case '(':
 		/* FALLTHROUGH */
 	case ')':
 		/* FALLTHROUGH */
 	case '\\':
 		ps_putchar(p, '\\');
 		break;
 	default:
 		break;
 	}
 
 	/* Write the character and adjust where we are on the page. */
 
 	f = (int)p->ps->lastf;
 
 	if (c <= 32 || c - 32 >= MAXCHAR)
 		c = 32;
 
 	ps_putchar(p, (char)c);
 	c -= 32;
 	p->ps->pscol += (size_t)fonts[f].gly[c].wx;
 }
 
 static void
 ps_pclose(struct termp *p)
 {
 
 	/*
 	 * Spit out that we're exiting a word context (this is a
 	 * "partial close" because we don't check the last-char buffer
 	 * or anything).
 	 */
 
 	if ( ! (PS_INLINE & p->ps->flags))
 		return;
 
 	if (TERMTYPE_PS != p->type) {
 		ps_printf(p, ") Tj\nET\n");
 	} else
 		ps_printf(p, ") show\n");
 
 	p->ps->flags &= ~PS_INLINE;
 }
 
 static void
 ps_fclose(struct termp *p)
 {
 
 	/*
 	 * Strong closure: if we have a last-char, spit it out after
 	 * checking that we're in the right font mode.  This will of
 	 * course open a new scope, if applicable.
 	 *
 	 * Following this, close out any scope that's open.
 	 */
 
-	if ('\0' != p->ps->last) {
-		if (p->ps->lastf != TERMFONT_NONE) {
+	if (p->ps->last != '\0') {
+		assert( ! (p->ps->flags & PS_BACKSP));
+		if (p->ps->nextf != p->ps->lastf) {
 			ps_pclose(p);
-			ps_setfont(p, TERMFONT_NONE);
+			ps_setfont(p, p->ps->nextf);
 		}
+		p->ps->nextf = TERMFONT_NONE;
 		ps_pletter(p, p->ps->last);
 		p->ps->last = '\0';
 	}
 
 	if ( ! (PS_INLINE & p->ps->flags))
 		return;
 
 	ps_pclose(p);
 }
 
 static void
 ps_letter(struct termp *p, int arg)
 {
-	char		cc, c;
+	size_t		savecol;
+	char		c;
 
 	c = arg >= 128 || arg <= 0 ? '?' : arg;
 
 	/*
-	 * State machine dictates whether to buffer the last character
-	 * or not.  Basically, encoded words are detected by checking if
-	 * we're an "8" and switching on the buffer.  Then we put "8" in
-	 * our buffer, and on the next charater, flush both character
-	 * and buffer.  Thus, "regular" words are detected by having a
-	 * regular character and a regular buffer character.
+	 * When receiving a backspace, merely flag it.
+	 * We don't know yet whether it is
+	 * a font instruction or an overstrike.
 	 */
 
-	if ('\0' == p->ps->last) {
-		assert(8 != c);
-		p->ps->last = c;
+	if (c == '\b') {
+		assert(p->ps->last != '\0');
+		assert( ! (p->ps->flags & PS_BACKSP));
+		p->ps->flags |= PS_BACKSP;
 		return;
-	} else if (8 == p->ps->last) {
-		assert(8 != c);
-		p->ps->last = '\0';
-	} else if (8 == c) {
-		assert(8 != p->ps->last);
-		if ('_' == p->ps->last) {
-			if (p->ps->lastf != TERMFONT_UNDER) {
-				ps_pclose(p);
-				ps_setfont(p, TERMFONT_UNDER);
+	}
+
+	/*
+	 * Decode font instructions.
+	 */
+
+	if (p->ps->flags & PS_BACKSP) {
+		if (p->ps->last == '_') {
+			switch (p->ps->nextf) {
+			case TERMFONT_BI:
+				break;
+			case TERMFONT_BOLD:
+				p->ps->nextf = TERMFONT_BI;
+				break;
+			default:
+				p->ps->nextf = TERMFONT_UNDER;
 			}
-		} else if (p->ps->lastf != TERMFONT_BOLD) {
-			ps_pclose(p);
-			ps_setfont(p, TERMFONT_BOLD);
+			p->ps->last = c;
+			p->ps->flags &= ~PS_BACKSP;
+			return;
 		}
-		p->ps->last = c;
-		return;
-	} else {
-		if (p->ps->lastf != TERMFONT_NONE) {
+		if (p->ps->last == c) {
+			switch (p->ps->nextf) {
+			case TERMFONT_BI:
+				break;
+			case TERMFONT_UNDER:
+				p->ps->nextf = TERMFONT_BI;
+				break;
+			default:
+				p->ps->nextf = TERMFONT_BOLD;
+			}
+			p->ps->flags &= ~PS_BACKSP;
+			return;
+		}
+
+		/*
+		 * This is not a font instruction, but rather
+		 * the next character.  Prepare for overstrike.
+		 */
+
+		savecol = p->ps->pscol;
+	} else
+		savecol = SIZE_MAX;
+
+	/*
+	 * We found the next character, so the font instructions
+	 * for the previous one are complete.
+	 * Use them and print it.
+	 */
+
+	if (p->ps->last != '\0') {
+		if (p->ps->nextf != p->ps->lastf) {
 			ps_pclose(p);
-			ps_setfont(p, TERMFONT_NONE);
+			ps_setfont(p, p->ps->nextf);
 		}
-		cc = p->ps->last;
-		p->ps->last = c;
-		c = cc;
+		p->ps->nextf = TERMFONT_NONE;
+		ps_pletter(p, p->ps->last);
 	}
 
-	ps_pletter(p, c);
+	/*
+	 * Do not print the current character yet because font
+	 * instructions might follow; only remember it.
+	 * For the first character, nothing else is done.
+	 * The final character will get printed from ps_fclose().
+	 */
+
+	p->ps->last = c;
+
+	/*
+	 * For an overstrike, back up to the previous position.
+	 */
+
+	if (savecol != SIZE_MAX) {
+		ps_pclose(p);
+		p->ps->pscol = savecol;
+		p->ps->flags &= ~PS_BACKSP;
+	}
 }
 
 static void
 ps_advance(struct termp *p, size_t len)
 {
 
 	/*
 	 * Advance some spaces.  This can probably be made smarter,
 	 * i.e., to have multiple space-separated words in the same
 	 * scope, but this is easier:  just close out the current scope
 	 * and readjust our column settings.
 	 */
 
 	ps_fclose(p);
 	p->ps->pscol += len;
 }
 
 static void
 ps_endline(struct termp *p)
 {
 
 	/* Close out any scopes we have open: we're at eoln. */
 
 	ps_fclose(p);
 
 	/*
 	 * If we're in the margin, don't try to recalculate our current
 	 * row.  XXX: if the column tries to be fancy with multiple
 	 * lines, we'll do nasty stuff.
 	 */
 
 	if (PS_MARGINS & p->ps->flags)
 		return;
 
 	/* Left-justify. */
 
 	p->ps->pscol = p->ps->left;
 
 	/* If we haven't printed anything, return. */
 
 	if (PS_NEWPAGE & p->ps->flags)
 		return;
 
 	/*
 	 * Put us down a line.  If we're at the page bottom, spit out a
 	 * showpage and restart our row.
 	 */
 
 	if (p->ps->psrow >= p->ps->lineheight + p->ps->bottom) {
 		p->ps->psrow -= p->ps->lineheight;
 		return;
 	}
 
 	ps_closepage(p);
 }
 
 static void
 ps_setfont(struct termp *p, enum termfont f)
 {
 
 	assert(f < TERMFONT__MAX);
 	p->ps->lastf = f;
 
 	/*
 	 * If we're still at the top of the page, let the font-setting
 	 * be delayed until we actually have stuff to print.
 	 */
 
 	if (PS_NEWPAGE & p->ps->flags)
 		return;
 
 	if (TERMTYPE_PS == p->type)
 		ps_printf(p, "/%s %zu selectfont\n",
 		    fonts[(int)f].name, p->ps->scale);
 	else
 		ps_printf(p, "/F%d %zu Tf\n",
 		    (int)f, p->ps->scale);
 }
 
 static size_t
 ps_width(const struct termp *p, int c)
 {
 
 	if (c <= 32 || c - 32 >= MAXCHAR)
 		c = 0;
 	else
 		c -= 32;
 
 	return((size_t)fonts[(int)TERMFONT_NONE].gly[c].wx);
 }
 
 static double
 ps_hspan(const struct termp *p, const struct roffsu *su)
 {
 	double		 r;
 
 	/*
 	 * All of these measurements are derived by converting from the
 	 * native measurement to AFM units.
 	 */
-
 	switch (su->unit) {
+	case SCALE_BU:
+		/*
+		 * Traditionally, the default unit is fixed to the
+		 * output media.  So this would refer to the point.  In
+		 * mandoc(1), however, we stick to the default terminal
+		 * scaling unit so that output is the same regardless
+		 * the media.
+		 */
+		r = PNT2AFM(p, su->scale * 72.0 / 240.0);
+		break;
 	case SCALE_CM:
-		r = PNT2AFM(p, su->scale * 28.34);
+		r = PNT2AFM(p, su->scale * 72.0 / 2.54);
 		break;
-	case SCALE_IN:
-		r = PNT2AFM(p, su->scale * 72.0);
-		break;
-	case SCALE_PC:
-		r = PNT2AFM(p, su->scale * 12.0);
-		break;
-	case SCALE_PT:
-		r = PNT2AFM(p, su->scale * 100.0);
-		break;
 	case SCALE_EM:
 		r = su->scale *
 		    fonts[(int)TERMFONT_NONE].gly[109 - 32].wx;
 		break;
-	case SCALE_MM:
-		r = PNT2AFM(p, su->scale * 2.834);
-		break;
 	case SCALE_EN:
 		r = su->scale *
 		    fonts[(int)TERMFONT_NONE].gly[110 - 32].wx;
+		break;
+	case SCALE_IN:
+		r = PNT2AFM(p, su->scale * 72.0);
+		break;
+	case SCALE_MM:
+		r = su->scale *
+		    fonts[(int)TERMFONT_NONE].gly[109 - 32].wx / 100.0;
+		break;
+	case SCALE_PC:
+		r = PNT2AFM(p, su->scale * 12.0);
+		break;
+	case SCALE_PT:
+		r = PNT2AFM(p, su->scale * 1.0);
 		break;
 	case SCALE_VS:
 		r = su->scale * p->ps->lineheight;
 		break;
 	default:
 		r = su->scale;
 		break;
 	}
 
 	return(r);
 }
 
 static void
 ps_growbuf(struct termp *p, size_t sz)
 {
 	if (p->ps->psmargcur + sz <= p->ps->psmargsz)
 		return;
 
 	if (sz < PS_BUFSLOP)
 		sz = PS_BUFSLOP;
 
 	p->ps->psmargsz += sz;
 	p->ps->psmarg = mandoc_realloc(p->ps->psmarg, p->ps->psmargsz);
 }
Index: vendor/mdocml/dist/test-dirent-namlen.c
===================================================================
--- vendor/mdocml/dist/test-dirent-namlen.c	(nonexistent)
+++ vendor/mdocml/dist/test-dirent-namlen.c	(revision 275397)
@@ -0,0 +1,10 @@
+#include 
+#include 
+
+int
+main(void)
+{
+	struct dirent	 entry;
+
+	return (sizeof(entry.d_namlen) == 0);
+}

Property changes on: vendor/mdocml/dist/test-dirent-namlen.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: vendor/mdocml/dist/test-fts.c
===================================================================
--- vendor/mdocml/dist/test-fts.c	(nonexistent)
+++ vendor/mdocml/dist/test-fts.c	(revision 275397)
@@ -0,0 +1,42 @@
+#include 
+#include 
+#include 
+#include 
+
+int
+main(void)
+{
+	const char	*argv[2];
+	FTS		*ftsp;
+	FTSENT		*entry;
+
+	argv[0] = ".";
+	argv[1] = (char *)NULL;
+
+	ftsp = fts_open((char * const *)argv,
+	    FTS_PHYSICAL | FTS_NOCHDIR, NULL);
+
+	if (ftsp == NULL) {
+		perror("fts_open");
+		return(1);
+	}
+
+	entry = fts_read(ftsp);
+
+	if (entry == NULL) {
+		perror("fts_read");
+		return(1);
+	}
+
+	if (fts_set(ftsp, entry, FTS_SKIP) != 0) {
+		perror("fts_set");
+		return(1);
+	}
+
+	if (fts_close(ftsp) != 0) {
+		perror("fts_close");
+		return(1);
+	}
+
+	return(0);
+}

Property changes on: vendor/mdocml/dist/test-fts.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: vendor/mdocml/dist/test-getsubopt.c
===================================================================
--- vendor/mdocml/dist/test-getsubopt.c	(revision 275396)
+++ vendor/mdocml/dist/test-getsubopt.c	(revision 275397)
@@ -1,19 +1,34 @@
+/*	$Id: test-getsubopt.c,v 1.3 2014/08/17 20:53:50 schwarze Exp $	*/
+/*
+ * Copyright (c) 2011 Kristaps Dzonsons 
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
 #if defined(__linux__) || defined(__MINT__)
-# define _GNU_SOURCE /* getsubopt() */
+#define _GNU_SOURCE /* getsubopt() */
 #endif
 
 #include 
 
-extern char *suboptarg;
-
 int
 main(void)
 {
 	char buf[] = "k=v";
 	char *options = buf;
 	char token0[] = "k";
 	char *const tokens[] = { token0, NULL };
 	char *value = NULL;
 	return( ! (0 == getsubopt(&options, tokens, &value)
-	    && suboptarg == buf && value == buf+2 && options == buf+3));
+	    && value == buf+2 && options == buf+3));
 }
Index: vendor/mdocml/dist/test-sqlite3.c
===================================================================
--- vendor/mdocml/dist/test-sqlite3.c	(nonexistent)
+++ vendor/mdocml/dist/test-sqlite3.c	(revision 275397)
@@ -0,0 +1,47 @@
+/*	$Id: test-sqlite3.c,v 1.1 2014/08/16 19:00:01 schwarze Exp $	*/
+/*
+ * Copyright (c) 2014 Ingo Schwarze 
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include 
+#include 
+#include 
+
+int
+main(void)
+{
+	sqlite3	*db;
+
+	if (sqlite3_open_v2("test.db", &db,
+	    SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
+	    NULL) != SQLITE_OK) {
+		perror("test.db");
+		fprintf(stderr, "sqlite3_open_v2: %s", sqlite3_errmsg(db));
+		return(1);
+	}
+	unlink("test.db");
+
+	if (sqlite3_exec(db, "PRAGMA foreign_keys = ON",
+	    NULL, NULL, NULL) != SQLITE_OK) {
+		fprintf(stderr, "sqlite3_exec: %s", sqlite3_errmsg(db));
+		return(1);
+	}
+
+	if (sqlite3_close(db) != SQLITE_OK) {
+		fprintf(stderr, "sqlite3_close: %s", sqlite3_errmsg(db));
+		return(1);
+	}
+	return(0);
+}

Property changes on: vendor/mdocml/dist/test-sqlite3.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: vendor/mdocml/dist/test-wchar.c
===================================================================
--- vendor/mdocml/dist/test-wchar.c	(nonexistent)
+++ vendor/mdocml/dist/test-wchar.c	(revision 275397)
@@ -0,0 +1,63 @@
+/*	$Id: test-wchar.c,v 1.2 2014/08/28 10:38:06 schwarze Exp $	*/
+/*
+ * Copyright (c) 2014 Ingo Schwarze 
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#if defined(__linux__) || defined(__MINT__)
+#define _GNU_SOURCE /* wcwidth() */
+#endif
+
+#include 
+#include 
+#include 
+#include 
+
+int
+main(void)
+{
+	wchar_t	 wc;
+	int	 width;
+
+	if (setlocale(LC_ALL, "") == NULL) {
+		fputs("setlocale(LC_ALL, \"\") failed\n", stderr);
+		return(1);
+	}
+
+	if (setlocale(LC_CTYPE, "en_US.UTF-8") == NULL) {
+		fputs("setlocale(LC_CTYPE, \"en_US.UTF-8\") failed\n",
+		    stderr);
+		return(1);
+	}
+
+	if (sizeof(wchar_t) < 4) {
+		fprintf(stderr, "wchar_t is only %zu bytes\n",
+		    sizeof(wchar_t));
+		return(1);
+	}
+
+	if ((width = wcwidth(L' ')) != 1) {
+		fprintf(stderr, "wcwidth(L' ') returned %d\n", width);
+		return(1);
+	}
+
+	dup2(STDERR_FILENO, STDOUT_FILENO);
+	wc = L'*';
+	if (putwchar(wc) != (wint_t)wc) {
+		fputs("bad putwchar return value\n", stderr);
+		return(1);
+	}
+
+	return(0);
+}

Property changes on: vendor/mdocml/dist/test-wchar.c
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Added: svn:keywords
## -0,0 +1 ##
+FreeBSD=%H
\ No newline at end of property
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Index: vendor/mdocml/dist/tree.c
===================================================================
--- vendor/mdocml/dist/tree.c	(revision 275396)
+++ vendor/mdocml/dist/tree.c	(revision 275397)
@@ -1,349 +1,370 @@
-/*	$Id: tree.c,v 1.53 2014/07/02 07:10:38 schwarze Exp $ */
+/*	$Id: tree.c,v 1.60 2014/11/28 05:51:32 schwarze Exp $ */
 /*
- * Copyright (c) 2008, 2009, 2011 Kristaps Dzonsons 
+ * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons 
  * Copyright (c) 2013, 2014 Ingo Schwarze 
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
  *
  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#ifdef HAVE_CONFIG_H
 #include "config.h"
-#endif
 
+#include 
+
 #include 
 #include 
 #include 
 #include 
 #include 
 
 #include "mandoc.h"
 #include "mdoc.h"
 #include "man.h"
 #include "main.h"
 
 static	void	print_box(const struct eqn_box *, int);
 static	void	print_man(const struct man_node *, int);
 static	void	print_mdoc(const struct mdoc_node *, int);
 static	void	print_span(const struct tbl_span *, int);
 
 
 void
 tree_mdoc(void *arg, const struct mdoc *mdoc)
 {
 
 	print_mdoc(mdoc_node(mdoc), 0);
 }
 
 void
 tree_man(void *arg, const struct man *man)
 {
 
 	print_man(man_node(man), 0);
 }
 
 static void
 print_mdoc(const struct mdoc_node *n, int indent)
 {
 	const char	 *p, *t;
 	int		  i, j;
 	size_t		  argc;
 	struct mdoc_argv *argv;
 
 	argv = NULL;
 	argc = 0;
 	t = p = NULL;
 
 	switch (n->type) {
 	case MDOC_ROOT:
 		t = "root";
 		break;
 	case MDOC_BLOCK:
 		t = "block";
 		break;
 	case MDOC_HEAD:
 		t = "block-head";
 		break;
 	case MDOC_BODY:
 		if (n->end)
 			t = "body-end";
 		else
 			t = "block-body";
 		break;
 	case MDOC_TAIL:
 		t = "block-tail";
 		break;
 	case MDOC_ELEM:
 		t = "elem";
 		break;
 	case MDOC_TEXT:
 		t = "text";
 		break;
 	case MDOC_TBL:
-		/* FALLTHROUGH */
+		break;
 	case MDOC_EQN:
+		t = "eqn";
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	switch (n->type) {
 	case MDOC_TEXT:
 		p = n->string;
 		break;
 	case MDOC_BODY:
 		p = mdoc_macronames[n->tok];
 		break;
 	case MDOC_HEAD:
 		p = mdoc_macronames[n->tok];
 		break;
 	case MDOC_TAIL:
 		p = mdoc_macronames[n->tok];
 		break;
 	case MDOC_ELEM:
 		p = mdoc_macronames[n->tok];
 		if (n->args) {
 			argv = n->args->argv;
 			argc = n->args->argc;
 		}
 		break;
 	case MDOC_BLOCK:
 		p = mdoc_macronames[n->tok];
 		if (n->args) {
 			argv = n->args->argv;
 			argc = n->args->argc;
 		}
 		break;
 	case MDOC_TBL:
-		/* FALLTHROUGH */
+		break;
 	case MDOC_EQN:
+		p = "EQ";
 		break;
 	case MDOC_ROOT:
 		p = "root";
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	if (n->span) {
 		assert(NULL == p && NULL == t);
 		print_span(n->span, indent);
-	} else if (n->eqn) {
-		assert(NULL == p && NULL == t);
-		print_box(n->eqn->root, indent);
 	} else {
 		for (i = 0; i < indent; i++)
 			putchar('\t');
 
 		printf("%s (%s)", p, t);
 
 		for (i = 0; i < (int)argc; i++) {
 			printf(" -%s", mdoc_argnames[argv[i].arg]);
 			if (argv[i].sz > 0)
 				printf(" [");
 			for (j = 0; j < (int)argv[i].sz; j++)
 				printf(" [%s]", argv[i].value[j]);
 			if (argv[i].sz > 0)
 				printf(" ]");
 		}
 
 		putchar(' ');
 		if (MDOC_LINE & n->flags)
 			putchar('*');
 		printf("%d:%d", n->line, n->pos + 1);
 		if (n->lastline != n->line)
 			printf("-%d", n->lastline);
 		putchar('\n');
 	}
 
+	if (n->eqn)
+		print_box(n->eqn->root->first, indent + 1);
 	if (n->child)
 		print_mdoc(n->child, indent + 1);
 	if (n->next)
 		print_mdoc(n->next, indent);
 }
 
 static void
 print_man(const struct man_node *n, int indent)
 {
 	const char	 *p, *t;
 	int		  i;
 
 	t = p = NULL;
 
 	switch (n->type) {
 	case MAN_ROOT:
 		t = "root";
 		break;
 	case MAN_ELEM:
 		t = "elem";
 		break;
 	case MAN_TEXT:
 		t = "text";
 		break;
 	case MAN_BLOCK:
 		t = "block";
 		break;
 	case MAN_HEAD:
 		t = "block-head";
 		break;
 	case MAN_BODY:
 		t = "block-body";
 		break;
-	case MAN_TAIL:
-		t = "block-tail";
-		break;
 	case MAN_TBL:
-		/* FALLTHROUGH */
+		break;
 	case MAN_EQN:
+		t = "eqn";
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	switch (n->type) {
 	case MAN_TEXT:
 		p = n->string;
 		break;
 	case MAN_ELEM:
 		/* FALLTHROUGH */
 	case MAN_BLOCK:
 		/* FALLTHROUGH */
 	case MAN_HEAD:
 		/* FALLTHROUGH */
-	case MAN_TAIL:
-		/* FALLTHROUGH */
 	case MAN_BODY:
 		p = man_macronames[n->tok];
 		break;
 	case MAN_ROOT:
 		p = "root";
 		break;
 	case MAN_TBL:
-		/* FALLTHROUGH */
+		break;
 	case MAN_EQN:
+		p = "EQ";
 		break;
 	default:
 		abort();
 		/* NOTREACHED */
 	}
 
 	if (n->span) {
 		assert(NULL == p && NULL == t);
 		print_span(n->span, indent);
-	} else if (n->eqn) {
-		assert(NULL == p && NULL == t);
-		print_box(n->eqn->root, indent);
 	} else {
 		for (i = 0; i < indent; i++)
 			putchar('\t');
 		printf("%s (%s) ", p, t);
 		if (MAN_LINE & n->flags)
 			putchar('*');
 		printf("%d:%d\n", n->line, n->pos + 1);
 	}
 
+	if (n->eqn)
+		print_box(n->eqn->root->first, indent + 1);
 	if (n->child)
 		print_man(n->child, indent + 1);
 	if (n->next)
 		print_man(n->next, indent);
 }
 
 static void
 print_box(const struct eqn_box *ep, int indent)
 {
 	int		 i;
 	const char	*t;
 
+	static const char *posnames[] = {
+	    NULL, "sup", "subsup", "sub",
+	    "to", "from", "fromto",
+	    "over", "sqrt", NULL };
+
 	if (NULL == ep)
 		return;
 	for (i = 0; i < indent; i++)
 		putchar('\t');
 
 	t = NULL;
 	switch (ep->type) {
 	case EQN_ROOT:
 		t = "eqn-root";
 		break;
+	case EQN_LISTONE:
 	case EQN_LIST:
 		t = "eqn-list";
 		break;
 	case EQN_SUBEXPR:
 		t = "eqn-expr";
 		break;
 	case EQN_TEXT:
 		t = "eqn-text";
 		break;
+	case EQN_PILE:
+		t = "eqn-pile";
+		break;
 	case EQN_MATRIX:
 		t = "eqn-matrix";
 		break;
 	}
 
-	assert(t);
-	printf("%s(%d, %d, %d, %d, %d, \"%s\", \"%s\") %s\n",
-	    t, EQN_DEFSIZE == ep->size ? 0 : ep->size,
-	    ep->pos + 1, ep->font, ep->mark, ep->pile,
-	    ep->left ? ep->left : "",
-	    ep->right ? ep->right : "",
-	    ep->text ? ep->text : "");
+	fputs(t, stdout);
+	if (ep->pos)
+		printf(" pos=%s", posnames[ep->pos]);
+	if (ep->left)
+		printf(" left=\"%s\"", ep->left);
+	if (ep->right)
+		printf(" right=\"%s\"", ep->right);
+	if (ep->top)
+		printf(" top=\"%s\"", ep->top);
+	if (ep->bottom)
+		printf(" bottom=\"%s\"", ep->bottom);
+	if (ep->text)
+		printf(" text=\"%s\"", ep->text);
+	if (ep->font)
+		printf(" font=%d", ep->font);
+	if (ep->size != EQN_DEFSIZE)
+		printf(" size=%d", ep->size);
+	if (ep->expectargs != UINT_MAX && ep->expectargs != ep->args)
+		printf(" badargs=%zu(%zu)", ep->args, ep->expectargs);
+	else if (ep->args)
+		printf(" args=%zu", ep->args);
+	putchar('\n');
 
 	print_box(ep->first, indent + 1);
 	print_box(ep->next, indent);
 }
 
 static void
 print_span(const struct tbl_span *sp, int indent)
 {
 	const struct tbl_dat *dp;
 	int		 i;
 
 	for (i = 0; i < indent; i++)
 		putchar('\t');
 
 	switch (sp->pos) {
 	case TBL_SPAN_HORIZ:
 		putchar('-');
 		return;
 	case TBL_SPAN_DHORIZ:
 		putchar('=');
 		return;
 	default:
 		break;
 	}
 
 	for (dp = sp->first; dp; dp = dp->next) {
 		switch (dp->pos) {
 		case TBL_DATA_HORIZ:
 			/* FALLTHROUGH */
 		case TBL_DATA_NHORIZ:
 			putchar('-');
 			continue;
 		case TBL_DATA_DHORIZ:
 			/* FALLTHROUGH */
 		case TBL_DATA_NDHORIZ:
 			putchar('=');
 			continue;
 		default:
 			break;
 		}
 		printf("[\"%s\"", dp->string ? dp->string : "");
 		if (dp->spans)
 			printf("(%d)", dp->spans);
 		if (NULL == dp->layout)
 			putchar('*');
 		putchar(']');
 		putchar(' ');
 	}
 
 	printf("(tbl) %d:1\n", sp->line);
 }