diff --git a/1.14.5-master.patch b/1.14.5-master.patch deleted file mode 100644 index 9212767..0000000 --- a/1.14.5-master.patch +++ /dev/null @@ -1,7696 +0,0 @@ ---- a/Makefile.depend -+++ b/Makefile.depend -@@ -29,7 +29,7 @@ dbm.o: dbm.c config.h mansearch.h dbm_ma - dbm_map.o: dbm_map.c config.h mansearch.h dbm_map.h dbm.h - demandoc.o: demandoc.c config.h mandoc.h roff.h man.h mdoc.h mandoc_parse.h - eqn.o: eqn.c config.h mandoc_aux.h mandoc.h roff.h eqn.h libmandoc.h eqn_parse.h --eqn_html.o: eqn_html.c config.h mandoc.h eqn.h out.h html.h -+eqn_html.o: eqn_html.c config.h mandoc.h roff.h eqn.h out.h html.h - eqn_term.o: eqn_term.c config.h eqn.h out.h term.h - html.o: html.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h out.h html.h manconf.h main.h - lib.o: lib.c config.h roff.h libmdoc.h lib.in -@@ -37,16 +37,16 @@ main.o: main.c config.h mandoc_aux.h man - man.o: man.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h - man_html.o: man_html.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h html.h main.h - man_macro.o: man_macro.c config.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h --man_term.o: man_term.c config.h mandoc_aux.h roff.h man.h out.h term.h main.h -+man_term.o: man_term.c config.h mandoc_aux.h mandoc.h roff.h man.h out.h term.h tag.h main.h - man_validate.o: man_validate.c config.h mandoc_aux.h mandoc.h roff.h man.h libmandoc.h roff_int.h libman.h - mandoc.o: mandoc.c config.h mandoc_aux.h mandoc.h roff.h libmandoc.h roff_int.h - mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h --mandoc_msg.o: mandoc_msg.c mandoc.h -+mandoc_msg.o: mandoc_msg.c config.h mandoc.h - mandoc_ohash.o: mandoc_ohash.c mandoc_aux.h mandoc_ohash.h compat_ohash.h - mandoc_xr.o: mandoc_xr.c mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc_xr.h - mandocd.o: mandocd.c config.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h main.h manconf.h - mandocdb.o: mandocdb.c config.h compat_fts.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mdoc.h man.h mandoc_parse.h manconf.h mansearch.h dba_array.h dba.h --manpath.o: manpath.c config.h mandoc_aux.h manconf.h -+manpath.o: manpath.c config.h mandoc_aux.h mandoc.h manconf.h - mansearch.o: mansearch.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h manconf.h mansearch.h dbm.h - mdoc.o: mdoc.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h - mdoc_argv.o: mdoc_argv.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h -@@ -67,10 +67,10 @@ roff_term.o: roff_term.c mandoc.h roff.h - roff_validate.o: roff_validate.c mandoc.h roff.h libmandoc.h roff_int.h - soelim.o: soelim.c config.h compat_stringlist.h - st.o: st.c config.h mandoc.h roff.h libmdoc.h --tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h tag.h -+tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h tag.h - tbl.o: tbl.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_parse.h tbl_int.h - tbl_data.o: tbl_data.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_int.h --tbl_html.o: tbl_html.c config.h mandoc.h tbl.h out.h html.h -+tbl_html.o: tbl_html.c config.h mandoc.h roff.h tbl.h out.h html.h - tbl_layout.o: tbl_layout.c config.h mandoc_aux.h mandoc.h tbl.h libmandoc.h tbl_int.h - tbl_opts.o: tbl_opts.c config.h mandoc.h tbl.h libmandoc.h tbl_int.h - tbl_term.o: tbl_term.c config.h mandoc.h tbl.h out.h term.h ---- a/NEWS -+++ b/NEWS -@@ -2,6 +2,88 @@ $Id: NEWS,v 1.34 2019/03/10 09:32:00 sch - - This file lists the most important changes in the mandoc.bsd.lv distribution. - -+Changes in version 1.14.6, released on XXX XXX, 2019 -+ -+ --- MAJOR NEW FEATURES --- -+ * man(1) -T ascii: slowly start implementing tagging support for man(7) -+ pages: tag alphabetic arguments of .IP, .TP, and .TQ macros -+ * -T html: wrap text and phrasing elements in paragraphs unless -+ already contained in flow containers; never put them directly -+ into sections. This helps to format paragraphs with the CSS -+ class selector .Pp. -+ --- MINOR NEW FEATURES --- -+ * roff(7): implement the .break request (break out of a .while loop) -+ * if messages are shown and output is printed without a pager, -+ display a heads-up on stderr at the end because otherwise, users -+ may easily miss the messages -+ * mandoc.css: support prefers-color-scheme: dark -+ --- RELIABILITY BUGFIXES --- -+ * man(1): do not segfault if /tmp/ is not writeable -+ * tbl(7): fix a crash when the last column is only reached by spans -+ * tbl(7) -T ascii: fix a NULL pointer access on empty data cells -+ * tbl(7) -T ascii: fix a NULL pointer access on a line next to a short row -+ * -T html: fix an assertion failure caused by .ft in rare situations -+ * roff(7): fix a rare case of writing one byte past the end of the input buffer -+ --- MINOR FUNCTIONAL IMPROVEMENTS --- -+ * man(1) -h: for pages lacking a SYNOPSIS, show the NAME section -+ * man(1): when the first argument starts with a digit, optionally -+ followed by a letter, and at least one more argument follows, -+ interpret the first argument as a section name even when additional -+ characters follow after the digit and letter -+ * man(1): with a specific section requested, try harder to find -+ the best match; use this order of preference: -+ 1. The section in both the directory name and the file name matches exactly. -+ 2. The section in the file name matches exactly. -+ 3. The section in the directory name matches exactly. -+ 4. Neither of them matches exactly. -+ * man(1): if no tags were generated at all, unlink(2) the empty -+ tags file as soon the condition can be detected and do not pass -+ it to less(1) -+ * makewhatis(8): handle both dangling symlinks and .so links -+ in manual page directories more gracefully -+ * man.cgi(8): for invalid queries and for valid queries returning -+ no result, return the appropriate 40x status code rather than 200 -+ * tbl(7) -T utf8: improved rendering of horizontal lines -+ * mdoc(7) -T html: format .Nd with rather than
-+ * mdoc(7) -T lint: do not warn about $Mdocdate$ without an actual date -+ * mdoc(7) -T lint: do not complain about function types of the -+ form "ret_type (fname)(args)", but otherwise check names more strictly -+ --- MINOR BUGFIXES --- -+ * man(1): do the search for each name independently, and show the -+ results in the order of the command line argument -+ * man(1): when asking for a single manual page by name, prefer -+ file name matches over .Dt/.TH matches over first NAME matches -+ over later NAME matches, but do not change the ordering for -+ apropos(1) nor for man -a -+ * roff(7): when calling an empty macro, do not clobber existing arguments -+ * mdoc(7) .Bl -column: parse Macro in .It "wordword" Ta word Macro -+ * -T html: remove some spurious line breaks, in particular inside
-+    --- STRUCTURAL IMPROVEMENTS ---
-+ * move some code out of the giant main() into separate functions
-+   doing one well-defined task each
-+ * clearly separate parser state (struct curparse) and formatter state
-+   (struct outstate), don't mix them in the same struct
-+ * in the HTML formatter, assert(3) that no HTML nesting violation occurs
-+ * let html_close_paragraph() close any phrasing context
-+    --- THANKS TO ---
-+ * Marc Espie (OpenBSD) for a patch and for suggesting a feature impovement
-+ * Anton Lindqvist (OpenBSD) for a patch
-+ * Armin Besirovic for a contribution to mandoc.css
-+ * Lorenzo Beretta for three bug reports
-+   and for suggesting two feature impovements
-+ * Anthony Bentley (OpenBSD) for three bug reports
-+   and for suggesting a feature impovement
-+ * Michael Stapelberg (Debian) and Jan Stary for a bug report
-+   and for suggesting a feature impovement
-+ * Stephen Gregoratto for two bug reports
-+ * Brian Callahan, Klemens Nanni (OpenBSD), Jason Thorpe (NetBSD),
-+   Yuri Pankov (FreeBSD), and Edgar Pettijohn for bug reports
-+ * Theo Buehler (OpenBSD), Leah Neukirchen (Void Linux), Colin Watson (Debian),
-+   and John Gardner for suggesting feature impovements
-+ * TJ Townsend (OpenBSD) for help with CSS
-+ * Christos Zoulas (NetBSD) for a report regarding portability
-+ * Michal Nowak for reporting four code style issues
-+
- Changes in version 1.14.5, released on March 10, 2019
- 
-     --- MAJOR NEW FEATURES ---
---- a/TODO
-+++ b/TODO
-@@ -62,8 +62,33 @@ are mere guesses, and some may be wrong.
-   needed for Tcl_NewStringObj(3) via wiz@  Wed, 5 Mar 2014 22:27:43 +0100
-   loc **  exist ***  algo ***  size *  imp ***
- 
-+- .als only works for macros in mandoc, not for user-defined strings.
-+  Also, the "val" field in struct roffkv would have to be replaced
-+  with a pointer to a reference-counted wrapper, and an alias
-+  would have to point to the same wrapper as the original.
-+  .als to undefined does nothing; the alias is not created.
-+  .rm'ing the original leaves the alias to point to the old value.
-+  .de .als .de changes both, but
-+  .de .als .rm .de only changes the new value, not the alias.
-+  Found in groffer(1) version 1.19
-+  Jan Stary 20 Apr 2019 20:16:54 +0200
-+  loc *  exist **  algo **  size **  imp *
-+
-+- roff string condition comparisons fail when vars contain quotes:
-+  .ds s '
-+  .if '\*s'' \&...
-+  hard to fix because of the basic architecture (string replacement
-+  happens before roff(7) syntax parsing)
-+  Found in groffer(1) version 1.19
-+  Jan Stary 20 Apr 2019 20:16:54 +0200
-+  loc *  exist ***  algo ***  size **  imp *
-+
- --- missing mdoc features ----------------------------------------------
- 
-+- .Sh and .Ss should be parsed and partially callable, see groff_mdoc(7)
-+  reed at reedmedia dot net Sat, 21 Dec 2019 17:13:07 -0600
-+  loc **  exist **  algo **  size **  imp *
-+
- - .Bl -column .Xo support is missing
-   ultimate goal:
-   restore .Xr and .Dv to
-@@ -264,6 +289,9 @@ are mere guesses, and some may be wrong.
-   https://github.com/schmonz/ikiwiki/compare/mandoc
-   Amitai Schlair  Mon, 19 May 2014 14:05:53 -0400
- 
-+- check compatibility with
-+  https://git.sr.ht/~sircmpwn/scdoc
-+
- - check features of the Slackware man.conf(5) format
-   Carsten Kunze  Wed, 11 Mar 2015 17:57:24 +0100
- 
-@@ -350,6 +378,11 @@ are mere guesses, and some may be wrong.
- 
- --- HTML issues --------------------------------------------------------
- 
-+- get rid of the last handful of style= attributes such that
-+  Content-Security-Policy: can be enabled without unsafe-inline
-+  suggested by bentley@  Nov 10, 2019 at 06:02:49AM -0700
-+  loc *  exist *  algo *  size *  imp **
-+
- - .Bf at the beginning of a paragraph inserts a bogus 1ex horizontal
-   space, see for example random(3).  Introduced in
-   http://mdocml.bsd.lv/cgi-bin/cvsweb/mdoc_html.c.diff?r1=1.91&r2=1.92
-@@ -362,6 +395,11 @@ are mere guesses, and some may be wrong.
-   https://github.com/Debian/debiman/issues/15
-   loc *  exist *  algo **  size **  imp **
- 
-+- space characters can end up in href= attributes, for example coming
-+  from the first .Xr argument (where they make no sense, but still);
-+  does this affect other characters, other source macros...?
-+  Jackson Pauls  29 Aug 2017 16:56:27 +0100
-+
- - 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...
-@@ -538,6 +576,15 @@ are mere guesses, and some may be wrong.
- * to improve in the groff_mdoc(7) macros
- ************************************************************************
- 
-+- delete OS release verification from .Dx, .Fx, .Nx, .Ox etc.
-+  https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=629161
-+  also Branden Robinson 18 Dec 2019 00:59:52 +1100
-+
-+- Can the distinction between .Vt and .Va be made stricter,
-+  recommending .Vt extern char * Ns Va optarg ; ?
-+  What about the block macro properties of .Vt in the SYNOPSIS?
-+  zeurkous 25 Dec 2019 08:48:36 +0100
-+
- - .Cd # arch1, arch2 in section 4 pages:
-   find better way to indicate multiple architectures, maybe:
-   allow .Dt vgafb 4 "macppc sparc64"
---- a/arch.c
-+++ b/arch.c
-@@ -26,7 +26,7 @@ arch_valid(const char *arch, enum mandoc
- 	const char *openbsd_arch[] = {
- 		"alpha", "amd64", "arm64", "armv7", "hppa", "i386",
- 		"landisk", "loongson", "luna88k", "macppc", "mips64",
--		"octeon", "sgi", "socppc", "sparc64", NULL
-+		"octeon", "sgi", "sparc64", NULL
- 	};
- 	const char *netbsd_arch[] = {
- 		"acorn26", "acorn32", "algor", "alpha", "amiga",
---- a/cgi.c
-+++ b/cgi.c
-@@ -1,7 +1,7 @@
- /*	$Id: cgi.c,v 1.166 2019/03/06 12:32:41 schwarze Exp $ */
- /*
-  * Copyright (c) 2011, 2012 Kristaps Dzonsons 
-- * Copyright (c) 2014, 2015, 2016, 2017, 2018 Ingo Schwarze 
-+ * Copyright (c) 2014-2019 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
-@@ -77,7 +77,8 @@ static	void		 parse_query_string(struct
- 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_noresult(const struct req *, int, const char *,
-+				const char *);
- static	void		 pg_redirect(const struct req *, const char *);
- static	void		 pg_search(const struct req *);
- static	void		 pg_searchres(const struct req *,
-@@ -339,6 +340,8 @@ resp_begin_http(int code, const char *ms
- 
- 	printf("Content-Type: text/html; charset=utf-8\r\n"
- 	     "Cache-Control: no-cache\r\n"
-+	     "Content-Security-Policy: default-src 'none'; "
-+	     "style-src 'self' 'unsafe-inline'\r\n"
- 	     "Pragma: no-cache\r\n"
- 	     "\r\n");
- 
-@@ -408,7 +411,8 @@ resp_searchform(const struct req *req, e
- {
- 	int		 i;
- 
--	printf("
\n" -+ printf("\n" - "
\n" - " Manual Page Search Parameters\n", - scriptname); -@@ -546,12 +550,13 @@ pg_index(const struct req *req) - } - - static void --pg_noresult(const struct req *req, const char *msg) -+pg_noresult(const struct req *req, int code, const char *http_msg, -+ const char *user_msg) - { -- resp_begin_html(200, NULL, NULL); -+ resp_begin_html(code, http_msg, NULL); - resp_searchform(req, FOCUS_QUERY); - puts("

"); -- puts(msg); -+ puts(user_msg); - puts("

"); - resp_end_html(); - } -@@ -869,7 +874,6 @@ resp_format(const struct req *req, const - memset(&conf, 0, sizeof(conf)); - conf.fragment = 1; - conf.style = mandoc_strdup(CSS_DIR "/mandoc.css"); -- conf.toc = 1; - usepath = strcmp(req->q.manpath, req->p[0]); - mandoc_asprintf(&conf.man, "/%s%s%s%s%%N.%%S", - scriptname, *scriptname == '\0' ? "" : "/", -@@ -1017,9 +1021,10 @@ pg_search(const struct req *req) - if (req->isquery && req->q.equal && argc == 1) - pg_redirect(req, argv[0]); - else if (mansearch(&search, &paths, argc, argv, &res, &ressz) == 0) -- pg_noresult(req, "You entered an invalid query."); -+ pg_noresult(req, 400, "Bad Request", -+ "You entered an invalid query."); - else if (ressz == 0) -- pg_noresult(req, "No results found."); -+ pg_noresult(req, 404, "Not Found", "No results found."); - else - pg_searchres(req, res, ressz); - ---- a/configure -+++ b/configure -@@ -41,7 +41,7 @@ OSENUM= - OSNAME= - UTF8_LOCALE= - --CC=`printf "all:\\n\\t@echo \\\$(CC)\\n" | env -i make -sf -` -+CC=cc - CFLAGS= - LDADD= - LDFLAGS= -@@ -529,7 +529,7 @@ fi - echo "extern char *mkdtemp(char *);" - - if [ ${HAVE_PROGNAME} -eq 0 ]; then -- echo "extern const char *getprogname(void);" -+ echo "extern const char *getprogname(void);" - echo "extern void setprogname(const char *);" - fi - ---- a/configure.local.example -+++ b/configure.local.example -@@ -28,6 +28,14 @@ - - # --- user settings relevant for all builds ---------------------------- - -+# By default, "cc" is used as the C compiler, but it can be overridden. -+# For example, the system compiler in SunOS 5.9 may not provide , -+# which may require this line: -+CC=gcc -+ -+# IBM AIX may need: -+CC=xlc -+ - # 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, -@@ -268,21 +276,6 @@ BINM_CATMAN=mcatman # default is "catma - - # 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 -- --# Because the system compiler may not provide , --# SunOS 5.9 may need: -- --CC=gcc -- --# IBM AIX may need: -- --CC=xlc -- - # Normally, leave CFLAGS unset. In that case, -g will automatically - # be used, and various -W options will be added if the compiler - # supports them. If you define CFLAGS manually, it will be used ---- a/dbm.c -+++ b/dbm.c -@@ -233,7 +233,7 @@ static struct dbm_res - page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match) - { - static const struct dbm_match *match; -- static const char *cp; -+ static const char *cp; - static int32_t ip; - struct dbm_res res = {-1, 0}; - -@@ -315,7 +315,7 @@ page_byarch(const struct dbm_match *arg_ - static const struct dbm_match *match; - struct dbm_res res = {-1, 0}; - static int32_t ip; -- const char *cp; -+ const char *cp; - - /* Initialize for a new iteration. */ - ---- a/dbm_map.h -+++ b/dbm_map.h -@@ -14,7 +14,7 @@ - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * -- * Private interface for low-level routines for the map-based version -+ * Private interface for low-level routines for the map-based version - * of the mandoc database, for read-only access. - * To be used by dbm*.c only. - */ ---- a/eqn.7 -+++ b/eqn.7 -@@ -44,28 +44,16 @@ 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 . -+An equation starts with an input line containing exactly the characters -+.Sq \&.EQ , -+may contain multiple input lines, and ends with an input line -+containing exactly the characters -+.Sq \&.EN . -+Equivalently, an equation can be given in the middle of a single -+text input line by surrounding it with the equation delimiters -+defined with the -+.Cm delim -+statement. - .Pp - The equation grammar is as follows, where quoted strings are - case-sensitive literals in the input: -@@ -178,6 +166,25 @@ statement is a synonym for - while - .Cm tdefine - is discarded. -+.It Cm delim -+This statement takes a string argument consisting of two bytes, -+to be used as the opening and closing delimiters for equations -+in the middle of text input lines. -+Conventionally, the dollar sign is used for both delimiters, -+as follows: -+.Bd -literal -offset indent -+\&.EQ -+delim $$ -+\&.EN -+An equation like $sin pi = 0$ can now be entered -+in the middle of a text input line. -+.Ed -+.Pp -+The special statement -+.Cm delim off -+temporarily disables previously declared delimiters and -+.Cm delim on -+reenables them. - .It Cm gfont - Set the default font of subsequent output. - Its syntax is as follows: -@@ -470,7 +477,7 @@ commands are also ignored. - .%T System for Typesetting Mathematics - .%J Communications of the ACM - .%V 18 --.%P 151\(en157 -+.%P pp. 151\(en157 - .%D March, 1975 - .Re - .Rs ---- a/eqn.c -+++ b/eqn.c -@@ -1,7 +1,7 @@ - /* $Id: eqn.c,v 1.83 2018/12/14 06:33:14 schwarze Exp $ */ - /* - * Copyright (c) 2011, 2014 Kristaps Dzonsons -- * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze -+ * Copyright (c) 2014,2015,2017,2018,2020 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 -@@ -399,6 +399,14 @@ eqn_next(struct eqn_node *ep, enum parse - case '"': - quoted = 1; - break; -+ case ' ': -+ case '\t': -+ case '~': -+ case '^': -+ if (quoted) -+ break; -+ ep->start++; -+ continue; - default: - break; - } -@@ -669,7 +677,7 @@ eqn_parse(struct eqn_node *ep) - if (ep->data == NULL) - return; - -- ep->start = ep->end = ep->data + strspn(ep->data, " ^~"); -+ ep->start = ep->end = ep->data; - - next_tok: - tok = eqn_next(ep, MODE_TOK); ---- a/eqn_html.c -+++ b/eqn_html.c -@@ -26,6 +26,7 @@ - #include - - #include "mandoc.h" -+#include "roff.h" - #include "eqn.h" - #include "out.h" - #include "html.h" ---- a/html.c -+++ b/html.c -@@ -42,34 +42,30 @@ - struct htmldata { - const char *name; - int flags; --#define HTML_NOSTACK (1 << 0) --#define HTML_AUTOCLOSE (1 << 1) --#define HTML_NLBEFORE (1 << 2) --#define HTML_NLBEGIN (1 << 3) --#define HTML_NLEND (1 << 4) --#define HTML_NLAFTER (1 << 5) -+#define HTML_INPHRASE (1 << 0) /* Can appear in phrasing context. */ -+#define HTML_TOPHRASE (1 << 1) /* Establishes phrasing context. */ -+#define HTML_NOSTACK (1 << 2) /* Does not have an end tag. */ -+#define HTML_NLBEFORE (1 << 3) /* Output line break before opening. */ -+#define HTML_NLBEGIN (1 << 4) /* Output line break after opening. */ -+#define HTML_NLEND (1 << 5) /* Output line break before closing. */ -+#define HTML_NLAFTER (1 << 6) /* Output line break after closing. */ - #define HTML_NLAROUND (HTML_NLBEFORE | HTML_NLAFTER) - #define HTML_NLINSIDE (HTML_NLBEGIN | HTML_NLEND) - #define HTML_NLALL (HTML_NLAROUND | HTML_NLINSIDE) --#define HTML_INDENT (1 << 6) --#define HTML_NOINDENT (1 << 7) -+#define HTML_INDENT (1 << 7) /* Indent content by two spaces. */ -+#define HTML_NOINDENT (1 << 8) /* Exception: never indent content. */ - }; - - static const struct htmldata htmltags[TAG_MAX] = { - {"html", HTML_NLALL}, - {"head", HTML_NLALL | HTML_INDENT}, -- {"body", HTML_NLALL}, -- {"meta", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, -+ {"meta", HTML_NOSTACK | HTML_NLALL}, -+ {"link", HTML_NOSTACK | HTML_NLALL}, -+ {"style", HTML_NLALL | HTML_INDENT}, - {"title", HTML_NLAROUND}, -+ {"body", HTML_NLALL}, - {"div", HTML_NLAROUND}, -- {"div", 0}, - {"section", HTML_NLALL}, -- {"h1", HTML_NLAROUND}, -- {"h2", HTML_NLAROUND}, -- {"span", 0}, -- {"link", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, -- {"br", HTML_NOSTACK | HTML_AUTOCLOSE | HTML_NLALL}, -- {"a", 0}, - {"table", HTML_NLALL | HTML_INDENT}, - {"tr", HTML_NLALL | HTML_INDENT}, - {"td", HTML_NLAROUND}, -@@ -79,16 +75,21 @@ static const struct htmldata htmltags[TA - {"dl", HTML_NLALL | HTML_INDENT}, - {"dt", HTML_NLAROUND}, - {"dd", HTML_NLAROUND | HTML_INDENT}, -- {"p", HTML_NLAROUND | HTML_INDENT}, -- {"pre", HTML_NLALL | HTML_NOINDENT}, -- {"var", 0}, -- {"cite", 0}, -- {"b", 0}, -- {"i", 0}, -- {"code", 0}, -- {"small", 0}, -- {"style", HTML_NLALL | HTML_INDENT}, -- {"math", HTML_NLALL | HTML_INDENT}, -+ {"h1", HTML_TOPHRASE | HTML_NLAROUND}, -+ {"h2", HTML_TOPHRASE | HTML_NLAROUND}, -+ {"p", HTML_TOPHRASE | HTML_NLAROUND | HTML_INDENT}, -+ {"pre", HTML_TOPHRASE | HTML_NLALL | HTML_NOINDENT}, -+ {"a", HTML_INPHRASE | HTML_TOPHRASE}, -+ {"b", HTML_INPHRASE | HTML_TOPHRASE}, -+ {"cite", HTML_INPHRASE | HTML_TOPHRASE}, -+ {"code", HTML_INPHRASE | HTML_TOPHRASE}, -+ {"i", HTML_INPHRASE | HTML_TOPHRASE}, -+ {"small", HTML_INPHRASE | HTML_TOPHRASE}, -+ {"span", HTML_INPHRASE | HTML_TOPHRASE}, -+ {"var", HTML_INPHRASE | HTML_TOPHRASE}, -+ {"br", HTML_INPHRASE | HTML_NOSTACK | HTML_NLALL}, -+ {"mark", HTML_INPHRASE | HTML_NOSTACK }, -+ {"math", HTML_INPHRASE | HTML_NLALL | HTML_INDENT}, - {"mrow", 0}, - {"mi", 0}, - {"mn", 0}, -@@ -120,6 +121,7 @@ static void print_ctag(struct html *, s - static int print_escape(struct html *, char); - static int print_encode(struct html *, const char *, const char *, int); - static void print_href(struct html *, const char *, const char *, int); -+static void print_metaf(struct html *); - - - void * -@@ -202,75 +204,64 @@ print_gen_head(struct html *h) - print_endline(h); - print_text(h, "td.head-vol { text-align: center; }"); - print_endline(h); -- print_text(h, "div.Pp { margin: 1ex 0ex; }"); -- print_endline(h); -- print_text(h, "div.Nd, div.Bf, div.Op { display: inline; }"); -+ print_text(h, ".Nd, .Bf, .Op { display: inline; }"); - print_endline(h); -- print_text(h, "span.Pa, span.Ad { font-style: italic; }"); -+ print_text(h, ".Pa, .Ad { font-style: italic; }"); - print_endline(h); -- print_text(h, "span.Ms { font-weight: bold; }"); -+ print_text(h, ".Ms { font-weight: bold; }"); - print_endline(h); -- print_text(h, "dl.Bl-diag "); -+ print_text(h, ".Bl-diag "); - print_byte(h, '>'); - print_text(h, " dt { font-weight: bold; }"); - print_endline(h); -- print_text(h, "code.Nm, code.Fl, code.Cm, code.Ic, " -- "code.In, code.Fd, code.Fn,"); -- print_endline(h); -- print_text(h, "code.Cd { font-weight: bold; " -- "font-family: inherit; }"); -+ print_text(h, "code.Nm, .Fl, .Cm, .Ic, code.In, .Fd, .Fn, .Cd " -+ "{ font-weight: bold; font-family: inherit; }"); - print_tagq(h, t); - } - --void --print_metaf(struct html *h, enum mandoc_esc deco) -+int -+html_setfont(struct html *h, enum mandoc_esc font) - { -- enum htmlfont font; -- -- switch (deco) { -+ switch (font) { - 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_FONTCW: -- font = HTMLFONT_CW; -+ case ESCAPE_FONTROMAN: - break; - case ESCAPE_FONT: -- case ESCAPE_FONTROMAN: -- font = HTMLFONT_NONE; -+ font = ESCAPE_FONTROMAN; - break; - default: -- return; -+ return 0; - } -+ h->metal = h->metac; -+ h->metac = font; -+ return 1; -+} - -+static void -+print_metaf(struct html *h) -+{ - if (h->metaf) { - print_tagq(h, h->metaf); - h->metaf = NULL; - } -- -- h->metal = h->metac; -- h->metac = font; -- -- switch (font) { -- case HTMLFONT_ITALIC: -+ switch (h->metac) { -+ case ESCAPE_FONTITALIC: - h->metaf = print_otag(h, TAG_I, ""); - break; -- case HTMLFONT_BOLD: -+ case ESCAPE_FONTBOLD: - h->metaf = print_otag(h, TAG_B, ""); - break; -- case HTMLFONT_BI: -+ case ESCAPE_FONTBI: - h->metaf = print_otag(h, TAG_B, ""); - print_otag(h, TAG_I, ""); - break; -- case HTMLFONT_CW: -+ case ESCAPE_FONTCW: - h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); - break; - default: -@@ -281,21 +272,18 @@ print_metaf(struct html *h, enum mandoc_ - void - html_close_paragraph(struct html *h) - { -- struct tag *t; -+ struct tag *this, *next; -+ int flags; - -- for (t = h->tag; t != NULL && t->closed == 0; t = t->next) { -- switch(t->tag) { -- case TAG_P: -- case TAG_PRE: -- print_tagq(h, t); -+ this = h->tag; -+ for (;;) { -+ next = this->next; -+ flags = htmltags[this->tag].flags; -+ if (flags & (HTML_INPHRASE | HTML_TOPHRASE)) -+ print_ctag(h, this); -+ if ((flags & HTML_INPHRASE) == 0) - break; -- case TAG_A: -- print_tagq(h, t); -- continue; -- default: -- continue; -- } -- break; -+ this = next; - } - } - -@@ -479,7 +467,8 @@ print_encode(struct html *h, const char - case ESCAPE_FONTROMAN: - if (0 == norecurse) { - h->flags |= HTML_NOSPACE; -- print_metaf(h, esc); -+ if (html_setfont(h, esc)) -+ print_metaf(h); - h->flags &= ~HTML_NOSPACE; - } - continue; -@@ -593,6 +582,25 @@ print_otag(struct html *h, enum htmltag - - tflags = htmltags[tag].flags; - -+ /* Flow content is not allowed in phrasing context. */ -+ -+ if ((tflags & HTML_INPHRASE) == 0) { -+ for (t = h->tag; t != NULL; t = t->next) { -+ if (t->closed) -+ continue; -+ assert((htmltags[t->tag].flags & HTML_TOPHRASE) == 0); -+ break; -+ } -+ -+ /* -+ * Always wrap phrasing elements in a paragraph -+ * unless already contained in some flow container; -+ * never put them directly into a section. -+ */ -+ -+ } else if (tflags & HTML_TOPHRASE && h->tag->tag == TAG_SECTION) -+ print_otag(h, TAG_P, "c", "Pp"); -+ - /* Push this tag onto the stack of open scopes. */ - - if ((tflags & HTML_NOSTACK) == 0) { -@@ -710,7 +718,7 @@ print_otag(struct html *h, enum htmltag - - /* Accommodate for "well-formed" singleton escaping. */ - -- if (HTML_AUTOCLOSE & htmltags[tag].flags) -+ if (htmltags[tag].flags & HTML_NOSTACK) - print_byte(h, '/'); - - print_byte(h, '>'); -@@ -797,6 +805,16 @@ print_gen_comment(struct html *h, struct - void - print_text(struct html *h, const char *word) - { -+ /* -+ * Always wrap text in a paragraph unless already contained in -+ * some flow container; never put it directly into a section. -+ */ -+ -+ if (h->tag->tag == TAG_SECTION) -+ print_otag(h, TAG_P, "c", "Pp"); -+ -+ /* Output whitespace before this text? */ -+ - if (h->col && (h->flags & HTML_NOSPACE) == 0) { - if ( ! (HTML_KEEP & h->flags)) { - if (HTML_PREKEEP & h->flags) -@@ -806,27 +824,14 @@ print_text(struct html *h, const char *w - print_word(h, " "); - } - -- assert(NULL == h->metaf); -- switch (h->metac) { -- case HTMLFONT_ITALIC: -- h->metaf = print_otag(h, TAG_I, ""); -- break; -- case HTMLFONT_BOLD: -- h->metaf = print_otag(h, TAG_B, ""); -- break; -- case HTMLFONT_BI: -- h->metaf = print_otag(h, TAG_B, ""); -- print_otag(h, TAG_I, ""); -- break; -- case HTMLFONT_CW: -- h->metaf = print_otag(h, TAG_SPAN, "c", "Li"); -- break; -- default: -- print_indent(h); -- break; -- } -+ /* -+ * Print the text, optionally surrounded by HTML whitespace, -+ * optionally manually switching fonts before and after. -+ */ - -- assert(word); -+ assert(h->metaf == NULL); -+ print_metaf(h); -+ print_indent(h); - if ( ! print_encode(h, word, NULL, 0)) { - if ( ! (h->flags & HTML_NONOSPACE)) - h->flags &= ~HTML_NOSPACE; -@@ -834,7 +839,7 @@ print_text(struct html *h, const char *w - } else - h->flags |= HTML_NOSPACE | HTML_NONEWLINE; - -- if (h->metaf) { -+ if (h->metaf != NULL) { - print_tagq(h, h->metaf); - h->metaf = NULL; - } -@@ -964,15 +969,12 @@ print_indent(struct html *h) - { - size_t i; - -- if (h->col) -+ if (h->col || h->noindent) - return; - -- if (h->noindent == 0) { -- h->col = h->indent * 2; -- for (i = 0; i < h->col; i++) -- putchar(' '); -- } -- h->flags &= ~HTML_NOSPACE; -+ h->col = h->indent * 2; -+ for (i = 0; i < h->col; i++) -+ putchar(' '); - } - - /* ---- a/html.h -+++ b/html.h -@@ -19,18 +19,13 @@ - enum htmltag { - TAG_HTML, - TAG_HEAD, -- TAG_BODY, - TAG_META, -+ TAG_LINK, -+ TAG_STYLE, - TAG_TITLE, -+ TAG_BODY, - TAG_DIV, -- TAG_IDIV, - TAG_SECTION, -- TAG_H1, -- TAG_H2, -- TAG_SPAN, -- TAG_LINK, -- TAG_BR, -- TAG_A, - TAG_TABLE, - TAG_TR, - TAG_TD, -@@ -40,15 +35,20 @@ enum htmltag { - TAG_DL, - TAG_DT, - TAG_DD, -+ TAG_H1, -+ TAG_H2, - TAG_P, - TAG_PRE, -- TAG_VAR, -- TAG_CITE, -+ TAG_A, - TAG_B, -- TAG_I, -+ TAG_CITE, - TAG_CODE, -+ TAG_I, - TAG_SMALL, -- TAG_STYLE, -+ TAG_SPAN, -+ TAG_VAR, -+ TAG_BR, -+ TAG_MARK, - TAG_MATH, - TAG_MROW, - TAG_MI, -@@ -69,15 +69,6 @@ enum htmltag { - TAG_MAX - }; - --enum htmlfont { -- HTMLFONT_NONE = 0, -- HTMLFONT_BOLD, -- HTMLFONT_ITALIC, -- HTMLFONT_BI, -- HTMLFONT_CW, -- HTMLFONT_MAX --}; -- - struct tag { - struct tag *next; - int refcnt; -@@ -111,8 +102,8 @@ struct html { - char *base_includes; /* base for include href */ - char *style; /* style-sheet URI */ - struct tag *metaf; /* current open font scope */ -- enum htmlfont metal; /* last used font */ -- enum htmlfont metac; /* current font mode */ -+ enum mandoc_esc metal; /* last used font */ -+ enum mandoc_esc metac; /* current font mode */ - int oflags; /* output options */ - #define HTML_FRAGMENT (1 << 0) /* don't emit HTML/HEAD/BODY */ - #define HTML_TOC (1 << 1) /* emit a table of contents */ -@@ -128,7 +119,6 @@ void roff_html_pre(struct html *, con - void print_gen_comment(struct html *, struct roff_node *); - void print_gen_decls(struct html *); - void print_gen_head(struct html *); --void print_metaf(struct html *, enum mandoc_esc); - struct tag *print_otag(struct html *, enum htmltag, const char *, ...); - void print_tagq(struct html *, const struct tag *); - void print_stagq(struct html *, const struct tag *); -@@ -141,3 +131,4 @@ void print_endline(struct html *); - void html_close_paragraph(struct html *); - enum roff_tok html_fillmode(struct html *, enum roff_tok); - char *html_make_id(const struct roff_node *, int); -+int html_setfont(struct html *, enum mandoc_esc); ---- a/lib.in -+++ b/lib.in -@@ -43,7 +43,7 @@ LINE("libcipher", "FreeSec Crypt Library - LINE("libcompat", "Compatibility Library (libcompat, \\-lcompat)") - LINE("libcrypt", "Crypt Library (libcrypt, \\-lcrypt)") - LINE("libcurses", "Curses Library (libcurses, \\-lcurses)") --LINE("libcuse", "Userland Character Device Library (libcuse, \\-lcuse)") -+LINE("libcuse", "Userland Character Device Library (libcuse, \\-lcuse)") - LINE("libdevattr", "Device attribute and event library (libdevattr, \\-ldevattr)") - LINE("libdevctl", "Device Control Library (libdevctl, \\-ldevctl)") - LINE("libdevinfo", "Device and Resource Information Utility Library (libdevinfo, \\-ldevinfo)") ---- a/libmandoc.h -+++ b/libmandoc.h -@@ -1,7 +1,7 @@ - /* $Id: libmandoc.h,v 1.77 2018/12/21 17:15:18 schwarze Exp $ */ - /* - * Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons -- * Copyright (c) 2013,2014,2015,2017,2018 Ingo Schwarze -+ * Copyright (c) 2013-2015,2017,2018,2020 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 -@@ -47,8 +47,9 @@ struct buf { - - struct roff; - struct roff_man; -+struct roff_node; - --char *mandoc_normdate(struct roff_man *, char *, int, int); -+char *mandoc_normdate(struct roff_node *, struct roff_node *); - int mandoc_eos(const char *, size_t); - int mandoc_strntoi(const char *, size_t, int); - const char *mandoc_a2msec(const char*); ---- a/main.c -+++ b/main.c -@@ -1,7 +1,7 @@ - /* $Id: main.c,v 1.322 2019/03/06 10:18:58 schwarze Exp $ */ - /* - * Copyright (c) 2008-2012 Kristaps Dzonsons -- * Copyright (c) 2010-2012, 2014-2019 Ingo Schwarze -+ * Copyright (c) 2010-2012, 2014-2020 Ingo Schwarze - * Copyright (c) 2010 Joerg Sonnenberger - * - * Permission to use, copy, modify, and distribute this software for any -@@ -21,6 +21,7 @@ - #include - #include - #include /* MACHINE */ -+#include - #include - - #include -@@ -31,6 +32,7 @@ - #include - #include - #include -+#include - #if HAVE_SANDBOX_INIT - #include - #endif -@@ -76,13 +78,12 @@ enum outt { - OUTT_PDF /* -Tpdf */ - }; - --struct curparse { -- struct mparse *mp; -- struct manoutput *outopts; /* output options */ -+struct outstate { -+ struct tag_files *tag_files; /* Tagging state variables. */ - void *outdata; /* data for output */ -- char *os_s; /* operating system for display */ -+ int use_pager; - int wstop; /* stop after a file with a warning */ -- enum mandoc_os os_e; /* check base system conventions */ -+ int had_output; /* Some output was generated. */ - enum outt outtype; /* which output to use */ - }; - -@@ -95,17 +96,18 @@ static int fs_lookup(const struct man - const char *, const char *, - struct manpage **, size_t *); - static int fs_search(const struct mansearch *, -- const struct manpaths *, int, char**, -+ const struct manpaths *, const char *, - struct manpage **, size_t *); --static int koptions(int *, char *); --static void moptions(int *, char *); --static void outdata_alloc(struct curparse *); --static void parse(struct curparse *, int, const char *); --static void passthrough(const char *, int, int); -+static void outdata_alloc(struct outstate *, struct manoutput *); -+static void parse(struct mparse *, int, const char *, -+ struct outstate *, struct manoutput *); -+static void passthrough(int, int); -+static void process_onefile(struct mparse *, struct manpage *, -+ int, struct outstate *, struct manconf *); -+static void run_pager(struct tag_files *); - static pid_t spawn_pager(struct tag_files *); --static int toptions(struct curparse *, char *); - static void usage(enum argmode) __attribute__((__noreturn__)); --static int woptions(struct curparse *, char *); -+static int woptions(char *, enum mandoc_os *, int *); - - static const int sec_prios[] = {1, 4, 5, 8, 6, 3, 7, 2, 9}; - static char help_arg[] = "help"; -@@ -115,26 +117,31 @@ static char *help_argv[] = {help_arg, - int - main(int argc, char *argv[]) - { -- struct manconf conf; -- struct mansearch search; -- struct curparse curp; -- struct winsize ws; -- struct tag_files *tag_files; -- struct manpage *res, *resp; -- const char *progname, *sec, *thisarg; -- char *conf_file, *defpaths, *auxpaths; -- char *oarg, *tagarg; -+ struct manconf conf; /* Manpaths and output options. */ -+ struct outstate outst; /* Output state. */ -+ struct winsize ws; /* Result of ioctl(TIOCGWINSZ). */ -+ struct mansearch search; /* Search options. */ -+ struct manpage *res; /* Complete list of search results. */ -+ struct manpage *resn; /* Search results for one name. */ -+ struct mparse *mp; /* Opaque parser object. */ -+ const char *conf_file; /* -C: alternate config file. */ -+ const char *os_s; /* -I: Operating system for display. */ -+ const char *progname, *sec; -+ char *defpaths; /* -M: override manpaths. */ -+ char *auxpaths; /* -m: additional manpaths. */ -+ char *oarg; /* -O: output option string. */ -+ char *tagarg; /* -O tag: default value. */ - unsigned char *uc; -- size_t i, sz; -+ size_t ressz; /* Number of elements in res[]. */ -+ size_t resnsz; /* Number of elements in resn[]. */ -+ size_t i, ib, ssz; -+ int options; /* Parser options. */ -+ int show_usage; /* Invalid argument: give up. */ - int prio, best_prio; -- enum outmode outmode; -- int fd, startdir; -- int show_usage; -- int options; -- int use_pager; -- int status, signum; -+ int startdir; - int c; -- pid_t pager_pid, tc_pgid, man_pgid, pid; -+ enum mandoc_os os_e; /* Check base system conventions. */ -+ enum outmode outmode; /* According to command line. */ - - #if HAVE_PROGNAME - progname = getprogname(); -@@ -154,10 +161,11 @@ main(int argc, char *argv[]) - return mandocdb(argc, argv); - - #if HAVE_PLEDGE -- if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) -- err((int)MANDOCLEVEL_SYSERR, "pledge"); -+ if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) { -+ mandoc_msg(MANDOCERR_PLEDGE, 0, 0, "%s", strerror(errno)); -+ return mandoc_msg_getrc(); -+ } - #endif -- - #if HAVE_SANDBOX_INIT - if (sandbox_init(kSBXProfileNoInternet, SANDBOX_NAMED, NULL) == -1) - errx((int)MANDOCLEVEL_SYSERR, "sandbox_init"); -@@ -166,8 +174,8 @@ main(int argc, char *argv[]) - /* Search options. */ - - memset(&conf, 0, sizeof(conf)); -- conf_file = defpaths = NULL; -- auxpaths = NULL; -+ conf_file = NULL; -+ defpaths = auxpaths = NULL; - - memset(&search, 0, sizeof(struct mansearch)); - search.outkey = "Nd"; -@@ -184,15 +192,19 @@ main(int argc, char *argv[]) - else - search.argmode = ARG_FILE; - -- /* Parser and formatter options. */ -+ /* Parser options. */ - -- memset(&curp, 0, sizeof(struct curparse)); -- curp.outtype = OUTT_LOCALE; -- curp.outopts = &conf.output; - options = MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1; -+ os_e = MANDOC_OS_OTHER; -+ os_s = NULL; -+ -+ /* Formatter options. */ -+ -+ memset(&outst, 0, sizeof(outst)); -+ outst.tag_files = NULL; -+ outst.outtype = OUTT_LOCALE; -+ outst.use_pager = 1; - -- use_pager = 1; -- tag_files = NULL; - show_usage = 0; - outmode = OUTMODE_DEF; - -@@ -210,30 +222,40 @@ main(int argc, char *argv[]) - conf_file = optarg; - break; - case 'c': -- use_pager = 0; -+ outst.use_pager = 0; - break; - case 'f': - search.argmode = ARG_WORD; - break; - case 'h': - conf.output.synopsisonly = 1; -- use_pager = 0; -+ outst.use_pager = 0; - outmode = OUTMODE_ALL; - break; - case 'I': -- if (strncmp(optarg, "os=", 3)) { -- warnx("-I %s: Bad argument", optarg); -- return (int)MANDOCLEVEL_BADARG; -+ if (strncmp(optarg, "os=", 3) != 0) { -+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, -+ "-I %s", optarg); -+ return mandoc_msg_getrc(); - } -- if (curp.os_s != NULL) { -- warnx("-I %s: Duplicate argument", optarg); -- return (int)MANDOCLEVEL_BADARG; -+ if (os_s != NULL) { -+ mandoc_msg(MANDOCERR_BADARG_DUPE, 0, 0, -+ "-I %s", optarg); -+ return mandoc_msg_getrc(); - } -- curp.os_s = mandoc_strdup(optarg + 3); -+ os_s = optarg + 3; - break; - case 'K': -- if ( ! koptions(&options, optarg)) -- return (int)MANDOCLEVEL_BADARG; -+ options &= ~(MPARSE_UTF8 | MPARSE_LATIN1); -+ if (strcmp(optarg, "utf-8") == 0) -+ options |= MPARSE_UTF8; -+ else if (strcmp(optarg, "iso-8859-1") == 0) -+ options |= MPARSE_LATIN1; -+ else if (strcmp(optarg, "us-ascii") != 0) { -+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, -+ "-K %s", optarg); -+ return mandoc_msg_getrc(); -+ } - break; - case 'k': - search.argmode = ARG_EXPR; -@@ -258,12 +280,37 @@ main(int argc, char *argv[]) - search.sec = optarg; - break; - case 'T': -- if ( ! toptions(&curp, optarg)) -- return (int)MANDOCLEVEL_BADARG; -+ if (strcmp(optarg, "ascii") == 0) -+ outst.outtype = OUTT_ASCII; -+ else if (strcmp(optarg, "lint") == 0) { -+ outst.outtype = OUTT_LINT; -+ mandoc_msg_setoutfile(stdout); -+ mandoc_msg_setmin(MANDOCERR_BASE); -+ } else if (strcmp(optarg, "tree") == 0) -+ outst.outtype = OUTT_TREE; -+ else if (strcmp(optarg, "man") == 0) -+ outst.outtype = OUTT_MAN; -+ else if (strcmp(optarg, "html") == 0) -+ outst.outtype = OUTT_HTML; -+ else if (strcmp(optarg, "markdown") == 0) -+ outst.outtype = OUTT_MARKDOWN; -+ else if (strcmp(optarg, "utf8") == 0) -+ outst.outtype = OUTT_UTF8; -+ else if (strcmp(optarg, "locale") == 0) -+ outst.outtype = OUTT_LOCALE; -+ else if (strcmp(optarg, "ps") == 0) -+ outst.outtype = OUTT_PS; -+ else if (strcmp(optarg, "pdf") == 0) -+ outst.outtype = OUTT_PDF; -+ else { -+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, -+ "-T %s", optarg); -+ return mandoc_msg_getrc(); -+ } - break; - case 'W': -- if ( ! woptions(&curp, optarg)) -- return (int)MANDOCLEVEL_BADARG; -+ if (woptions(optarg, &os_e, &outst.wstop) == -1) -+ return mandoc_msg_getrc(); - break; - case 'w': - outmode = OUTMODE_FLN; -@@ -279,11 +326,12 @@ main(int argc, char *argv[]) - - /* Postprocess options. */ - -- if (outmode == OUTMODE_DEF) { -+ switch (outmode) { -+ case OUTMODE_DEF: - switch (search.argmode) { - case ARG_FILE: - outmode = OUTMODE_ALL; -- use_pager = 0; -+ outst.use_pager = 0; - break; - case ARG_NAME: - outmode = OUTMODE_ONE; -@@ -292,6 +340,16 @@ main(int argc, char *argv[]) - outmode = OUTMODE_LST; - break; - } -+ break; -+ case OUTMODE_FLN: -+ if (search.argmode == ARG_FILE) -+ outmode = OUTMODE_ALL; -+ break; -+ case OUTMODE_ALL: -+ break; -+ case OUTMODE_LST: -+ case OUTMODE_ONE: -+ abort(); - } - - if (oarg != NULL) { -@@ -299,25 +357,22 @@ main(int argc, char *argv[]) - search.outkey = oarg; - else { - while (oarg != NULL) { -- thisarg = oarg; - if (manconf_output(&conf.output, -- strsep(&oarg, ","), 0) == 0) -- continue; -- warnx("-O %s: Bad argument", thisarg); -- return (int)MANDOCLEVEL_BADARG; -+ strsep(&oarg, ","), 0) == -1) -+ return mandoc_msg_getrc(); - } - } - } - -- if (curp.outtype != OUTT_TREE || !curp.outopts->noval) -+ if (outst.outtype != OUTT_TREE || conf.output.noval == 0) - options |= MPARSE_VALIDATE; - - if (outmode == OUTMODE_FLN || - outmode == OUTMODE_LST || - !isatty(STDOUT_FILENO)) -- use_pager = 0; -+ outst.use_pager = 0; - -- if (use_pager && -+ if (outst.use_pager && - (conf.output.width == 0 || conf.output.indent == 0) && - ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1 && - ws.ws_col > 1) { -@@ -328,9 +383,13 @@ main(int argc, char *argv[]) - } - - #if HAVE_PLEDGE -- if (!use_pager) -- if (pledge("stdio rpath", NULL) == -1) -- err((int)MANDOCLEVEL_SYSERR, "pledge"); -+ if (outst.use_pager == 0) { -+ if (pledge("stdio rpath", NULL) == -1) { -+ mandoc_msg(MANDOCERR_PLEDGE, 0, 0, -+ "%s", strerror(errno)); -+ return mandoc_msg_getrc(); -+ } -+ } - #endif - - /* Parse arguments. */ -@@ -339,11 +398,10 @@ main(int argc, char *argv[]) - argc -= optind; - argv += optind; - } -- resp = NULL; - - /* -- * Quirks for help(1) -- * and for a man(1) section argument without -s. -+ * Quirks for help(1) and man(1), -+ * in particular for a section argument without -s. - */ - - if (search.argmode == ARG_NAME) { -@@ -355,7 +413,7 @@ main(int argc, char *argv[]) - } else if (argc > 1 && - ((uc = (unsigned char *)argv[0]) != NULL) && - ((isdigit(uc[0]) && (uc[1] == '\0' || -- (isalpha(uc[1]) && uc[2] == '\0'))) || -+ isalpha(uc[1]))) || - (uc[0] == 'n' && uc[1] == '\0'))) { - search.sec = (char *)uc; - argv++; -@@ -367,6 +425,8 @@ main(int argc, char *argv[]) - if (search.arch == NULL) - search.arch = MACHINE; - #endif -+ if (outmode == OUTMODE_ONE) -+ search.firstmatch = 1; - } - - /* -@@ -380,128 +440,155 @@ main(int argc, char *argv[]) - conf.output.tag = tagarg == NULL ? *argv : tagarg + 1; - } - -- /* man(1), whatis(1), apropos(1) */ -- -- if (search.argmode != ARG_FILE) { -- if (search.argmode == ARG_NAME && -- outmode == OUTMODE_ONE) -- search.firstmatch = 1; -- -- /* Access the mandoc database. */ -+ /* Read the configuration file. */ - -+ if (search.argmode != ARG_FILE) - manconf_parse(&conf, conf_file, defpaths, auxpaths); -- if ( ! mansearch(&search, &conf.manpath, -- argc, argv, &res, &sz)) -- usage(search.argmode); - -- if (sz == 0 && search.argmode == ARG_NAME) -- fs_search(&search, &conf.manpath, -- argc, argv, &res, &sz); -- -- if (search.argmode == ARG_NAME) { -- for (c = 0; c < argc; c++) { -- if (strchr(argv[c], '/') == NULL) -- continue; -- if (access(argv[c], R_OK) == -1) { -- warn("%s", argv[c]); -+ /* man(1): Resolve each name individually. */ -+ -+ if (search.argmode == ARG_NAME) { -+ if (argc < 1) { -+ if (outmode != OUTMODE_FLN) -+ usage(ARG_NAME); -+ if (conf.manpath.sz == 0) { -+ warnx("The manpath is empty."); -+ mandoc_msg_setrc(MANDOCLEVEL_BADARG); -+ } else { -+ for (i = 0; i + 1 < conf.manpath.sz; i++) -+ printf("%s:", conf.manpath.paths[i]); -+ printf("%s\n", conf.manpath.paths[i]); -+ } -+ manconf_free(&conf); -+ return (int)mandoc_msg_getrc(); -+ } -+ for (res = NULL, ressz = 0; argc > 0; argc--, argv++) { -+ (void)mansearch(&search, &conf.manpath, -+ 1, argv, &resn, &resnsz); -+ if (resnsz == 0) -+ (void)fs_search(&search, &conf.manpath, -+ *argv, &resn, &resnsz); -+ if (resnsz == 0 && strchr(*argv, '/') == NULL) { -+ if (search.arch != NULL && -+ arch_valid(search.arch, OSENUM) == 0) -+ warnx("Unknown architecture \"%s\".", -+ search.arch); -+ else if (search.sec != NULL) -+ warnx("No entry for %s in " -+ "section %s of the manual.", -+ *argv, search.sec); -+ else -+ warnx("No entry for %s in " -+ "the manual.", *argv); -+ mandoc_msg_setrc(MANDOCLEVEL_BADARG); -+ continue; -+ } -+ if (resnsz == 0) { -+ if (access(*argv, R_OK) == -1) { -+ mandoc_msg_setinfilename(*argv); -+ mandoc_msg(MANDOCERR_BADARG_BAD, -+ 0, 0, "%s", strerror(errno)); -+ mandoc_msg_setinfilename(NULL); - continue; - } -+ resnsz = 1; -+ resn = mandoc_calloc(resnsz, sizeof(*res)); -+ resn->file = mandoc_strdup(*argv); -+ resn->ipath = SIZE_MAX; -+ resn->form = FORM_SRC; -+ } -+ if (outmode != OUTMODE_ONE || resnsz == 1) { - res = mandoc_reallocarray(res, -- sz + 1, sizeof(*res)); -- res[sz].file = mandoc_strdup(argv[c]); -- res[sz].names = NULL; -- res[sz].output = NULL; -- res[sz].ipath = SIZE_MAX; -- res[sz].sec = 10; -- res[sz].form = FORM_SRC; -- sz++; -+ ressz + resnsz, sizeof(*res)); -+ memcpy(res + ressz, resn, -+ sizeof(*resn) * resnsz); -+ ressz += resnsz; -+ continue; - } -- } - -- if (sz == 0) { -- if (search.argmode != ARG_NAME) -- warnx("nothing appropriate"); -- mandoc_msg_setrc(MANDOCLEVEL_BADARG); -- goto out; -- } -+ /* Search for the best section. */ - -- /* -- * 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 = 20; -- } else if (outmode == OUTMODE_ALL) -- argc = (int)sz; -- -- /* Iterate all matching manuals. */ -- -- resp = res; -- 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. */ -- sec = res[i].file; -+ best_prio = 40; -+ for (ib = i = 0; i < resnsz; i++) { -+ sec = resn[i].file; - sec += strcspn(sec, "123456789"); - if (sec[0] == '\0') -- continue; -+ continue; /* No section at all. */ - prio = sec_prios[sec[0] - '1']; -- if (sec[1] != '/') -- prio += 10; -+ if (search.sec != NULL) { -+ ssz = strlen(search.sec); -+ if (strncmp(sec, search.sec, ssz) == 0) -+ sec += ssz; -+ } else -+ sec++; /* Prefer without suffix. */ -+ if (*sec != '/') -+ prio += 10; /* Wrong dir name. */ -+ if (search.sec != NULL && -+ (strlen(sec) <= ssz + 3 || -+ strcmp(sec + strlen(sec) - ssz, -+ search.sec) != 0)) -+ prio += 20; /* Wrong file ext. */ - if (prio >= best_prio) - continue; - best_prio = prio; -- resp = res + i; -+ ib = i; - } -+ res = mandoc_reallocarray(res, ressz + 1, -+ sizeof(*res)); -+ memcpy(res + ressz++, resn + ib, sizeof(*resn)); - } - -- /* -- * 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. -- */ -+ /* apropos(1), whatis(1): Process the full search expression. */ -+ -+ } else if (search.argmode != ARG_FILE) { -+ if (mansearch(&search, &conf.manpath, -+ argc, argv, &res, &ressz) == 0) -+ usage(search.argmode); - -- if (outmode == OUTMODE_FLN || outmode == OUTMODE_LST) -+ if (ressz == 0) { -+ warnx("nothing appropriate"); -+ mandoc_msg_setrc(MANDOCLEVEL_BADARG); - goto out; -- } -+ } - -- /* mandoc(1) */ -+ /* mandoc(1): Take command line arguments as file names. */ - --#if HAVE_PLEDGE -- if (use_pager) { -- if (pledge("stdio rpath tmppath tty proc exec", NULL) == -1) -- err((int)MANDOCLEVEL_SYSERR, "pledge"); - } else { -- if (pledge("stdio rpath", NULL) == -1) -- err((int)MANDOCLEVEL_SYSERR, "pledge"); -+ ressz = argc > 0 ? argc : 1; -+ res = mandoc_calloc(ressz, sizeof(*res)); -+ for (i = 0; i < ressz; i++) { -+ if (argc > 0) -+ res[i].file = mandoc_strdup(argv[i]); -+ res[i].ipath = SIZE_MAX; -+ res[i].form = FORM_SRC; -+ } - } --#endif - -- if (search.argmode == ARG_FILE) -- moptions(&options, auxpaths); -- -- mchars_alloc(); -- curp.mp = mparse_alloc(options, curp.os_e, curp.os_s); -+ switch (outmode) { -+ case OUTMODE_FLN: -+ for (i = 0; i < ressz; i++) -+ puts(res[i].file); -+ goto out; -+ case OUTMODE_LST: -+ for (i = 0; i < ressz; i++) -+ printf("%s - %s\n", res[i].names, -+ res[i].output == NULL ? "" : -+ res[i].output); -+ goto out; -+ default: -+ break; -+ } - -- if (argc < 1) { -- if (use_pager) { -- tag_files = tag_init(); -- tag_files->tagname = conf.output.tag; -- } -- thisarg = ""; -- mandoc_msg_setinfilename(thisarg); -- parse(&curp, STDIN_FILENO, thisarg); -- mandoc_msg_setinfilename(NULL); -+ if (search.argmode == ARG_FILE && auxpaths != NULL) { -+ if (strcmp(auxpaths, "doc") == 0) -+ options |= MPARSE_MDOC; -+ else if (strcmp(auxpaths, "an") == 0) -+ options |= MPARSE_MAN; - } - -+ mchars_alloc(); -+ mp = mparse_alloc(options, os_e, os_s); -+ - /* - * Remember the original working directory, if possible. - * This will be needed if some names on the command line -@@ -511,166 +598,57 @@ main(int argc, char *argv[]) - */ - startdir = open(".", O_RDONLY | O_DIRECTORY); - -- while (argc > 0) { -- -- /* -- * Changing directories is not needed in ARG_FILE mode. -- * Do it on a best-effort basis. Even in case of -- * failure, some functionality may still work. -- */ -- if (resp != NULL) { -- if (resp->ipath != SIZE_MAX) -- (void)chdir(conf.manpath.paths[resp->ipath]); -- else if (startdir != -1) -- (void)fchdir(startdir); -- thisarg = resp->file; -- } else -- thisarg = *argv; -- -- fd = mparse_open(curp.mp, thisarg); -- if (fd != -1) { -- if (use_pager) { -- use_pager = 0; -- tag_files = tag_init(); -- tag_files->tagname = conf.output.tag; -- } -- -- mandoc_msg_setinfilename(thisarg); -- if (resp == NULL || resp->form == FORM_SRC) -- parse(&curp, fd, thisarg); -- else -- passthrough(resp->file, fd, -- conf.output.synopsisonly); -- mandoc_msg_setinfilename(NULL); -- -- if (ferror(stdout)) { -- if (tag_files != NULL) { -- warn("%s", tag_files->ofn); -- tag_unlink(); -- tag_files = NULL; -- } else -- warn("stdout"); -- mandoc_msg_setrc(MANDOCLEVEL_SYSERR); -- break; -- } -- -- if (argc > 1 && curp.outtype <= OUTT_UTF8) { -- if (curp.outdata == NULL) -- outdata_alloc(&curp); -- terminal_sepline(curp.outdata); -- } -- } else -- mandoc_msg(MANDOCERR_FILE, 0, 0, -- "%s: %s", thisarg, strerror(errno)); -- -- if (curp.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) -+ for (i = 0; i < ressz; i++) { -+ process_onefile(mp, res + i, startdir, &outst, &conf); -+ if (outst.wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) - break; -- -- if (resp != NULL) -- resp++; -- else -- argv++; -- if (--argc) -- mparse_reset(curp.mp); - } - if (startdir != -1) { - (void)fchdir(startdir); - close(startdir); - } - -- if (curp.outdata != NULL) { -- switch (curp.outtype) { -+ if (outst.outdata != NULL) { -+ switch (outst.outtype) { - case OUTT_HTML: -- html_free(curp.outdata); -+ html_free(outst.outdata); - break; - case OUTT_UTF8: - case OUTT_LOCALE: - case OUTT_ASCII: -- ascii_free(curp.outdata); -+ ascii_free(outst.outdata); - break; - case OUTT_PDF: - case OUTT_PS: -- pspdf_free(curp.outdata); -+ pspdf_free(outst.outdata); - break; - default: - break; - } - } - mandoc_xr_free(); -- mparse_free(curp.mp); -+ mparse_free(mp); - mchars_free(); - - out: -- if (search.argmode != ARG_FILE) { -+ mansearch_free(res, ressz); -+ if (search.argmode != ARG_FILE) - manconf_free(&conf); -- mansearch_free(res, sz); -- } - -- free(curp.os_s); -- -- /* -- * When using a pager, finish writing both temporary files, -- * fork it, wait for the user to close it, and clean up. -- */ -- -- if (tag_files != NULL) { -+ if (outst.tag_files != NULL) { - fclose(stdout); - tag_write(); -- man_pgid = getpgid(0); -- tag_files->tcpgid = man_pgid == getpid() ? -- getpgid(getppid()) : man_pgid; -- pager_pid = 0; -- signum = SIGSTOP; -- for (;;) { -- -- /* Stop here until moved to the foreground. */ -- -- tc_pgid = tcgetpgrp(tag_files->ofd); -- if (tc_pgid != man_pgid) { -- if (tc_pgid == pager_pid) { -- (void)tcsetpgrp(tag_files->ofd, -- man_pgid); -- if (signum == SIGTTIN) -- continue; -- } else -- tag_files->tcpgid = tc_pgid; -- kill(0, signum); -- continue; -- } -- -- /* Once in the foreground, activate the pager. */ -- -- if (pager_pid) { -- (void)tcsetpgrp(tag_files->ofd, pager_pid); -- kill(pager_pid, SIGCONT); -- } else -- pager_pid = spawn_pager(tag_files); -- -- /* Wait for the pager to stop or exit. */ -- -- while ((pid = waitpid(pager_pid, &status, -- WUNTRACED)) == -1 && errno == EINTR) -- continue; -- -- if (pid == -1) { -- warn("wait"); -- mandoc_msg_setrc(MANDOCLEVEL_SYSERR); -- break; -- } -- if (!WIFSTOPPED(status)) -- break; -- -- signum = WSTOPSIG(status); -- } -+ run_pager(outst.tag_files); - tag_unlink(); -- } -+ } else if (outst.had_output && outst.outtype != OUTT_LINT) -+ mandoc_msg_summary(); -+ - return (int)mandoc_msg_getrc(); - } - - static void - usage(enum argmode argmode) - { -- - switch (argmode) { - case ARG_FILE: - fputs("usage: mandoc [-ac] [-I os=name] " -@@ -701,6 +679,7 @@ fs_lookup(const struct manpaths *paths, - const char *sec, const char *arch, const char *name, - struct manpage **res, size_t *ressz) - { -+ struct stat sb; - glob_t globinfo; - struct manpage *page; - char *file; -@@ -710,13 +689,13 @@ fs_lookup(const struct manpaths *paths, - form = FORM_SRC; - mandoc_asprintf(&file, "%s/man%s/%s.%s", - paths->paths[ipath], sec, name, sec); -- if (access(file, R_OK) != -1) -+ if (stat(file, &sb) != -1) - goto found; - free(file); - - mandoc_asprintf(&file, "%s/cat%s/%s.0", - paths->paths[ipath], sec, name); -- if (access(file, R_OK) != -1) { -+ if (stat(file, &sb) != -1) { - form = FORM_CAT; - goto found; - } -@@ -725,7 +704,7 @@ fs_lookup(const struct manpaths *paths, - if (arch != NULL) { - mandoc_asprintf(&file, "%s/man%s/%s/%s.%s", - paths->paths[ipath], sec, arch, name, sec); -- if (access(file, R_OK) != -1) -+ if (stat(file, &sb) != -1) - goto found; - free(file); - } -@@ -734,98 +713,150 @@ fs_lookup(const struct manpaths *paths, - paths->paths[ipath], sec, name); - globres = glob(file, 0, NULL, &globinfo); - if (globres != 0 && globres != GLOB_NOMATCH) -- warn("%s: glob", file); -+ mandoc_msg(MANDOCERR_GLOB, 0, 0, -+ "%s: %s", file, strerror(errno)); - free(file); - if (globres == 0) - file = mandoc_strdup(*globinfo.gl_pathv); - globfree(&globinfo); -- if (globres == 0) -- goto found; -+ if (globres == 0) { -+ if (stat(file, &sb) != -1) -+ goto found; -+ free(file); -+ } - if (res != NULL || ipath + 1 != paths->sz) -- return 0; -+ return -1; - - mandoc_asprintf(&file, "%s.%s", name, sec); -- globres = access(file, R_OK); -+ globres = stat(file, &sb); - free(file); -- return globres != -1; -+ return globres; - - found: - warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s", - name, sec, BINM_MAKEWHATIS, paths->paths[ipath]); - if (res == NULL) { - free(file); -- return 1; -+ return 0; - } -- *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage)); -+ *res = mandoc_reallocarray(*res, ++*ressz, sizeof(**res)); - page = *res + (*ressz - 1); - page->file = file; - page->names = NULL; - page->output = NULL; -+ page->bits = NAME_FILE & NAME_MASK; - page->ipath = ipath; - page->sec = (*sec >= '1' && *sec <= '9') ? *sec - '1' + 1 : 10; - page->form = form; -- return 1; -+ return 0; - } - - static int - fs_search(const struct mansearch *cfg, const struct manpaths *paths, -- int argc, char **argv, struct manpage **res, size_t *ressz) -+ const char *name, struct manpage **res, size_t *ressz) - { - const char *const sections[] = - {"1", "8", "6", "2", "3", "5", "7", "4", "9", "3p"}; - const size_t nsec = sizeof(sections)/sizeof(sections[0]); - -- size_t ipath, isec, lastsz; -+ size_t ipath, isec; - - assert(cfg->argmode == ARG_NAME); -- - if (res != NULL) - *res = NULL; -- *ressz = lastsz = 0; -- while (argc) { -- for (ipath = 0; ipath < paths->sz; ipath++) { -- if (cfg->sec != NULL) { -- if (fs_lookup(paths, ipath, cfg->sec, -- cfg->arch, *argv, res, ressz) && -- cfg->firstmatch) -- return 1; -- } else for (isec = 0; isec < nsec; isec++) -+ *ressz = 0; -+ for (ipath = 0; ipath < paths->sz; ipath++) { -+ if (cfg->sec != NULL) { -+ if (fs_lookup(paths, ipath, cfg->sec, cfg->arch, -+ name, res, ressz) != -1 && cfg->firstmatch) -+ return 0; -+ } else { -+ for (isec = 0; isec < nsec; isec++) - if (fs_lookup(paths, ipath, sections[isec], -- cfg->arch, *argv, res, ressz) && -+ cfg->arch, name, res, ressz) != -1 && - cfg->firstmatch) -- return 1; -+ return 0; - } -- if (res != NULL && *ressz == lastsz && -- strchr(*argv, '/') == NULL) { -- if (cfg->arch != NULL && -- arch_valid(cfg->arch, OSENUM) == 0) -- warnx("Unknown architecture \"%s\".", -- cfg->arch); -- else if (cfg->sec == NULL) -- warnx("No entry for %s in the manual.", -- *argv); -- else -- warnx("No entry for %s in section %s " -- "of the manual.", *argv, cfg->sec); -- } -- lastsz = *ressz; -- argv++; -- argc--; - } -- return 0; -+ return -1; - } - - static void --parse(struct curparse *curp, int fd, const char *file) -+process_onefile(struct mparse *mp, struct manpage *resp, int startdir, -+ struct outstate *outst, struct manconf *conf) - { -- struct roff_meta *meta; -+ int fd; -+ -+ /* -+ * Changing directories is not needed in ARG_FILE mode. -+ * Do it on a best-effort basis. Even in case of -+ * failure, some functionality may still work. -+ */ -+ if (resp->ipath != SIZE_MAX) -+ (void)chdir(conf->manpath.paths[resp->ipath]); -+ else if (startdir != -1) -+ (void)fchdir(startdir); -+ -+ mandoc_msg_setinfilename(resp->file); -+ if (resp->file != NULL) { -+ if ((fd = mparse_open(mp, resp->file)) == -1) { -+ mandoc_msg(resp->ipath == SIZE_MAX ? -+ MANDOCERR_BADARG_BAD : MANDOCERR_OPEN, -+ 0, 0, "%s", strerror(errno)); -+ mandoc_msg_setinfilename(NULL); -+ return; -+ } -+ } else -+ fd = STDIN_FILENO; -+ -+ if (outst->use_pager) { -+ outst->use_pager = 0; -+ outst->tag_files = tag_init(conf->output.tag); -+ } -+ -+ if (outst->had_output && outst->outtype <= OUTT_UTF8) { -+ if (outst->outdata == NULL) -+ outdata_alloc(outst, &conf->output); -+ terminal_sepline(outst->outdata); -+ } -+ -+ if (resp->form == FORM_SRC) -+ parse(mp, fd, resp->file, outst, &conf->output); -+ else { -+ passthrough(fd, conf->output.synopsisonly); -+ outst->had_output = 1; -+ } - -- /* Begin by parsing the file itself. */ -+ if (ferror(stdout)) { -+ if (outst->tag_files != NULL) { -+ mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s: %s", -+ outst->tag_files->ofn, strerror(errno)); -+ tag_unlink(); -+ outst->tag_files = NULL; -+ } else -+ mandoc_msg(MANDOCERR_WRITE, 0, 0, "%s", -+ strerror(errno)); -+ } -+ mandoc_msg_setinfilename(NULL); -+} -+ -+static void -+parse(struct mparse *mp, int fd, const char *file, -+ struct outstate *outst, struct manoutput *outconf) -+{ -+ static int previous; -+ struct roff_meta *meta; - -- assert(file); - assert(fd >= 0); -+ if (file == NULL) -+ file = ""; -+ -+ if (previous) -+ mparse_reset(mp); -+ else -+ previous = 1; - -- mparse_readfd(curp->mp, fd, file); -+ mparse_readfd(mp, fd, file); - if (fd != STDIN_FILENO) - close(fd); - -@@ -834,61 +865,62 @@ parse(struct curparse *curp, int fd, con - * level, do not produce output. - */ - -- if (curp->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) -+ if (outst->wstop && mandoc_msg_getrc() != MANDOCLEVEL_OK) - return; - -- if (curp->outdata == NULL) -- outdata_alloc(curp); -- else if (curp->outtype == OUTT_HTML) -- html_reset(curp); -+ if (outst->outdata == NULL) -+ outdata_alloc(outst, outconf); -+ else if (outst->outtype == OUTT_HTML) -+ html_reset(outst); - - mandoc_xr_reset(); -- meta = mparse_result(curp->mp); -+ meta = mparse_result(mp); - - /* Execute the out device, if it exists. */ - -+ outst->had_output = 1; - if (meta->macroset == MACROSET_MDOC) { -- switch (curp->outtype) { -+ switch (outst->outtype) { - case OUTT_HTML: -- html_mdoc(curp->outdata, meta); -+ html_mdoc(outst->outdata, meta); - break; - case OUTT_TREE: -- tree_mdoc(curp->outdata, meta); -+ tree_mdoc(outst->outdata, meta); - break; - case OUTT_MAN: -- man_mdoc(curp->outdata, meta); -+ man_mdoc(outst->outdata, meta); - break; - case OUTT_PDF: - case OUTT_ASCII: - case OUTT_UTF8: - case OUTT_LOCALE: - case OUTT_PS: -- terminal_mdoc(curp->outdata, meta); -+ terminal_mdoc(outst->outdata, meta); - break; - case OUTT_MARKDOWN: -- markdown_mdoc(curp->outdata, meta); -+ markdown_mdoc(outst->outdata, meta); - break; - default: - break; - } - } - if (meta->macroset == MACROSET_MAN) { -- switch (curp->outtype) { -+ switch (outst->outtype) { - case OUTT_HTML: -- html_man(curp->outdata, meta); -+ html_man(outst->outdata, meta); - break; - case OUTT_TREE: -- tree_man(curp->outdata, meta); -+ tree_man(outst->outdata, meta); - break; - case OUTT_MAN: -- mparse_copy(curp->mp); -+ mparse_copy(mp); - break; - case OUTT_PDF: - case OUTT_ASCII: - case OUTT_UTF8: - case OUTT_LOCALE: - case OUTT_PS: -- terminal_man(curp->outdata, meta); -+ terminal_man(outst->outdata, meta); - break; - default: - break; -@@ -919,7 +951,7 @@ check_xr(void) - search.firstmatch = 1; - if (mansearch(&search, &paths, 1, &xr->name, NULL, &sz)) - continue; -- if (fs_search(&search, &paths, 1, &xr->name, NULL, &sz)) -+ if (fs_search(&search, &paths, xr->name, NULL, &sz) != -1) - continue; - if (xr->count == 1) - mandoc_msg(MANDOCERR_XR_BAD, xr->line, -@@ -932,26 +964,26 @@ check_xr(void) - } - - static void --outdata_alloc(struct curparse *curp) -+outdata_alloc(struct outstate *outst, struct manoutput *outconf) - { -- switch (curp->outtype) { -+ switch (outst->outtype) { - case OUTT_HTML: -- curp->outdata = html_alloc(curp->outopts); -+ outst->outdata = html_alloc(outconf); - break; - case OUTT_UTF8: -- curp->outdata = utf8_alloc(curp->outopts); -+ outst->outdata = utf8_alloc(outconf); - break; - case OUTT_LOCALE: -- curp->outdata = locale_alloc(curp->outopts); -+ outst->outdata = locale_alloc(outconf); - break; - case OUTT_ASCII: -- curp->outdata = ascii_alloc(curp->outopts); -+ outst->outdata = ascii_alloc(outconf); - break; - case OUTT_PDF: -- curp->outdata = pdf_alloc(curp->outopts); -+ outst->outdata = pdf_alloc(outconf); - break; - case OUTT_PS: -- curp->outdata = ps_alloc(curp->outopts); -+ outst->outdata = ps_alloc(outconf); - break; - default: - break; -@@ -959,34 +991,34 @@ outdata_alloc(struct curparse *curp) - } - - static void --passthrough(const char *file, int fd, int synopsis_only) -+passthrough(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, *cp; - size_t linesz; - ssize_t len, written; -- int print; -+ int lno, print; - -+ stream = NULL; - line = NULL; - linesz = 0; - - if (fflush(stdout) == EOF) { -- syscall = "fflush"; -- goto fail; -+ mandoc_msg(MANDOCERR_FFLUSH, 0, 0, "%s", strerror(errno)); -+ goto done; - } -- - if ((stream = fdopen(fd, "r")) == NULL) { - close(fd); -- syscall = "fdopen"; -- goto fail; -+ mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno)); -+ goto done; - } - -- print = 0; -+ lno = print = 0; - while ((len = getline(&line, &linesz, stream)) != -1) { -+ lno++; - cp = line; - if (synopsis_only) { - if (print) { -@@ -1004,98 +1036,24 @@ passthrough(const char *file, int fd, in - } - } - for (; len > 0; len -= written) { -- if ((written = write(STDOUT_FILENO, cp, len)) != -1) -- continue; -- fclose(stream); -- syscall = "write"; -- goto fail; -+ if ((written = write(STDOUT_FILENO, cp, len)) == -1) { -+ mandoc_msg(MANDOCERR_WRITE, 0, 0, -+ "%s", strerror(errno)); -+ goto done; -+ } - } - } -- -- if (ferror(stream)) { -- fclose(stream); -- syscall = "getline"; -- goto fail; -- } -+ if (ferror(stream)) -+ mandoc_msg(MANDOCERR_GETLINE, lno, 0, "%s", strerror(errno)); - - done: - free(line); -- fclose(stream); -- return; -- --fail: -- free(line); -- warn("%s: SYSERR: %s", file, syscall); -- mandoc_msg_setrc(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 { -- warnx("-K %s: Bad argument", arg); -- return 0; -- } -- return 1; --} -- --static void --moptions(int *options, char *arg) --{ -- -- if (arg == NULL) -- return; -- if (strcmp(arg, "doc") == 0) -- *options |= MPARSE_MDOC; -- else if (strcmp(arg, "an") == 0) -- *options |= MPARSE_MAN; --} -- --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; -- mandoc_msg_setoutfile(stdout); -- mandoc_msg_setmin(MANDOCERR_BASE); -- } 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, "markdown")) -- curp->outtype = OUTT_MARKDOWN; -- 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, "ps")) -- curp->outtype = OUTT_PS; -- else if (0 == strcmp(arg, "pdf")) -- curp->outtype = OUTT_PDF; -- else { -- warnx("-T %s: Bad argument", arg); -- return 0; -- } -- -- return 1; -+ if (stream != NULL) -+ fclose(stream); - } - - static int --woptions(struct curparse *curp, char *arg) -+woptions(char *arg, enum mandoc_os *os_e, int *wstop) - { - char *v, *o; - const char *toks[11]; -@@ -1116,7 +1074,7 @@ woptions(struct curparse *curp, char *ar - o = arg; - switch (getsubopt(&arg, (char * const *)toks, &v)) { - case 0: -- curp->wstop = 1; -+ *wstop = 1; - break; - case 1: - case 2: -@@ -1135,22 +1093,80 @@ woptions(struct curparse *curp, char *ar - mandoc_msg_setmin(MANDOCERR_UNSUPP); - break; - case 7: -- mandoc_msg_setmin(MANDOCERR_MAX); -+ mandoc_msg_setmin(MANDOCERR_BADARG); - break; - case 8: - mandoc_msg_setmin(MANDOCERR_BASE); -- curp->os_e = MANDOC_OS_OPENBSD; -+ *os_e = MANDOC_OS_OPENBSD; - break; - case 9: - mandoc_msg_setmin(MANDOCERR_BASE); -- curp->os_e = MANDOC_OS_NETBSD; -+ *os_e = MANDOC_OS_NETBSD; - break; - default: -- warnx("-W %s: Bad argument", o); -- return 0; -+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-W %s", o); -+ return -1; -+ } -+ } -+ return 0; -+} -+ -+/* -+ * Wait until moved to the foreground, -+ * then fork the pager and wait for the user to close it. -+ */ -+static void -+run_pager(struct tag_files *tag_files) -+{ -+ int signum, status; -+ pid_t man_pgid, tc_pgid; -+ pid_t pager_pid, wait_pid; -+ -+ man_pgid = getpgid(0); -+ tag_files->tcpgid = man_pgid == getpid() ? getpgid(getppid()) : -+ man_pgid; -+ pager_pid = 0; -+ signum = SIGSTOP; -+ -+ for (;;) { -+ /* Stop here until moved to the foreground. */ -+ -+ tc_pgid = tcgetpgrp(tag_files->ofd); -+ if (tc_pgid != man_pgid) { -+ if (tc_pgid == pager_pid) { -+ (void)tcsetpgrp(tag_files->ofd, man_pgid); -+ if (signum == SIGTTIN) -+ continue; -+ } else -+ tag_files->tcpgid = tc_pgid; -+ kill(0, signum); -+ continue; -+ } -+ -+ /* Once in the foreground, activate the pager. */ -+ -+ if (pager_pid) { -+ (void)tcsetpgrp(tag_files->ofd, pager_pid); -+ kill(pager_pid, SIGCONT); -+ } else -+ pager_pid = spawn_pager(tag_files); -+ -+ /* Wait for the pager to stop or exit. */ -+ -+ while ((wait_pid = waitpid(pager_pid, &status, -+ WUNTRACED)) == -1 && errno == EINTR) -+ continue; -+ -+ if (wait_pid == -1) { -+ mandoc_msg(MANDOCERR_WAIT, 0, 0, -+ "%s", strerror(errno)); -+ break; - } -+ if (!WIFSTOPPED(status)) -+ break; -+ -+ signum = WSTOPSIG(status); - } -- return 1; - } - - static pid_t -@@ -1196,7 +1212,7 @@ spawn_pager(struct tag_files *tag_files) - - use_ofn = 1; - #if HAVE_LESS_T -- if ((cmdlen = strlen(argv[0])) >= 4) { -+ if (*tag_files->tfn != '\0' && (cmdlen = strlen(argv[0])) >= 4) { - cp = argv[0] + cmdlen - 4; - if (strcmp(cp, "less") == 0) { - argv[argc++] = mandoc_strdup("-T"); -@@ -1215,15 +1231,19 @@ spawn_pager(struct tag_files *tag_files) - - switch (pager_pid = fork()) { - case -1: -- err((int)MANDOCLEVEL_SYSERR, "fork"); -+ mandoc_msg(MANDOCERR_FORK, 0, 0, "%s", strerror(errno)); -+ exit(mandoc_msg_getrc()); - case 0: - break; - default: - (void)setpgid(pager_pid, 0); - (void)tcsetpgrp(tag_files->ofd, pager_pid); - #if HAVE_PLEDGE -- if (pledge("stdio rpath tmppath tty proc", NULL) == -1) -- err((int)MANDOCLEVEL_SYSERR, "pledge"); -+ if (pledge("stdio rpath tmppath tty proc", NULL) == -1) { -+ mandoc_msg(MANDOCERR_PLEDGE, 0, 0, -+ "%s", strerror(errno)); -+ exit(mandoc_msg_getrc()); -+ } - #endif - tag_files->pager_pid = pager_pid; - return pager_pid; -@@ -1231,8 +1251,10 @@ spawn_pager(struct tag_files *tag_files) - - /* The child process becomes the pager. */ - -- if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) -- err((int)MANDOCLEVEL_SYSERR, "pager stdout"); -+ if (dup2(tag_files->ofd, STDOUT_FILENO) == -1) { -+ mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno)); -+ _exit(mandoc_msg_getrc()); -+ } - close(tag_files->ofd); - assert(tag_files->tfd == -1); - -@@ -1242,5 +1264,6 @@ spawn_pager(struct tag_files *tag_files) - nanosleep(&timeout, NULL); - - execvp(argv[0], argv); -- err((int)MANDOCLEVEL_SYSERR, "exec %s", argv[0]); -+ mandoc_msg(MANDOCERR_EXEC, 0, 0, "%s: %s", argv[0], strerror(errno)); -+ _exit(mandoc_msg_getrc()); - } ---- a/man.1 -+++ b/man.1 -@@ -3,7 +3,7 @@ - .\" 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, 2014-2018 Ingo Schwarze -+.\" Copyright (c) 2010, 2011, 2014-2020 Ingo Schwarze - .\" - .\" Redistribution and use in source and binary forms, with or without - .\" modification, are permitted provided that the following conditions -@@ -51,7 +51,7 @@ The - .Nm - utility - displays the --manual pages entitled -+manual page entitled - .Ar name . - Pages may be selected according to - a specific category -@@ -64,7 +64,6 @@ The options are as follows: - .Bl -tag -width Ds - .It Fl a - Display all matching manual pages. --Normally, only the first page found is displayed. - .It Fl C Ar file - Use the specified - .Ar file -@@ -129,31 +128,31 @@ are ignored. - This option implies - .Fl a . - .It Fl M Ar path --Override the list of standard directories which --.Nm --searches for manual pages. -+Override the list of directories to search 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 . -+This option also overrides the environment variable -+.Ev MANPATH -+and any directories specified in the -+.Xr man.conf 5 -+file. - .It Fl m Ar path --Augment the list of standard directories which --.Nm --searches for manual pages. -+Augment the list of directories to search 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 -+These directories will be searched before those specified using the - .Fl M --option or the -+option, the - .Ev MANPATH --environment variable. -+environment variable, the -+.Xr man.conf 5 -+file, or the default directories. - .It Fl S Ar subsection - Only show pages for the specified - .Xr machine 1 -@@ -197,13 +196,12 @@ System maintenance and operation command - .It 9 - Kernel internals. - .El --.Pp --If not specified and a match is found in more than one section, --the first match is selected from the following list: --1, 8, 6, 2, 3, 5, 7, 4, 9, 3p. - .It Fl w - List the pathnames of all matching manual pages instead of displaying - any of them. -+If no -+.Ar name -+is given, list the directories that would be searched. - .El - .Pp - The options -@@ -214,9 +212,23 @@ The options - .Fl fkl - are mutually exclusive and override each other. - .Pp --Guidelines for writing --man pages can be found in --.Xr mdoc 7 . -+The search starts with the -+.Fl m -+argument if provided, then continues with the -+.Fl M -+argument, the -+.Ev MANPATH -+variable, the -+.Ic manpath -+entries in the -+.Xr man.conf 5 -+file, or with -+.Pa /usr/share/man : Ns Pa /usr/X11R6/man : Ns Pa /usr/local/man -+by default. -+Within each of these, directories are searched in the order provided. -+Within each directory, the search proceeds according to the following -+list of sections: 1, 8, 6, 2, 3, 5, 7, 4, 9, 3p. -+The first match found is shown. - .Pp - The - .Xr mandoc.db 5 -@@ -236,6 +248,10 @@ The database is kept up to date with - which is run by the - .Xr weekly 8 - maintenance script. -+.Pp -+Guidelines for writing -+man pages can be found in -+.Xr mdoc 7 . - .Sh ENVIRONMENT - .Bl -tag -width MANPATHX - .It Ev MACHINE -@@ -286,15 +302,15 @@ manual opens a manual page at the defini - .Ar term - rather than at the beginning. - .It Ev MANPATH --The standard search path used by --.Nm --may be changed by specifying a path in the -+Override the standard search path which is either specified in -+.Xr man.conf 5 -+or the default path. -+The format of - .Ev MANPATH --environment variable. --The format of the path is a colon -+is a colon - .Pq Ql \&: - separated list of directories. --Invalid paths are ignored. -+Invalid directories are ignored. - Overridden by - .Fl M , - ignored if -@@ -303,12 +319,10 @@ is specified. - .Pp - 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; -+begins with a colon, it is appended to the standard path; -+if it ends with a colon, it is prepended to the standard path; - 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. -+the standard path is inserted between the colons. - .It Ev PAGER - Specifies the pagination program to use when - .Ev MANPAGER -@@ -321,7 +335,9 @@ is used. - .Sh FILES - .Bl -tag -width /etc/man.conf -compact - .It Pa /etc/man.conf --default man configuration file -+default -+.Nm -+configuration file - .El - .Sh EXIT STATUS - .Ex -std man -@@ -365,7 +381,7 @@ are extensions to that specification. - A - .Nm - command first appeared in --.At v3 . -+.At v2 . - .Pp - The - .Fl w ---- a/man.7 -+++ b/man.7 -@@ -160,7 +160,9 @@ This has no effect unless the tabulator - .Ic ta - request. - .It Ic EE --This is a non-standard GNU extension. -+This is a non-standard Version 9 -+.At -+extension later adopted by GNU. - In - .Xr mandoc 1 , - it does the same as the -@@ -168,7 +170,9 @@ it does the same as the - .Ic fi - request (switch to fill mode). - .It Ic EX --This is a non-standard GNU extension. -+This is a non-standard Version 9 -+.At -+extension later adopted by GNU. - In - .Xr mandoc 1 , - it does the same as the -@@ -496,8 +500,8 @@ The syntax is as follows: - .It Ic BI Ta n Ta current Ta \& - .It Ic BR Ta n Ta current Ta \& - .It Ic DT Ta 0 Ta current Ta \& --.It Ic EE Ta 0 Ta current Ta GNU --.It Ic EX Ta 0 Ta current Ta GNU -+.It Ic EE Ta 0 Ta current Ta Version 9 At -+.It Ic EX Ta 0 Ta current Ta Version 9 At - .It Ic I Ta n Ta next-line Ta \& - .It Ic IB Ta n Ta current Ta \& - .It Ic IR Ta n Ta current Ta \& ---- a/man.conf.5 -+++ b/man.conf.5 -@@ -101,15 +101,11 @@ manual. - .It Ic toc Ta none Ta Cm html Ta print table of contents - .It Ic width Ta integer Ta Cm ascii , utf8 Ta right margin - .El --.It Ic _whatdb Ar path Ns Cm /whatis.db --This directive provides the same functionality as --.Ic manpath , --but using a historic and misleading syntax. --It is kept for backward compatibility for now, --but will eventually be removed. - .El - .Sh FILES --.Pa /etc/man.conf -+.Bl -tag -width /etc/examples/man.conf -compact -+.It Pa /etc/man.conf -+.El - .Sh EXAMPLES - The following configuration file reproduces the defaults: - installing it is equivalent to not having a ---- a/man_html.c -+++ b/man_html.c -@@ -203,9 +203,9 @@ print_man_node(MAN_ARGS) - * Close out scope of font prior to opening a macro - * scope. - */ -- if (HTMLFONT_NONE != h->metac) { -+ if (h->metac != ESCAPE_FONTROMAN) { - h->metal = h->metac; -- h->metac = HTMLFONT_NONE; -+ h->metac = ESCAPE_FONTROMAN; - } - - /* ---- a/man_term.c -+++ b/man_term.c -@@ -1,7 +1,7 @@ - /* $Id: man_term.c,v 1.228 2019/01/05 21:18:26 schwarze Exp $ */ - /* - * Copyright (c) 2008-2012 Kristaps Dzonsons -- * Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze -+ * Copyright (c) 2010-2015, 2017-2020 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 -@@ -27,10 +27,12 @@ - #include - - #include "mandoc_aux.h" -+#include "mandoc.h" - #include "roff.h" - #include "man.h" - #include "out.h" - #include "term.h" -+#include "tag.h" - #include "main.h" - - #define MAXMARGINS 64 /* maximum number of indented scopes */ -@@ -92,6 +94,8 @@ static void post_SY(DECL_ARGS); - static void post_TP(DECL_ARGS); - static void post_UR(DECL_ARGS); - -+static void tag_man(struct termp *, struct roff_node *); -+ - static const struct man_term_act man_term_acts[MAN_MAX - MAN_TH] = { - { NULL, NULL, 0 }, /* TH */ - { pre_SH, post_SH, 0 }, /* SH */ -@@ -146,7 +150,7 @@ terminal_man(void *arg, const struct rof - { - struct mtermp mt; - struct termp *p; -- struct roff_node *n; -+ struct roff_node *n, *nc, *nn; - size_t save_defindent; - - p = (struct termp *)arg; -@@ -165,18 +169,23 @@ terminal_man(void *arg, const struct rof - - n = man->first->child; - if (p->synopsisonly) { -- while (n != NULL) { -- if (n->tok == MAN_SH && -- n->child->child->type == ROFFT_TEXT && -- !strcmp(n->child->child->string, "SYNOPSIS")) { -- if (n->child->next->child != NULL) -- print_man_nodelist(p, &mt, -- n->child->next->child, man); -- term_newln(p); -+ for (nn = NULL; n != NULL; n = n->next) { -+ if (n->tok != MAN_SH) -+ continue; -+ nc = n->child->child; -+ if (nc->type != ROFFT_TEXT) -+ continue; -+ if (strcmp(nc->string, "SYNOPSIS") == 0) - break; -- } -- n = n->next; -+ if (nn == NULL && strcmp(nc->string, "NAME") == 0) -+ nn = n; - } -+ if (n == NULL) -+ n = nn; -+ p->flags |= TERMP_NOSPACE; -+ if (n != NULL && (n = n->child->next->child) != NULL) -+ print_man_nodelist(p, &mt, n, man); -+ term_newln(p); - } else { - term_begin(p, print_man_head, print_man_foot, man); - p->flags |= TERMP_NOSPACE; -@@ -310,7 +319,7 @@ pre_alternate(DECL_ARGS) - assert(nn->type == ROFFT_TEXT); - term_word(p, nn->string); - if (nn->flags & NODE_EOS) -- p->flags |= TERMP_SENTENCE; -+ p->flags |= TERMP_SENTENCE; - if (nn->next != NULL) - p->flags |= TERMP_NOSPACE; - } -@@ -529,8 +538,10 @@ pre_IP(DECL_ARGS) - case ROFFT_HEAD: - p->tcol->offset = mt->offset; - p->tcol->rmargin = mt->offset + len; -- if (n->child != NULL) -+ if (n->child != NULL) { - print_man_node(p, mt, n->child, meta); -+ tag_man(p, n->child); -+ } - return 0; - case ROFFT_BODY: - p->tcol->offset = mt->offset + len; -@@ -610,6 +621,18 @@ pre_TP(DECL_ARGS) - while (nn != NULL && (nn->flags & NODE_LINE) == 0) - nn = nn->next; - -+ if (nn == NULL) -+ return 0; -+ -+ if (nn->type == ROFFT_TEXT) -+ tag_man(p, nn); -+ else if (nn->child != NULL && -+ nn->child->type == ROFFT_TEXT && -+ (nn->tok == MAN_B || nn->tok == MAN_BI || -+ nn->tok == MAN_BR || nn->tok == MAN_I || -+ nn->tok == MAN_IB || nn->tok == MAN_IR)) -+ tag_man(p, nn->child); -+ - while (nn != NULL) { - print_man_node(p, mt, nn, meta); - nn = nn->next; -@@ -1143,3 +1166,60 @@ print_man_head(struct termp *p, const st - } - free(title); - } -+ -+/* -+ * Skip leading whitespace, dashes, backslashes, and font escapes, -+ * then create a tag if the first following byte is a letter. -+ * Priority is high unless whitespace is present. -+ */ -+static void -+tag_man(struct termp *p, struct roff_node *n) -+{ -+ const char *cp, *arg; -+ int prio, sz; -+ -+ assert(n->type == ROFFT_TEXT); -+ cp = n->string; -+ prio = TAG_STRONG; -+ for (;;) { -+ switch (*cp) { -+ case ' ': -+ case '\t': -+ prio = TAG_WEAK; -+ /* FALLTHROUGH */ -+ case '-': -+ cp++; -+ break; -+ case '\\': -+ cp++; -+ switch (mandoc_escape(&cp, &arg, &sz)) { -+ case ESCAPE_FONT: -+ case ESCAPE_FONTROMAN: -+ case ESCAPE_FONTITALIC: -+ case ESCAPE_FONTBOLD: -+ case ESCAPE_FONTPREV: -+ case ESCAPE_FONTBI: -+ break; -+ case ESCAPE_SPECIAL: -+ if (sz != 1) -+ return; -+ switch (*arg) { -+ case '&': -+ case '-': -+ case 'e': -+ break; -+ default: -+ return; -+ } -+ break; -+ default: -+ return; -+ } -+ break; -+ default: -+ if (isalpha((unsigned char)*cp)) -+ tag_put(cp, prio, p->line); -+ return; -+ } -+ } -+} ---- a/man_validate.c -+++ b/man_validate.c -@@ -1,7 +1,7 @@ - /* $Id: man_validate.c,v 1.146 2018/12/31 10:04:39 schwarze Exp $ */ - /* - * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons -- * Copyright (c) 2010, 2012-2018 Ingo Schwarze -+ * Copyright (c) 2010, 2012-2020 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 -@@ -41,7 +41,7 @@ - - typedef void (*v_check)(CHKARGS); - --static void check_abort(CHKARGS); -+static void check_abort(CHKARGS) __attribute__((__noreturn__)); - static void check_par(CHKARGS); - static void check_part(CHKARGS); - static void check_root(CHKARGS); -@@ -185,8 +185,7 @@ check_root(CHKARGS) - - man->meta.title = mandoc_strdup(""); - man->meta.msec = mandoc_strdup(""); -- man->meta.date = man->quick ? mandoc_strdup("") : -- mandoc_normdate(man, NULL, n->line, n->pos); -+ man->meta.date = mandoc_normdate(NULL, NULL); - } - - if (man->meta.os_e && -@@ -369,8 +368,8 @@ post_TH(CHKARGS) - /* ->TITLE<- MSEC DATE OS VOL */ - - n = n->child; -- if (n && n->string) { -- for (p = n->string; '\0' != *p; p++) { -+ if (n != NULL && n->string != NULL) { -+ for (p = n->string; *p != '\0'; p++) { - /* Only warn about this once... */ - if (isalpha((unsigned char)*p) && - ! isupper((unsigned char)*p)) { -@@ -388,9 +387,9 @@ post_TH(CHKARGS) - - /* TITLE ->MSEC<- DATE OS VOL */ - -- if (n) -+ if (n != NULL) - n = n->next; -- if (n && n->string) -+ if (n != NULL && n->string != NULL) - man->meta.msec = mandoc_strdup(n->string); - else { - man->meta.msec = mandoc_strdup(""); -@@ -400,18 +399,12 @@ post_TH(CHKARGS) - - /* TITLE MSEC ->DATE<- OS VOL */ - -- if (n) -+ if (n != NULL) - n = n->next; -- if (n && n->string && '\0' != n->string[0]) { -- man->meta.date = man->quick ? -- mandoc_strdup(n->string) : -- mandoc_normdate(man, n->string, n->line, n->pos); -- } else { -+ if (man->quick && n != NULL) - man->meta.date = mandoc_strdup(""); -- mandoc_msg(MANDOCERR_DATE_MISSING, -- n ? n->line : nb->line, -- n ? n->pos : nb->pos, "TH"); -- } -+ else -+ man->meta.date = mandoc_normdate(n, nb); - - /* TITLE MSEC DATE ->OS<- VOL */ - ---- a/mandoc.1 -+++ b/mandoc.1 -@@ -1,7 +1,7 @@ - .\" $Id: mandoc.1,v 1.237 2019/02/23 18:53:54 schwarze Exp $ - .\" - .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons --.\" Copyright (c) 2012, 2014-2018 Ingo Schwarze -+.\" Copyright (c) 2012, 2014-2019 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 -@@ -222,7 +222,8 @@ reads from standard input. - .Pp - The options - .Fl fhklw --are also supported and are documented in man(1). -+are also supported and are documented in -+.Xr man 1 . - In - .Fl f - and -@@ -697,7 +698,7 @@ No input files have been read. - .It 6 - An operating system error occurred, for example exhaustion - of memory, file descriptors, or process table entries. --Such errors cause -+Such errors may cause - .Nm - to exit at once, possibly in the middle of parsing or formatting a file. - .El -@@ -777,6 +778,13 @@ fields. - .Pp - Message levels have the following meanings: - .Bl -tag -width "warning" -+.It Cm syserr -+An operating system error occurred. -+There isn't necessarily anything wrong with the input files. -+Output may all the same be missing or incomplete. -+.It Cm badarg -+Invalid command line arguments were specified. -+No input files have been read and no output is produced. - .It Cm unsupp - An input file uses unsupported low-level - .Xr roff 7 -@@ -825,8 +833,7 @@ Messages of the - .Cm error , - and - .Cm unsupp --levels except those about non-existent or unreadable input files --are hidden unless their level, or a lower level, is requested using a -+levels are hidden unless their level, or a lower level, is requested using a - .Fl W - option or - .Fl T Cm lint -@@ -1066,7 +1073,7 @@ macro lacks the mandatory section argume - The section number in a - .Ic \&Dt - line is invalid, but still used. --.It Sy "missing date, using today's date" -+.It Sy "missing date, using \(dq\(dq" - .Pq mdoc, man - The document was parsed as - .Xr mdoc 7 -@@ -1699,9 +1706,12 @@ The meaning of blank input lines is only - 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 -+are formatted like - .Ic \&sp - requests. -+To request a paragraph break, use -+.Ic \&Pp -+instead of a blank line. - .It Sy "tab in filled text" - .Pq mdoc , man - The meaning of tab characters is only well-defined in non-fill mode: -@@ -2238,6 +2248,43 @@ macro or of an undefined macro. - The macro is ignored, and its arguments are handled - as if they were a text line. - .El -+.Ss Bad command line arguments -+.Bl -ohang -+.It Sy "bad command line argument" -+The argument following one of the -+.Fl IKMmOTW -+command line options is invalid, or a -+.Ar file -+given as a command line argument cannot be opened. -+.It Sy "duplicate command line argument" -+The -+.Fl I -+command line option was specified twice. -+.It Sy "option has a superfluous value" -+An argument to the -+.Fl O -+option has a value but does not accept one. -+.It Sy "missing option value" -+An argument to the -+.Fl O -+option has no argument but requires one. -+.It Sy "bad option value" -+An argument to the -+.Fl O -+.Cm indent -+or -+.Cm width -+option has an invalid value. -+.It Sy "duplicate option value" -+The same -+.Fl O -+option is specified more than once. -+.It Sy "no such tag" -+The -+.Fl O Cm tag -+option was specified but the tag was not found in any of the displayed -+manual pages. -+.El - .Sh SEE ALSO - .Xr apropos 1 , - .Xr man 1 , ---- a/mandoc.c -+++ b/mandoc.c -@@ -1,7 +1,7 @@ - /* $Id: mandoc.c,v 1.114 2018/12/30 00:49:55 schwarze Exp $ */ - /* - * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons -- * Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze -+ * Copyright (c) 2011-2015, 2017-2020 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 -@@ -494,9 +494,10 @@ time2a(time_t t) - size_t ssz; - int isz; - -+ buf = NULL; - tm = localtime(&t); - if (tm == NULL) -- return NULL; -+ goto fail; - - /* - * Reserve space: -@@ -520,7 +521,8 @@ time2a(time_t t) - * of looking at LC_TIME. - */ - -- if ((isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday)) == -1) -+ isz = snprintf(p, 4 + 1, "%d, ", tm->tm_mday); -+ if (isz < 0 || isz > 4) - goto fail; - p += isz; - -@@ -530,46 +532,63 @@ time2a(time_t t) - - fail: - free(buf); -- return NULL; -+ return mandoc_strdup(""); - } - - char * --mandoc_normdate(struct roff_man *man, char *in, int ln, int pos) -+mandoc_normdate(struct roff_node *nch, struct roff_node *nbl) - { - char *cp; - time_t t; - -- /* No date specified: use today's date. */ -+ /* No date specified. */ - -- if (in == NULL || *in == '\0' || strcmp(in, "$" "Mdocdate$") == 0) { -- mandoc_msg(MANDOCERR_DATE_MISSING, ln, pos, NULL); -- return time2a(time(NULL)); -+ if (nch == NULL) { -+ if (nbl == NULL) -+ mandoc_msg(MANDOCERR_DATE_MISSING, 0, 0, NULL); -+ else -+ mandoc_msg(MANDOCERR_DATE_MISSING, nbl->line, -+ nbl->pos, "%s", roff_name[nbl->tok]); -+ return mandoc_strdup(""); - } -+ if (*nch->string == '\0') { -+ mandoc_msg(MANDOCERR_DATE_MISSING, nch->line, -+ nch->pos, "%s", roff_name[nbl->tok]); -+ return mandoc_strdup(""); -+ } -+ if (strcmp(nch->string, "$" "Mdocdate$") == 0) -+ return time2a(time(NULL)); - - /* Valid mdoc(7) date format. */ - -- if (a2time(&t, "$" "Mdocdate: %b %d %Y $", in) || -- a2time(&t, "%b %d, %Y", in)) { -+ if (a2time(&t, "$" "Mdocdate: %b %d %Y $", nch->string) || -+ a2time(&t, "%b %d, %Y", nch->string)) { - cp = time2a(t); - if (t > time(NULL) + 86400) -- mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", cp); -- else if (*in != '$' && strcmp(in, cp) != 0) -- mandoc_msg(MANDOCERR_DATE_NORM, ln, pos, "%s", cp); -+ mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, -+ nch->pos, "%s %s", roff_name[nbl->tok], cp); -+ else if (*nch->string != '$' && -+ strcmp(nch->string, cp) != 0) -+ mandoc_msg(MANDOCERR_DATE_NORM, nch->line, -+ nch->pos, "%s %s", roff_name[nbl->tok], cp); - return cp; - } - - /* In man(7), do not warn about the legacy format. */ - -- if (a2time(&t, "%Y-%m-%d", in) == 0) -- mandoc_msg(MANDOCERR_DATE_BAD, ln, pos, "%s", in); -+ if (a2time(&t, "%Y-%m-%d", nch->string) == 0) -+ mandoc_msg(MANDOCERR_DATE_BAD, nch->line, nch->pos, -+ "%s %s", roff_name[nbl->tok], nch->string); - else if (t > time(NULL) + 86400) -- mandoc_msg(MANDOCERR_DATE_FUTURE, ln, pos, "%s", in); -- else if (man->meta.macroset == MACROSET_MDOC) -- mandoc_msg(MANDOCERR_DATE_LEGACY, ln, pos, "Dd %s", in); -+ mandoc_msg(MANDOCERR_DATE_FUTURE, nch->line, nch->pos, -+ "%s %s", roff_name[nbl->tok], nch->string); -+ else if (nbl->tok == MDOC_Dd) -+ mandoc_msg(MANDOCERR_DATE_LEGACY, nch->line, nch->pos, -+ "Dd %s", nch->string); - - /* Use any non-mdoc(7) date verbatim. */ - -- return mandoc_strdup(in); -+ return mandoc_strdup(nch->string); - } - - int ---- a/mandoc.css -+++ b/mandoc.css -@@ -10,8 +10,13 @@ - - /* Global defaults. */ - --html { max-width: 65em; } --body { font-family: Helvetica,Arial,sans-serif; } -+html { max-width: 65em; -+ --bg: #FFFFFF; -+ --fg: #000000; } -+body { background: var(--bg); -+ color: var(--fg); -+ font-family: Helvetica,Arial,sans-serif; } -+h1 { font-size: 110%; } - table { margin-top: 0em; - margin-bottom: 0em; - border-collapse: collapse; } -@@ -69,8 +74,7 @@ td.foot-os { text-align: right; } - section.Sh { } - h1.Sh { margin-top: 1.2em; - margin-bottom: 0.6em; -- margin-left: -3.2em; -- font-size: 110%; } -+ margin-left: -3.2em; } - section.Ss { } - h2.Ss { margin-top: 1.2em; - margin-bottom: 0.6em; -@@ -310,14 +314,14 @@ h1.Sh::before, h2.Ss::before, .St::befor - pointer-events: none; - position: absolute; - bottom: 100%; -- box-shadow: 0 0 .35em #000; -+ box-shadow: 0 0 .35em var(--fg); - padding: .15em .25em; - white-space: nowrap; - font-family: Helvetica,Arial,sans-serif; - font-style: normal; - font-weight: bold; -- color: black; -- background: #fff; } -+ background: var(--bg); -+ color: var(--fg); } - .An:hover::before, .Ar:hover::before, .Cd:hover::before, .Cm:hover::before, - .Dv:hover::before, .Em:hover::before, .Er:hover::before, .Ev:hover::before, - .Fa:hover::before, .Fd:hover::before, .Fl:hover::before, .Fn:hover::before, -@@ -345,3 +349,12 @@ h1.Sh, h2.Ss { margin-left: 0em; } - .HP { margin-left: 2em; - text-indent: -2em; } - } -+ -+/* Overrides for a dark color scheme for accessibility. */ -+ -+@media (prefers-color-scheme: dark) { -+html { --bg: #1E1F21; -+ --fg: #EEEFF1; } -+:link { color: #BAD7FF; } -+:visited { color: #F6BAFF; } -+} ---- a/mandoc.h -+++ b/mandoc.h -@@ -1,7 +1,7 @@ - /* $Id: mandoc.h,v 1.262 2018/12/16 00:17:02 schwarze Exp $ */ - /* - * Copyright (c) 2010, 2011, 2014 Kristaps Dzonsons -- * Copyright (c) 2012-2018 Ingo Schwarze -+ * Copyright (c) 2012-2020 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 -@@ -83,7 +83,7 @@ enum mandocerr { - MANDOCERR_TH_NOTITLE, /* missing manual title, using "": [macro] */ - MANDOCERR_MSEC_MISSING, /* missing manual section, using "": macro */ - MANDOCERR_MSEC_BAD, /* unknown manual section: Dt ... section */ -- MANDOCERR_DATE_MISSING, /* missing date, using today's date */ -+ MANDOCERR_DATE_MISSING, /* missing date, using "": [macro] */ - MANDOCERR_DATE_BAD, /* cannot parse date, using it verbatim: date */ - MANDOCERR_DATE_FUTURE, /* date in the future, using it anyway: date */ - MANDOCERR_OS_MISSING, /* missing Os macro, using "" */ -@@ -193,7 +193,6 @@ enum mandocerr { - MANDOCERR_TBLDATA_BLK, /* data block open at end of tbl: macro */ - - /* related to document structure and macros */ -- MANDOCERR_FILE, /* cannot open file */ - MANDOCERR_PROLOG_REP, /* duplicate prologue macro: macro */ - MANDOCERR_DT_LATE, /* skipping late title macro: Dt args */ - MANDOCERR_ROFFLOOP, /* input stack limit exceeded, infinite loop? */ -@@ -224,6 +223,7 @@ enum mandocerr { - MANDOCERR_SHIFT, /* excessive shift: ..., but max is ... */ - MANDOCERR_SO_PATH, /* NOT IMPLEMENTED: .so with absolute path or ".." */ - MANDOCERR_SO_FAIL, /* .so request failed */ -+ MANDOCERR_TG_SPC, /* skipping tag containing whitespace: tag */ - MANDOCERR_ARG_SKIP, /* skipping all arguments: macro args */ - MANDOCERR_ARG_EXCESS, /* skipping excess arguments: macro ... args */ - MANDOCERR_DIVZERO, /* divide by zero */ -@@ -242,6 +242,35 @@ enum mandocerr { - MANDOCERR_TBLLAYOUT_MOD, /* unsupported tbl layout modifier: m */ - MANDOCERR_TBLMACRO, /* ignoring macro in table: macro */ - -+ MANDOCERR_BADARG, /* ===== start of bad invocations ===== */ -+ -+ MANDOCERR_BADARG_BAD, /* bad argument */ -+ MANDOCERR_BADARG_DUPE, /* duplicate argument */ -+ MANDOCERR_BADVAL, /* does not take a value */ -+ MANDOCERR_BADVAL_MISS, /* missing argument value */ -+ MANDOCERR_BADVAL_BAD, /* bad argument value */ -+ MANDOCERR_BADVAL_DUPE, /* duplicate argument value */ -+ MANDOCERR_TAG, /* no such tag */ -+ -+ MANDOCERR_SYSERR, /* ===== start of system errors ===== */ -+ -+ MANDOCERR_DUP, -+ MANDOCERR_EXEC, -+ MANDOCERR_FDOPEN, -+ MANDOCERR_FFLUSH, -+ MANDOCERR_FORK, -+ MANDOCERR_FSTAT, -+ MANDOCERR_GETLINE, -+ MANDOCERR_GLOB, -+ MANDOCERR_GZCLOSE, -+ MANDOCERR_GZDOPEN, -+ MANDOCERR_MKSTEMP, -+ MANDOCERR_OPEN, -+ MANDOCERR_PLEDGE, -+ MANDOCERR_READ, -+ MANDOCERR_WAIT, -+ MANDOCERR_WRITE, -+ - MANDOCERR_MAX - }; - -@@ -281,6 +310,7 @@ enum mandoclevel mandoc_msg_getrc(void) - void mandoc_msg_setrc(enum mandoclevel); - void mandoc_msg(enum mandocerr, int, int, const char *, ...) - __attribute__((__format__ (__printf__, 4, 5))); -+void mandoc_msg_summary(void); - void mchars_alloc(void); - void mchars_free(void); - int mchars_num2char(const char *, size_t); ---- a/mandoc_char.7 -+++ b/mandoc_char.7 -@@ -107,8 +107,8 @@ supporting it, for example in - .Fl T Cm utf8 - and - .Fl T Cm html . --But currently, no practically relevant manual page formatter actually --requires that subtlety, so in manual pages just write plain -+But currently, no practically relevant manual page formatter requires -+that subtlety, so in manual pages, it is sufficient to write plain - .Sq - - to represent hyphen, minus, and hyphen-minus. - .Pp ---- a/mandoc_headers.3 -+++ b/mandoc_headers.3 -@@ -1,3 +1,19 @@ -+.\" $Id$ -+.\" -+.\" Copyright (c) 2014-2019 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: December 30 2018 $ - .Dt MANDOC_HEADERS 3 - .Os -@@ -129,19 +145,19 @@ and the function - Uses pointers to the types - .Vt struct ohash - from --.Pa mandoc_ohash.h , -+.Qq Pa mandoc_ohash.h , - .Vt struct mdoc_arg - and - .Vt union mdoc_data - from --.Pa mdoc.h , -+.Qq Pa mdoc.h , - .Vt struct tbl_span - from --.Pa tbl.h , -+.Qq Pa tbl.h , - and - .Vt struct eqn_box - from --.Pa eqn.h -+.Qq Pa eqn.h - as opaque struct members. - .It Qq Pa tbl.h - Data structures for the -@@ -184,13 +200,13 @@ Top level parser interface, for use in t - and in the main parser, but not in formatters. - .Pp - Requires --.Pa mandoc.h -+.Qq Pa mandoc.h - for - .Vt enum mandocerr - and - .Vt enum mandoclevel - and --.Pa roff.h -+.Qq Pa roff.h - for - .Vt enum mandoc_os . - .Pp -@@ -202,7 +218,7 @@ for function prototypes. - Uses - .Vt struct roff_meta - from --.Pa roff.h -+.Qq Pa roff.h - as an opaque type for function prototypes. - .It Qq Pa mandoc_xr.h - Cross reference validation; intended for use in the main program -@@ -251,11 +267,11 @@ described in - Uses the types - .Vt struct roff_node - from --.Pa roff.h -+.Qq Pa roff.h - and - .Vt struct roff_man - from --.Pa roff_int.h -+.Qq Pa roff_int.h - as opaque types for function prototypes. - .Pp - When this header is included, the same file should not include -@@ -269,7 +285,7 @@ described in - Uses the type - .Vt struct roff_man - from --.Pa roff.h -+.Qq Pa roff.h - as an opaque type for function prototypes. - .Pp - When this header is included, the same file should not include -@@ -305,7 +321,7 @@ for function prototypes. - Uses the type - .Vt struct roff_man - from --.Pa roff.h -+.Qq Pa roff.h - as an opaque type for function prototypes. - .It Qq Pa roff_int.h - Parser internals shared by multiple parsers. -@@ -334,24 +350,24 @@ and the two special functions - and - .Fn mdoc_argv_free - because the latter two are needed by --.Qq Pa roff.c . -+.Pa roff.c . - .Pp - Uses the types - .Vt struct ohash - from --.Pa mandoc_ohash.h , -+.Qq Pa mandoc_ohash.h , - .Vt struct roff_node - and - .Vt struct roff_meta - from --.Pa roff.h , -+.Qq Pa roff.h , - .Vt struct roff - from - .Pa roff.c , - and - .Vt struct mdoc_arg - from --.Pa mdoc.h -+.Qq Pa mdoc.h - as opaque types for function prototypes. - .It Qq Pa libmdoc.h - Requires -@@ -372,14 +388,14 @@ parser. - Uses the types - .Vt struct roff_node - from --.Pa roff.h , -+.Qq Pa roff.h , - .Vt struct roff_man - from --.Pa roff_int.h , -+.Qq Pa roff_int.h , - and - .Vt struct mdoc_arg - from --.Pa mdoc.h -+.Qq Pa mdoc.h - as opaque types for function prototypes. - .Pp - When this header is included, the same file should not include -@@ -399,11 +415,11 @@ parser. - Uses the types - .Vt struct roff_node - from --.Pa roff.h -+.Qq Pa roff.h - and - .Vt struct roff_man - from --.Pa roff_int.h -+.Qq Pa roff_int.h - as opaque types for function prototypes. - .Pp - When this header is included, the same file should not include -@@ -437,12 +453,12 @@ and - Uses the type - .Vt struct eqn_box - from --.Pa mandoc.h -+.Qq Pa mandoc.h - as an opaque type for function prototypes. - Uses the types - .Vt struct roff_node - from --.Pa roff.h -+.Qq Pa roff.h - and - .Vt struct eqn_def - from -@@ -466,11 +482,11 @@ Provides the functions documented in - Uses the types - .Vt struct tbl_span - from --.Pa tbl.h -+.Qq Pa tbl.h - and - .Vt struct tbl_node - from --.Pa tbl_int.h -+.Qq Pa tbl_int.h - as opaque types for function prototypes. - .Pp - When this header is included, the same file should not include -@@ -523,11 +539,11 @@ and - Uses - .Vt struct tbl_span - from --.Pa mandoc.h -+.Qq Pa mandoc.h - as an opaque type for function prototypes. - .Pp - When this header is included, the same file should not include --.Pa mansearch.h . -+.Qq Pa mansearch.h . - .It Qq Pa term.h - Requires - .In sys/types.h -@@ -558,27 +574,30 @@ Uses - and - .Vt struct eqn_box - from --.Pa mandoc.h -+.Qq Pa mandoc.h - and - .Vt struct roff_meta - and - .Vt struct roff_node - from --.Pa roff.h -+.Qq Pa roff.h - as opaque types for function prototypes. - .Pp - When this header is included, the same file should not include --.Pa html.h -+.Qq Pa html.h - or --.Pa mansearch.h . -+.Qq Pa mansearch.h . - .It Qq Pa html.h - Requires - .In sys/types.h - for - .Vt size_t , --.Pa mandoc.h -+.Qq Pa mandoc.h - for - .Vt enum mandoc_esc , -+.Qq Pa roff.h -+for -+.Vt enum roff_tok , - and - .Qq Pa out.h - for -@@ -602,22 +621,26 @@ Uses - and - .Vt struct eqn_box - from --.Pa mandoc.h -+.Qq Pa mandoc.h - and - .Vt struct roff_node - from --.Pa roff.h -+.Qq Pa roff.h - as opaque types for function prototypes. - .Pp - When this header is included, the same file should not include --.Pa term.h -+.Qq Pa term.h - or --.Pa mansearch.h . -+.Qq Pa mansearch.h . - .It Qq Pa tag.h - Requires - .In sys/types.h - for --.Vt size_t . -+.Vt size_t -+and -+.In limits.h -+for -+.Dv INT_MAX . - .Pp - Provides an interface to generate - .Xr ctags 1 -@@ -631,7 +654,7 @@ Provides the top level steering function - Uses the type - .Vt struct roff_meta - from --.Pa roff.h -+.Qq Pa roff.h - as an opaque type for function prototypes. - .It Qq Pa manconf.h - Requires -@@ -671,12 +694,12 @@ and - Uses - .Vt struct manpaths - from --.Pa manconf.h -+.Qq Pa manconf.h - as an opaque type for function prototypes. - .Pp - When this header is included, the same file should not include --.Pa out.h , --.Pa term.h , -+.Qq Pa out.h , -+.Qq Pa term.h , - or --.Pa html.h . -+.Qq Pa html.h . - .El ---- a/mandoc_msg.c -+++ b/mandoc_msg.c -@@ -1,7 +1,7 @@ - /* $Id: mandoc_msg.c,v 1.6 2019/03/06 15:55:38 schwarze Exp $ */ - /* - * Copyright (c) 2010, 2011 Kristaps Dzonsons -- * Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze -+ * Copyright (c) 2014-2020 Ingo Schwarze - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -29,8 +29,8 @@ static const enum mandocerr lowest_type[ - MANDOCERR_WARNING, - MANDOCERR_ERROR, - MANDOCERR_UNSUPP, -- MANDOCERR_MAX, -- MANDOCERR_MAX -+ MANDOCERR_BADARG, -+ MANDOCERR_SYSERR - }; - - static const char *const level_name[MANDOCLEVEL_MAX] = { -@@ -83,7 +83,7 @@ static const char *const type_message[MA - "missing manual title, using \"\"", - "missing manual section, using \"\"", - "unknown manual section", -- "missing date, using today's date", -+ "missing date, using \"\"", - "cannot parse date, using it verbatim", - "date in the future, using it anyway", - "missing Os macro, using \"\"", -@@ -193,7 +193,6 @@ static const char *const type_message[MA - "data block open at end of tbl", - - /* related to document structure and macros */ -- NULL, - "duplicate prologue macro", - "skipping late title macro", - "input stack limit exceeded, infinite loop?", -@@ -224,6 +223,7 @@ static const char *const type_message[MA - "excessive shift", - "NOT IMPLEMENTED: .so with absolute path or \"..\"", - ".so request failed", -+ "skipping tag containing whitespace", - "skipping all arguments", - "skipping excess arguments", - "divide by zero", -@@ -240,11 +240,40 @@ static const char *const type_message[MA - "eqn delim option in tbl", - "unsupported tbl layout modifier", - "ignoring macro in table", -+ -+ /* bad command line arguments */ -+ NULL, -+ "bad command line argument", -+ "duplicate command line argument", -+ "option has a superfluous value", -+ "missing option value", -+ "bad option value", -+ "duplicate option value", -+ "no such tag", -+ -+ /* system errors */ -+ NULL, -+ "dup", -+ "exec", -+ "fdopen", -+ "fflush", -+ "fork", -+ "fstat", -+ "getline", -+ "glob", -+ "gzclose", -+ "gzdopen", -+ "mkstemp", -+ "open", -+ "pledge", -+ "read", -+ "wait", -+ "write", - }; - - static FILE *fileptr = NULL; - static const char *filename = NULL; --static enum mandocerr min_type = MANDOCERR_MAX; -+static enum mandocerr min_type = MANDOCERR_BADARG; - static enum mandoclevel rc = MANDOCLEVEL_OK; - - -@@ -297,10 +326,10 @@ mandoc_msg(enum mandocerr t, int line, i - va_list ap; - enum mandoclevel level; - -- if (t < min_type && t != MANDOCERR_FILE) -+ if (t < min_type) - return; - -- level = MANDOCLEVEL_UNSUPP; -+ level = MANDOCLEVEL_SYSERR; - while (t < lowest_type[level]) - level--; - mandoc_msg_setrc(level); -@@ -327,3 +356,12 @@ mandoc_msg(enum mandocerr t, int line, i - } - fputc('\n', fileptr); - } -+ -+void -+mandoc_msg_summary(void) -+{ -+ if (fileptr != NULL && rc != MANDOCLEVEL_OK) -+ fprintf(fileptr, -+ "%s: see above the output for %s messages\n", -+ getprogname(), level_name[rc]); -+} ---- a/mandoc_parse.h -+++ b/mandoc_parse.h -@@ -29,6 +29,7 @@ - #define MPARSE_UTF8 (1 << 4) /* accept UTF-8 input */ - #define MPARSE_LATIN1 (1 << 5) /* accept ISO-LATIN-1 input */ - #define MPARSE_VALIDATE (1 << 6) /* call validation functions */ -+#define MPARSE_COMMENT (1 << 7) /* save comments in the tree */ - - - struct roff_meta; ---- a/mandocdb.c -+++ b/mandocdb.c -@@ -1,7 +1,7 @@ - /* $Id: mandocdb.c,v 1.262 2018/12/30 00:49:55 schwarze Exp $ */ - /* - * Copyright (c) 2011, 2012 Kristaps Dzonsons -- * Copyright (c) 2011-2018 Ingo Schwarze -+ * Copyright (c) 2011-2020 Ingo Schwarze - * Copyright (c) 2016 Ed Maste - * - * Permission to use, copy, modify, and distribute this software for any -@@ -179,6 +179,7 @@ static int write_utf8; /* write UTF-8 - static int exitcode; /* to be returned by main */ - static enum op op; /* operational mode */ - static char basedir[PATH_MAX]; /* current base directory */ -+static size_t basedir_len; /* strlen(basedir) */ - static struct mpage *mpage_head; /* list of distinct manual pages */ - static struct ohash mpages; /* table of distinct manual pages */ - static struct ohash mlinks; /* table of directory entries */ -@@ -342,7 +343,7 @@ mandocdb(int argc, char *argv[]) - * clobber each other. - */ - #define CHECKOP(_op, _ch) do \ -- if (OP_DEFAULT != (_op)) { \ -+ if ((_op) != OP_DEFAULT) { \ - warnx("-%c: Conflicting option", (_ch)); \ - goto usage; \ - } while (/*CONSTCOND*/0) -@@ -351,7 +352,7 @@ mandocdb(int argc, char *argv[]) - path_arg = NULL; - op = OP_DEFAULT; - -- while (-1 != (ch = getopt(argc, argv, "aC:Dd:npQT:tu:v"))) -+ while ((ch = getopt(argc, argv, "aC:Dd:npQT:tu:v")) != -1) - switch (ch) { - case 'a': - use_all = 1; -@@ -379,7 +380,7 @@ mandocdb(int argc, char *argv[]) - mparse_options |= MPARSE_QUICK; - break; - case 'T': -- if (strcmp(optarg, "utf8")) { -+ if (strcmp(optarg, "utf8") != 0) { - warnx("-T%s: Unsupported output format", - optarg); - goto usage; -@@ -416,7 +417,7 @@ mandocdb(int argc, char *argv[]) - } - #endif - -- if (OP_CONFFILE == op && argc > 0) { -+ if (op == OP_CONFFILE && argc > 0) { - warnx("-C: Too many arguments"); - goto usage; - } -@@ -427,13 +428,13 @@ mandocdb(int argc, char *argv[]) - mandoc_ohash_init(&mpages, 6, offsetof(struct mpage, inodev)); - mandoc_ohash_init(&mlinks, 6, offsetof(struct mlink, file)); - -- if (OP_UPDATE == op || OP_DELETE == op || OP_TEST == op) { -+ if (op == OP_UPDATE || op == OP_DELETE || op == OP_TEST) { - - /* - * Most of these deal with a specific directory. - * Jump into that directory first. - */ -- if (OP_TEST != op && 0 == set_basedir(path_arg, 1)) -+ if (op != OP_TEST && set_basedir(path_arg, 1) == 0) - goto out; - - dba = nodb ? dba_new(128) : dba_read(MANDOC_DB); -@@ -454,11 +455,11 @@ mandocdb(int argc, char *argv[]) - " from scratch", strerror(errno)); - exitcode = (int)MANDOCLEVEL_OK; - op = OP_DEFAULT; -- if (0 == treescan()) -+ if (treescan() == 0) - goto out; - dba = dba_new(128); - } -- if (OP_DELETE != op) -+ if (op != OP_DELETE) - mpages_merge(dba, mp); - if (nodb == 0) - dbwrite(dba); -@@ -492,7 +493,7 @@ mandocdb(int argc, char *argv[]) - sz = strlen(conf.manpath.paths[j]); - if (sz && conf.manpath.paths[j][sz - 1] == '/') - conf.manpath.paths[j][--sz] = '\0'; -- if (0 == sz) -+ if (sz == 0) - continue; - - if (j) { -@@ -502,9 +503,9 @@ mandocdb(int argc, char *argv[]) - offsetof(struct mlink, file)); - } - -- if ( ! set_basedir(conf.manpath.paths[j], argc > 0)) -+ if (set_basedir(conf.manpath.paths[j], argc > 0) == 0) - continue; -- if (0 == treescan()) -+ if (treescan() == 0) - continue; - dba = dba_new(128); - mpages_merge(dba, mp); -@@ -608,9 +609,9 @@ treescan(void) - say(path, "&realpath"); - continue; - } -- if (strstr(buf, basedir) != buf -+ if (strncmp(buf, basedir, basedir_len) != 0 - #ifdef HOMEBREWDIR -- && strstr(buf, HOMEBREWDIR) != buf -+ && strncmp(buf, HOMEBREWDIR, strlen(HOMEBREWDIR)) - #endif - ) { - if (warnings) say("", -@@ -777,17 +778,17 @@ treescan(void) - * See treescan() for the fts(3) version of this. - */ - static void --filescan(const char *file) -+filescan(const char *infile) - { -- char buf[PATH_MAX]; - struct stat st; - struct mlink *mlink; -- char *p, *start; -+ char *linkfile, *p, *realdir, *start, *usefile; -+ size_t realdir_len; - - assert(use_all); - -- if (0 == strncmp(file, "./", 2)) -- file += 2; -+ if (strncmp(infile, "./", 2) == 0) -+ infile += 2; - - /* - * We have to do lstat(2) before realpath(3) loses -@@ -796,13 +797,13 @@ filescan(const char *file) - * we want to use the orginal file name, while for - * regular files, we want to use the real path. - */ -- if (-1 == lstat(file, &st)) { -+ if (lstat(infile, &st) == -1) { - exitcode = (int)MANDOCLEVEL_BADARG; -- say(file, "&lstat"); -+ say(infile, "&lstat"); - return; -- } else if (0 == ((S_IFREG | S_IFLNK) & st.st_mode)) { -+ } else if (S_ISREG(st.st_mode) == 0 && S_ISLNK(st.st_mode) == 0) { - exitcode = (int)MANDOCLEVEL_BADARG; -- say(file, "Not a regular file"); -+ say(infile, "Not a regular file"); - return; - } - -@@ -810,23 +811,24 @@ filescan(const char *file) - * We have to resolve the file name to the real path - * in any case for the base directory check. - */ -- if (NULL == realpath(file, buf)) { -+ if ((usefile = realpath(infile, NULL)) == NULL) { - exitcode = (int)MANDOCLEVEL_BADARG; -- say(file, "&realpath"); -+ say(infile, "&realpath"); - return; - } - -- if (OP_TEST == op) -- start = buf; -- else if (strstr(buf, basedir) == buf) -- start = buf + strlen(basedir); -+ if (op == OP_TEST) -+ start = usefile; -+ else if (strncmp(usefile, basedir, basedir_len) == 0) -+ start = usefile + basedir_len; - #ifdef HOMEBREWDIR -- else if (strstr(buf, HOMEBREWDIR) == buf) -- start = buf; -+ else if (strncmp(usefile, HOMEBREWDIR, strlen(HOMEBREWDIR)) == 0) -+ start = usefile; - #endif - else { - exitcode = (int)MANDOCLEVEL_BADARG; -- say("", "%s: outside base directory", buf); -+ say("", "%s: outside base directory", infile); -+ free(usefile); - return; - } - -@@ -834,25 +836,72 @@ filescan(const char *file) - * 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)) { -+ do { -+ if (S_ISLNK(st.st_mode) == 0) -+ break; -+ -+ /* -+ * Some implementations of realpath(3) may succeed -+ * even if the target of the link does not exist, -+ * so check again for extra safety. -+ */ -+ if (stat(usefile, &st) == -1) { - exitcode = (int)MANDOCLEVEL_BADARG; -- say(file, "&stat"); -+ say(infile, "&stat"); -+ free(usefile); - return; - } -- if (strlcpy(buf, file, sizeof(buf)) >= sizeof(buf)) { -- say(file, "Filename too long"); -- return; -+ linkfile = mandoc_strdup(infile); -+ if (op == OP_TEST) { -+ free(usefile); -+ start = usefile = linkfile; -+ break; -+ } -+ if (strncmp(infile, basedir, basedir_len) == 0) { -+ free(usefile); -+ usefile = linkfile; -+ start = usefile + basedir_len; -+ break; - } -- start = buf; -- if (OP_TEST != op && strstr(buf, basedir) == buf) -- start += strlen(basedir); -- } -+ -+ /* -+ * This symbolic link points into the basedir -+ * from the outside. Let's see whether any of -+ * the parent directories resolve to the basedir. -+ */ -+ p = strchr(linkfile, '\0'); -+ do { -+ while (*--p != '/') -+ continue; -+ *p = '\0'; -+ if ((realdir = realpath(linkfile, NULL)) == NULL) { -+ exitcode = (int)MANDOCLEVEL_BADARG; -+ say(infile, "&realpath"); -+ free(linkfile); -+ free(usefile); -+ return; -+ } -+ realdir_len = strlen(realdir) + 1; -+ free(realdir); -+ *p = '/'; -+ } while (realdir_len > basedir_len); -+ -+ /* -+ * If one of the directories resolves to the basedir, -+ * use the rest of the original name. -+ * Otherwise, the best we can do -+ * is to use the filename pointed to. -+ */ -+ if (realdir_len == basedir_len) { -+ free(usefile); -+ usefile = linkfile; -+ start = p + 1; -+ } else { -+ free(linkfile); -+ start = usefile + basedir_len; -+ } -+ } while (/* CONSTCOND */ 0); - - mlink = mandoc_calloc(1, sizeof(struct mlink)); - mlink->dform = FORM_NONE; -@@ -860,6 +909,7 @@ filescan(const char *file) - sizeof(mlink->file)) { - say(start, "Filename too long"); - free(mlink); -+ free(usefile); - return; - } - -@@ -868,13 +918,13 @@ filescan(const char *file) - * but outside our tree, guess the base directory. - */ - -- if (op == OP_TEST || (start == buf && *start == '/')) { -- if (strncmp(buf, "man/", 4) == 0) -- start = buf + 4; -- else if ((start = strstr(buf, "/man/")) != NULL) -+ if (op == OP_TEST || (start == usefile && *start == '/')) { -+ if (strncmp(usefile, "man/", 4) == 0) -+ start = usefile + 4; -+ else if ((start = strstr(usefile, "/man/")) != NULL) - start += 5; - else -- start = buf; -+ start = usefile; - } - - /* -@@ -883,18 +933,18 @@ filescan(const char *file) - * If we find one of these and what's underneath is a directory, - * assume it's an architecture. - */ -- if (NULL != (p = strchr(start, '/'))) { -+ if ((p = strchr(start, '/')) != NULL) { - *p++ = '\0'; -- if (0 == strncmp(start, "man", 3)) { -+ if (strncmp(start, "man", 3) == 0) { - mlink->dform = FORM_SRC; - mlink->dsec = start + 3; -- } else if (0 == strncmp(start, "cat", 3)) { -+ } else if (strncmp(start, "cat", 3) == 0) { - mlink->dform = FORM_CAT; - mlink->dsec = start + 3; - } - - start = p; -- if (NULL != mlink->dsec && NULL != (p = strchr(start, '/'))) { -+ if (mlink->dsec != NULL && (p = strchr(start, '/')) != NULL) { - *p++ = '\0'; - mlink->arch = start; - start = p; -@@ -906,10 +956,10 @@ filescan(const char *file) - * Suffix of `.0' indicates a catpage, `.1-9' is a manpage. - */ - p = strrchr(start, '\0'); -- while (p-- > start && '/' != *p && '.' != *p) -- /* Loop. */ ; -+ while (p-- > start && *p != '/' && *p != '.') -+ continue; - -- if ('.' == *p) { -+ if (*p == '.') { - *p++ = '\0'; - mlink->fsec = p; - } -@@ -919,11 +969,12 @@ filescan(const char *file) - * Use the filename portion of the path. - */ - mlink->name = start; -- if (NULL != (p = strrchr(start, '/'))) { -+ if ((p = strrchr(start, '/')) != NULL) { - mlink->name = p + 1; - *p = '\0'; - } - mlink_add(mlink, &st); -+ free(usefile); - } - - static void -@@ -1186,9 +1237,11 @@ mpages_merge(struct dba *dba, struct mpa - mlink->next = mlink_dest->next; - mlink_dest->next = mpage->mlinks; - mpage->mlinks = NULL; -+ goto nextpage; - } -- goto nextpage; -- } else if (meta != NULL && meta->macroset == MACROSET_MDOC) { -+ meta->macroset = MACROSET_NONE; -+ } -+ if (meta != NULL && meta->macroset == MACROSET_MDOC) { - mpage->form = FORM_SRC; - mpage->sec = meta->msec; - mpage->sec = mandoc_strdup( -@@ -1208,12 +1261,15 @@ mpages_merge(struct dba *dba, struct mpa - } - - assert(mpage->desc == NULL); -- if (meta == NULL) { -- mpage->form = FORM_CAT; -+ if (meta == NULL || meta->sodest != NULL) { - mpage->sec = mandoc_strdup(mlink->dsec); - mpage->arch = mandoc_strdup(mlink->arch); - mpage->title = mandoc_strdup(mlink->name); -- parse_cat(mpage, fd); -+ if (meta == NULL) { -+ mpage->form = FORM_CAT; -+ parse_cat(mpage, fd); -+ } else -+ mpage->form = FORM_SRC; - } else if (meta->macroset == MACROSET_MDOC) - parse_mdoc(mpage, meta, meta->first); - else -@@ -2245,7 +2301,6 @@ set_basedir(const char *targetdir, int r - 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. -@@ -2254,8 +2309,8 @@ set_basedir(const char *targetdir, int r - * 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))) { -+ if (getcwd_status == 0) { -+ if (getcwd(startdir, sizeof(startdir)) == NULL) { - getcwd_status = 2; - (void)strlcpy(startdir, strerror(errno), - sizeof(startdir)); -@@ -2268,19 +2323,20 @@ set_basedir(const char *targetdir, int r - * Do not use it any longer, not even for messages. - */ - *basedir = '\0'; -+ basedir_len = 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) { -+ if (chdir_status && *targetdir != '/') { -+ if (getcwd_status == 2) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "getcwd: %s", startdir); - return 0; - } -- if (-1 == chdir(startdir)) { -+ if (chdir(startdir) == -1) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "&chdir %s", startdir); - return 0; -@@ -2292,29 +2348,33 @@ set_basedir(const char *targetdir, int r - * pathname and append a trailing slash, such that - * we can reliably check whether files are inside. - */ -- if (NULL == realpath(targetdir, basedir)) { -+ if (realpath(targetdir, basedir) == NULL) { - if (report_baddir || errno != ENOENT) { - exitcode = (int)MANDOCLEVEL_BADARG; - say("", "&%s: realpath", targetdir); - } -+ *basedir = '\0'; - return 0; -- } else if (-1 == chdir(basedir)) { -+ } else if (chdir(basedir) == -1) { - if (report_baddir || errno != ENOENT) { - exitcode = (int)MANDOCLEVEL_BADARG; - say("", "&chdir"); - } -+ *basedir = '\0'; - return 0; - } - chdir_status = 1; -- cp = strchr(basedir, '\0'); -- if ('/' != cp[-1]) { -- if (cp - basedir >= PATH_MAX - 1) { -+ basedir_len = strlen(basedir); -+ if (basedir[basedir_len - 1] != '/') { -+ if (basedir_len >= PATH_MAX - 1) { - exitcode = (int)MANDOCLEVEL_SYSERR; - say("", "Filename too long"); -+ *basedir = '\0'; -+ basedir_len = 0; - return 0; - } -- *cp++ = '/'; -- *cp = '\0'; -+ basedir[basedir_len++] = '/'; -+ basedir[basedir_len] = '\0'; - } - return 1; - } -@@ -2325,15 +2385,15 @@ say(const char *file, const char *format - va_list ap; - int use_errno; - -- if ('\0' != *basedir) -+ if (*basedir != '\0') - fprintf(stderr, "%s", basedir); -- if ('\0' != *basedir && '\0' != *file) -+ if (*basedir != '\0' && *file != '\0') - fputc('/', stderr); -- if ('\0' != *file) -+ if (*file != '\0') - fprintf(stderr, "%s", file); - - use_errno = 1; -- if (NULL != format) { -+ if (format != NULL) { - switch (*format) { - case '&': - format++; -@@ -2346,15 +2406,15 @@ say(const char *file, const char *format - break; - } - } -- if (NULL != format) { -- if ('\0' != *basedir || '\0' != *file) -+ if (format != NULL) { -+ if (*basedir != '\0' || *file != '\0') - fputs(": ", stderr); - va_start(ap, format); - vfprintf(stderr, format, ap); - va_end(ap); - } - if (use_errno) { -- if ('\0' != *basedir || '\0' != *file || NULL != format) -+ if (*basedir != '\0' || *file != '\0' || format != NULL) - fputs(": ", stderr); - perror(NULL); - } else ---- a/manpath.c -+++ b/manpath.c -@@ -1,6 +1,6 @@ - /* $Id: manpath.c,v 1.37 2018/11/22 11:30:23 schwarze Exp $ */ - /* -- * Copyright (c) 2011,2014,2015,2017,2018 Ingo Schwarze -+ * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze - * Copyright (c) 2011 Kristaps Dzonsons - * - * Permission to use, copy, modify, and distribute this software for any -@@ -21,20 +21,19 @@ - #include - - #include --#if HAVE_ERR --#include --#endif -+#include - #include - #include - #include - #include - - #include "mandoc_aux.h" -+#include "mandoc.h" - #include "manconf.h" - - static void manconf_file(struct manconf *, const char *); --static void manpath_add(struct manpaths *, const char *, int); --static void manpath_parseline(struct manpaths *, char *, int); -+static void manpath_add(struct manpaths *, const char *, char); -+static void manpath_parseline(struct manpaths *, char *, char); - - - void -@@ -44,11 +43,11 @@ manconf_parse(struct manconf *conf, cons - char *insert; - - /* Always prepend -m. */ -- manpath_parseline(&conf->manpath, auxp, 1); -+ manpath_parseline(&conf->manpath, auxp, 'm'); - - /* If -M is given, it overrides everything else. */ - if (NULL != defp) { -- manpath_parseline(&conf->manpath, defp, 1); -+ manpath_parseline(&conf->manpath, defp, 'M'); - return; - } - -@@ -66,13 +65,13 @@ manconf_parse(struct manconf *conf, cons - /* Prepend man.conf(5) to MANPATH. */ - if (':' == defp[0]) { - manconf_file(conf, file); -- manpath_parseline(&conf->manpath, defp, 0); -+ manpath_parseline(&conf->manpath, defp, '\0'); - return; - } - - /* Append man.conf(5) to MANPATH. */ - if (':' == defp[strlen(defp) - 1]) { -- manpath_parseline(&conf->manpath, defp, 0); -+ manpath_parseline(&conf->manpath, defp, '\0'); - manconf_file(conf, file); - return; - } -@@ -81,28 +80,28 @@ manconf_parse(struct manconf *conf, cons - insert = strstr(defp, "::"); - if (NULL != insert) { - *insert++ = '\0'; -- manpath_parseline(&conf->manpath, defp, 0); -+ manpath_parseline(&conf->manpath, defp, '\0'); - manconf_file(conf, file); -- manpath_parseline(&conf->manpath, insert + 1, 0); -+ manpath_parseline(&conf->manpath, insert + 1, '\0'); - return; - } - - /* MANPATH overrides man.conf(5) completely. */ -- manpath_parseline(&conf->manpath, defp, 0); -+ manpath_parseline(&conf->manpath, defp, '\0'); - } - - void - manpath_base(struct manpaths *dirs) - { - char path_base[] = MANPATH_BASE; -- manpath_parseline(dirs, path_base, 0); -+ manpath_parseline(dirs, path_base, '\0'); - } - - /* - * Parse a FULL pathname from a colon-separated list of arrays. - */ - static void --manpath_parseline(struct manpaths *dirs, char *path, int complain) -+manpath_parseline(struct manpaths *dirs, char *path, char option) - { - char *dir; - -@@ -110,7 +109,7 @@ manpath_parseline(struct manpaths *dirs, - return; - - for (dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) -- manpath_add(dirs, dir, complain); -+ manpath_add(dirs, dir, option); - } - - /* -@@ -118,33 +117,32 @@ manpath_parseline(struct manpaths *dirs, - * Grow the array one-by-one for simplicity's sake. - */ - static void --manpath_add(struct manpaths *dirs, const char *dir, int complain) -+manpath_add(struct manpaths *dirs, const char *dir, char option) - { - char buf[PATH_MAX]; - struct stat sb; - char *cp; - size_t i; - -- if (NULL == (cp = realpath(dir, buf))) { -- if (complain) -- warn("manpath: %s", dir); -- return; -- } -+ if ((cp = realpath(dir, buf)) == NULL) -+ goto fail; - - for (i = 0; i < dirs->sz; i++) -- if (0 == strcmp(dirs->paths[i], dir)) -+ if (strcmp(dirs->paths[i], dir) == 0) - return; - -- if (stat(cp, &sb) == -1) { -- if (complain) -- warn("manpath: %s", dir); -- return; -- } -+ if (stat(cp, &sb) == -1) -+ goto fail; - - dirs->paths = mandoc_reallocarray(dirs->paths, -- dirs->sz + 1, sizeof(char *)); -- -+ dirs->sz + 1, sizeof(*dirs->paths)); - dirs->paths[dirs->sz++] = mandoc_strdup(cp); -+ return; -+ -+fail: -+ if (option != '\0') -+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, -+ "-%c %s: %s", option, dir, strerror(errno)); - } - - void -@@ -165,7 +163,7 @@ manconf_free(struct manconf *conf) - static void - manconf_file(struct manconf *conf, const char *file) - { -- const char *const toks[] = { "manpath", "output", "_whatdb" }; -+ const char *const toks[] = { "manpath", "output" }; - char manpath_default[] = MANPATH_DEFAULT; - - FILE *stream; -@@ -202,15 +200,8 @@ manconf_file(struct manconf *conf, const - } - - switch (tok) { -- case 2: /* _whatdb */ -- while (ep > cp && ep[-1] != '/') -- ep--; -- if (ep == cp) -- continue; -- *ep = '\0'; -- /* FALLTHROUGH */ - case 0: /* manpath */ -- manpath_add(&conf->manpath, cp, 0); -+ manpath_add(&conf->manpath, cp, '\0'); - *manpath_default = '\0'; - break; - case 1: /* output */ -@@ -225,7 +216,7 @@ manconf_file(struct manconf *conf, const - - out: - if (*manpath_default != '\0') -- manpath_parseline(&conf->manpath, manpath_default, 0); -+ manpath_parseline(&conf->manpath, manpath_default, '\0'); - } - - int -@@ -235,14 +226,15 @@ manconf_output(struct manoutput *conf, c - "includes", "man", "paper", "style", "indent", "width", - "tag", "fragment", "mdoc", "noval", "toc" - }; -+ const size_t ntoks = sizeof(toks) / sizeof(toks[0]); - - const char *errstr; - char *oldval; - size_t len, tok; - -- for (tok = 0; tok < sizeof(toks)/sizeof(toks[0]); tok++) { -+ for (tok = 0; tok < ntoks; tok++) { - len = strlen(toks[tok]); -- if ( ! strncmp(cp, toks[tok], len) && -+ if (strncmp(cp, toks[tok], len) == 0 && - strchr(" = ", cp[len]) != NULL) { - cp += len; - if (*cp == '=') -@@ -254,11 +246,11 @@ manconf_output(struct manoutput *conf, c - } - - if (tok < 6 && *cp == '\0') { -- warnx("-O %s=?: Missing argument value", toks[tok]); -+ mandoc_msg(MANDOCERR_BADVAL_MISS, 0, 0, "-O %s=?", toks[tok]); - return -1; - } -- if (tok > 6 && *cp != '\0') { -- warnx("-O %s: Does not take a value: %s", toks[tok], cp); -+ if (tok > 6 && tok < ntoks && *cp != '\0') { -+ mandoc_msg(MANDOCERR_BADVAL, 0, 0, "-O %s=%s", toks[tok], cp); - return -1; - } - -@@ -299,7 +291,8 @@ manconf_output(struct manoutput *conf, c - conf->indent = strtonum(cp, 0, 1000, &errstr); - if (errstr == NULL) - return 0; -- warnx("-O indent=%s is %s", cp, errstr); -+ mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0, -+ "-O indent=%s is %s", cp, errstr); - return -1; - case 5: - if (conf->width) { -@@ -309,7 +302,8 @@ manconf_output(struct manoutput *conf, c - conf->width = strtonum(cp, 1, 1000, &errstr); - if (errstr == NULL) - return 0; -- warnx("-O width=%s is %s", cp, errstr); -+ mandoc_msg(MANDOCERR_BADVAL_BAD, 0, 0, -+ "-O width=%s is %s", cp, errstr); - return -1; - case 6: - if (conf->tag != NULL) { -@@ -331,13 +325,16 @@ manconf_output(struct manoutput *conf, c - conf->toc = 1; - return 0; - default: -- if (fromfile) -- warnx("-O %s: Bad argument", cp); -+ mandoc_msg(MANDOCERR_BADARG_BAD, 0, 0, "-O %s", cp); -+ return -1; -+ } -+ if (fromfile) { -+ free(oldval); -+ return 0; -+ } else { -+ mandoc_msg(MANDOCERR_BADVAL_DUPE, 0, 0, -+ "-O %s=%s: already set to %s", toks[tok], cp, oldval); -+ free(oldval); - return -1; - } -- if (fromfile == 0) -- warnx("-O %s=%s: Option already set to %s", -- toks[tok], cp, oldval); -- free(oldval); -- return -1; - } ---- a/mansearch.c -+++ b/mansearch.c -@@ -191,7 +191,7 @@ mansearch(const struct mansearch *search - mpage->file, R_OK) == -1) { - warn("%s", mpage->file); - warnx("outdated mandoc.db contains " -- "bogus %s entry, run makewhatis %s", -+ "bogus %s entry, run makewhatis %s", - page->file + 1, paths->paths[i]); - free(mpage->file); - free(rp); -@@ -199,6 +199,7 @@ mansearch(const struct mansearch *search - } - mpage->names = buildnames(page); - mpage->output = buildoutput(outkey, page); -+ mpage->bits = search->firstmatch ? rp->bits : 0; - mpage->ipath = i; - mpage->sec = *page->sect - '0'; - if (mpage->sec < 0 || mpage->sec > 9) -@@ -294,8 +295,10 @@ manmerge_term(struct expr *e, struct oha - break; - slot = ohash_lookup_memory(htab, - (char *)&res, sizeof(res.page), res.page); -- if ((rp = ohash_find(htab, slot)) != NULL) -+ if ((rp = ohash_find(htab, slot)) != NULL) { -+ rp->bits |= res.bits; - continue; -+ } - rp = mandoc_malloc(sizeof(*rp)); - *rp = res; - ohash_insert(htab, slot, rp); -@@ -408,7 +411,8 @@ manpage_compare(const void *vp1, const v - - mp1 = vp1; - mp2 = vp2; -- if ((diff = mp1->sec - mp2->sec)) -+ if ((diff = mp2->bits - mp1->bits) || -+ (diff = mp1->sec - mp2->sec)) - return diff; - - /* Fall back to alphabetic ordering of names. */ ---- a/mansearch.h -+++ b/mansearch.h -@@ -92,6 +92,7 @@ struct manpage { - char *file; /* to be prefixed by manpath */ - char *names; /* a list of names with sections */ - char *output; /* user-defined additional output */ -+ uint64_t bits; /* name type mask */ - size_t ipath; /* number of the manpath */ - int sec; /* section number, 10 means invalid */ - enum form form; ---- a/mdoc.7 -+++ b/mdoc.7 -@@ -1,7 +1,7 @@ - .\" $Id: mdoc.7,v 1.276 2019/02/07 15:45:53 schwarze Exp $ - .\" - .\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons --.\" Copyright (c) 2010, 2011, 2013-2018 Ingo Schwarze -+.\" Copyright (c) 2010, 2011, 2013-2020 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 -@@ -449,6 +449,7 @@ in the alphabetical - .It Ic \&Ss Ta subsection header (one line) - .It Ic \&Sx Ta internal cross reference to a section or subsection - .It Ic \&Xr Ta cross reference to another manual page: Ar name section -+.It Ic \&Tg Ta tag the definition of a Ar term Pq <= 1 arguments - .It Ic \&Pp Ta start a text paragraph (no arguments) - .El - .Ss Displays and lists -@@ -596,6 +597,13 @@ block. - Book or journal page number of an - .Ic \&Rs - block. -+Conventionally, the argument starts with -+.Ql p.\& -+for a single page or -+.Ql pp.\& -+for a range of pages, for example: -+.Pp -+.Dl .%P pp. 42\e(en47 - .It Ic \&%Q Ar name - Institutional author (school, government, etc.) of an - .Ic \&Rs -@@ -1156,17 +1164,19 @@ declarations. - This practise is discouraged. - .It Ic \&Cm Ar keyword ... - Command modifiers. --Typically used for fixed strings passed as arguments, unless -+Typically used for fixed strings passed as arguments to interactive -+commands, to commands in interpreted scripts, or to configuration -+file directives, unless - .Ic \&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" -+.Dl ".Ic set Fl o Cm vi" -+.Dl ".Ic lookup Cm file bind" -+.Dl ".Ic permit Ar identity Op Cm as Ar target" - .It Ic \&D1 Ar line - One-line indented display. - This is formatted by the default rules and is useful for simple indented -@@ -1677,10 +1687,10 @@ This macro is not implemented in - .Xr mandoc 1 . - It was used to include the contents of a (header) file literally. - .It Ic \&Ic Ar keyword ... --Designate an internal or interactive command. --This is similar to --.Ic \&Cm --but used for instructions rather than values. -+Internal or interactive command, or configuration instruction -+in a configuration file. -+See also -+.Ic \&Cm . - .Pp - Examples: - .Dl \&.Ic :wq -@@ -2539,6 +2549,49 @@ Table cell separator in - .Ic \&Bl Fl column - lists; can only be used below - .Ic \&It . -+.It Ic \&Tg Op Ar term -+Announce that the next input line starts a definition of the -+.Ar term . -+This macro must appear alone on its own input line. -+The argument defaults to the first argument of the first macro -+on the next line. -+The argument may not contain whitespace characters, not even when it is quoted. -+This macro is a -+.Xr mandoc 1 -+extension and is typically ignored by other formatters. -+.Pp -+When viewing terminal output with -+.Xr less 1 , -+the interactive -+.Ic :t -+command can be used to go to the definition of the -+.Ar term -+as described for the -+.Ev MANPAGER -+variable in -+.Xr man 1 ; -+when producing HTML output, a fragment identifier -+.Pq Ic id No attribute -+is generated, to be used for deep linking to this place of the document. -+.Pp -+In most cases, adding a -+.Ic \&Tg -+macro would be redundant because -+.Xr mandoc 1 -+is able to automatically tag most definitions. -+This macro is intended for cases where automatic tagging of a -+.Ar term -+is unsatisfactory, for example if a definition is not tagged -+automatically (false negative) or if places are tagged that do -+not define the -+.Ar term -+(false positives). -+When there is at least one -+.Ic \&Tg -+macro for a -+.Ar term , -+no other places are automatically marked as definitions of that -+.Ar term . - .It Ic \&Tn Ar word ... - Supported only for compatibility, do not use this in new manuals. - Even though the macro name -@@ -2903,6 +2956,7 @@ then the macro accepts an arbitrary numb - .It Ic \&St Ta \&No Ta Yes Ta 1 - .It Ic \&Sx Ta Yes Ta Yes Ta >0 - .It Ic \&Sy Ta Yes Ta Yes Ta >0 -+.It Ic \&Tg Ta \&No Ta \&No Ta <2 - .It Ic \&Tn Ta Yes Ta Yes Ta >0 - .It Ic \&Ud Ta \&No Ta \&No Ta 0 - .It Ic \&Ux Ta Yes Ta Yes Ta n -@@ -2969,7 +3023,7 @@ exclamation mark - Note that even a period preceded by a backslash - .Pq Sq \e.\& - gets this special handling; use --.Sq \e&. -+.Sq \e&.\& - to prevent that. - .Pp - Many in-line macros interrupt their scope when they encounter -@@ -2996,6 +3050,13 @@ in the same way as a plain - .Sq \&| - character. - Using this predefined string is not recommended in new manuals. -+.Pp -+Appending a zero-width space -+.Pq Sq \e& -+to the end of an input line is also useful to prevent the interpretation -+of a trailing period, exclamation or question mark as the end of a -+sentence, for example when an abbreviation happens to occur -+at the end of a text or macro input line. - .Ss Font handling - In - .Nm ---- a/mdoc_argv.c -+++ b/mdoc_argv.c -@@ -1,7 +1,7 @@ - /* $Id: mdoc_argv.c,v 1.119 2018/12/21 17:15:19 schwarze Exp $ */ - /* - * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons -- * Copyright (c) 2012, 2014-2018 Ingo Schwarze -+ * Copyright (c) 2012, 2014-2019 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 -@@ -454,6 +454,7 @@ args(struct roff_man *mdoc, int line, in - mandoc_msg(MANDOCERR_ARG_QUOTE, line, *pos, NULL); - mdoc->flags &= ~MDOC_PHRASELIT; - } -+ mdoc->flags &= ~MDOC_PHRASEQL; - return ARGS_EOLN; - } - ---- a/mdoc_html.c -+++ b/mdoc_html.c -@@ -1,7 +1,7 @@ - /* $Id: mdoc_html.c,v 1.328 2019/03/01 10:57:18 schwarze Exp $ */ - /* - * Copyright (c) 2008-2011, 2014 Kristaps Dzonsons -- * Copyright (c) 2014-2019 Ingo Schwarze -+ * Copyright (c) 2014-2020 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 -@@ -115,6 +115,7 @@ static int mdoc_ss_pre(MDOC_ARGS); - static int mdoc_st_pre(MDOC_ARGS); - static int mdoc_sx_pre(MDOC_ARGS); - static int mdoc_sy_pre(MDOC_ARGS); -+static int mdoc_tg_pre(MDOC_ARGS); - static int mdoc_va_pre(MDOC_ARGS); - static int mdoc_vt_pre(MDOC_ARGS); - static int mdoc_xr_pre(MDOC_ARGS); -@@ -241,6 +242,7 @@ static const struct mdoc_html_act mdoc_h - {mdoc__x_pre, mdoc__x_post}, /* %Q */ - {mdoc__x_pre, mdoc__x_post}, /* %U */ - {NULL, NULL}, /* Ta */ -+ {mdoc_tg_pre, NULL}, /* Tg */ - }; - - -@@ -351,26 +353,34 @@ print_mdoc_node(MDOC_ARGS) - if (n->type == ROFFT_COMMENT || n->flags & NODE_NOPRT) - return; - -- html_fillmode(h, n->flags & NODE_NOFILL ? ROFF_nf : ROFF_fi); -+ if (n->flags & NODE_NOFILL) { -+ html_fillmode(h, ROFF_nf); -+ if (n->flags & NODE_LINE) -+ print_endline(h); -+ } else -+ html_fillmode(h, ROFF_fi); - - child = 1; - n->flags &= ~NODE_ENDED; - switch (n->type) { - case ROFFT_TEXT: -+ if (n->flags & NODE_LINE) { -+ switch (*n->string) { -+ case '\0': -+ h->col = 1; -+ print_endline(h); -+ return; -+ case ' ': -+ if ((h->flags & HTML_NONEWLINE) == 0 && -+ (n->flags & NODE_NOFILL) == 0) -+ print_otag(h, TAG_BR, ""); -+ break; -+ default: -+ break; -+ } -+ } - t = h->tag; - t->refcnt++; -- -- /* 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 == ' ' && n->flags & NODE_LINE &&
--		    (h->flags & HTML_NONEWLINE) == 0 &&
--		    (n->flags & NODE_NOFILL) == 0)
--			print_otag(h, TAG_BR, "");
- 		if (NODE_DELIMC & n->flags)
- 			h->flags |= HTML_NOSPACE;
- 		print_text(h, n->string);
-@@ -439,12 +449,6 @@ print_mdoc_node(MDOC_ARGS)
- 			n->body->flags |= NODE_ENDED;
- 		break;
- 	}
--
--	if (n->flags & NODE_NOFILL &&
--	    (n->next == NULL || n->next->flags & NODE_LINE)) {
--		h->col++;
--		print_endline(h);
--	}
- }
- 
- static void
-@@ -653,7 +657,6 @@ mdoc_nd_pre(MDOC_ARGS)
- {
- 	switch (n->type) {
- 	case ROFFT_BLOCK:
--		html_close_paragraph(h);
- 		return 1;
- 	case ROFFT_HEAD:
- 		return 0;
-@@ -663,8 +666,7 @@ mdoc_nd_pre(MDOC_ARGS)
- 		abort();
- 	}
- 	print_text(h, "\\(em");
--	/* Cannot use TAG_SPAN because it may contain blocks. */
--	print_otag(h, TAG_DIV, "c", "Nd");
-+	print_otag(h, TAG_SPAN, "c", "Nd");
- 	return 1;
- }
- 
-@@ -722,6 +724,16 @@ mdoc_xr_pre(MDOC_ARGS)
- }
- 
- static int
-+mdoc_tg_pre(MDOC_ARGS)
-+{
-+	char	*id;
-+
-+	if ((id = html_make_id(n, 1)) != NULL)
-+		print_otag(h, TAG_MARK, "i", id);
-+	return 0;
-+}
-+
-+static int
- mdoc_ns_pre(MDOC_ARGS)
- {
- 
-@@ -1272,7 +1284,11 @@ mdoc_skip_pre(MDOC_ARGS)
- static int
- mdoc_pp_pre(MDOC_ARGS)
- {
--	if ((n->flags & NODE_NOFILL) == 0) {
-+	if (n->flags & NODE_NOFILL) {
-+		print_endline(h);
-+		h->col = 1;
-+		print_endline(h);
-+	} else {
- 		html_close_paragraph(h);
- 		print_otag(h, TAG_P, "c", "Pp");
- 	}
-@@ -1700,7 +1716,7 @@ mdoc_quote_pre(MDOC_ARGS)
- 		/*
- 		 * Give up on semantic markup for now.
- 		 * We cannot use TAG_SPAN because .Oo may contain blocks.
--		 * We cannot use TAG_IDIV because we might be in a
-+		 * We cannot use TAG_DIV because we might be in a
- 		 * phrasing context (like .Dl or .Pp); we cannot
- 		 * close out a .Pp at this point either because
- 		 * that would break the line.
-@@ -1715,9 +1731,11 @@ mdoc_quote_pre(MDOC_ARGS)
- 		break;
- 	case MDOC_Do:
- 	case MDOC_Dq:
-+		print_text(h, "\\(lq");
-+		break;
- 	case MDOC_Qo:
- 	case MDOC_Qq:
--		print_text(h, "\\(lq");
-+		print_text(h, "\"");
- 		break;
- 	case MDOC_Po:
- 	case MDOC_Pq:
-@@ -1773,12 +1791,14 @@ mdoc_quote_post(MDOC_ARGS)
- 		else
- 			print_text(h, n->norm->Es->child->next->string);
- 		break;
--	case MDOC_Qo:
--	case MDOC_Qq:
- 	case MDOC_Do:
- 	case MDOC_Dq:
- 		print_text(h, "\\(rq");
- 		break;
-+	case MDOC_Qo:
-+	case MDOC_Qq:
-+		print_text(h, "\"");
-+		break;
- 	case MDOC_Po:
- 	case MDOC_Pq:
- 		print_text(h, ")");
---- a/mdoc_macro.c
-+++ b/mdoc_macro.c
-@@ -1,7 +1,7 @@
- /*	$Id: mdoc_macro.c,v 1.232 2019/01/07 07:26:29 schwarze Exp $ */
- /*
-  * Copyright (c) 2008-2012 Kristaps Dzonsons 
-- * Copyright (c) 2010, 2012-2019 Ingo Schwarze 
-+ * Copyright (c) 2010, 2012-2020 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
-@@ -61,7 +61,7 @@ static	void		rew_pending(struct roff_man
- 				const struct roff_node *);
- 
- static const struct mdoc_macro mdoc_macros[MDOC_MAX - MDOC_Dd] = {
--	{ in_line_eoln, MDOC_PROLOGUE }, /* Dd */
-+	{ in_line_eoln, MDOC_PROLOGUE | MDOC_JOIN }, /* Dd */
- 	{ in_line_eoln, MDOC_PROLOGUE }, /* Dt */
- 	{ in_line_eoln, MDOC_PROLOGUE }, /* Os */
- 	{ blk_full, MDOC_PARSED | MDOC_JOIN }, /* Sh */
-@@ -200,6 +200,7 @@ static const struct mdoc_macro mdoc_macr
- 	{ in_line_eoln, MDOC_JOIN }, /* %Q */
- 	{ in_line_eoln, 0 }, /* %U */
- 	{ phrase_ta, MDOC_CALLABLE | MDOC_PARSED | MDOC_JOIN }, /* Ta */
-+	{ in_line_eoln, 0 }, /* Tg */
- };
- 
- 
---- a/mdoc_man.c
-+++ b/mdoc_man.c
-@@ -262,6 +262,7 @@ static const struct mdoc_man_act mdoc_ma
- 	{ NULL, NULL, post_percent, NULL, NULL }, /* %Q */
- 	{ NULL, NULL, post_percent, NULL, NULL }, /* %U */
- 	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
-+	{ NULL, NULL, NULL, NULL, NULL }, /* Tg */
- };
- static const struct mdoc_man_act *mdoc_man_act(enum roff_tok);
- 
---- a/mdoc_markdown.c
-+++ b/mdoc_markdown.c
-@@ -226,6 +226,7 @@ static	const struct md_act md_acts[MDOC_
- 	{ NULL, NULL, md_post_pc, NULL, NULL }, /* %Q */
- 	{ NULL, md_pre_Lk, md_post_pc, NULL, NULL }, /* %U */
- 	{ NULL, NULL, NULL, NULL, NULL }, /* Ta */
-+	{ NULL, NULL, NULL, NULL, NULL }, /* Tg */
- };
- static const struct md_act *md_act(enum roff_tok);
- 
-@@ -1290,7 +1291,7 @@ md_post_It(struct roff_node *n)
- 		while ((n = n->prev) != NULL && n->type != ROFFT_HEAD)
- 			i++;
- 
--		/* 
-+		/*
- 		 * If a width was specified for this column,
- 		 * subtract what printed, and
- 		 * add the same spacing as in mdoc_term.c.
---- a/mdoc_state.c
-+++ b/mdoc_state.c
-@@ -157,6 +157,7 @@ static	const state_handler state_handler
- 	NULL,		/* %Q */
- 	NULL,		/* %U */
- 	NULL,		/* Ta */
-+	NULL,		/* Tg */
- };
- 
- 
---- a/mdoc_term.c
-+++ b/mdoc_term.c
-@@ -1,7 +1,7 @@
- /*	$Id: mdoc_term.c,v 1.372 2019/01/04 03:39:01 schwarze Exp $ */
- /*
-  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
-- * Copyright (c) 2010, 2012-2019 Ingo Schwarze 
-+ * Copyright (c) 2010, 2012-2020 Ingo Schwarze 
-  * Copyright (c) 2013 Franco Fichtner 
-  *
-  * Permission to use, copy, modify, and distribute this software for any
-@@ -119,6 +119,7 @@ static	int	  termp_pp_pre(DECL_ARGS);
- static	int	  termp_ss_pre(DECL_ARGS);
- static	int	  termp_sy_pre(DECL_ARGS);
- static	int	  termp_tag_pre(DECL_ARGS);
-+static	int	  termp_tg_pre(DECL_ARGS);
- static	int	  termp_under_pre(DECL_ARGS);
- static	int	  termp_vt_pre(DECL_ARGS);
- static	int	  termp_xr_pre(DECL_ARGS);
-@@ -245,15 +246,16 @@ static const struct mdoc_term_act mdoc_t
- 	{ NULL, termp____post }, /* %Q */
- 	{ NULL, termp____post }, /* %U */
- 	{ NULL, NULL }, /* Ta */
-+	{ termp_tg_pre, NULL }, /* Tg */
- };
- 
--static	int	 fn_prio;
-+static	int	 fn_prio = TAG_STRONG;
- 
- 
- void
- terminal_mdoc(void *arg, const struct roff_meta *mdoc)
- {
--	struct roff_node	*n;
-+	struct roff_node	*n, *nn;
- 	struct termp		*p;
- 	size_t			 save_defindent;
- 
-@@ -265,16 +267,20 @@ terminal_mdoc(void *arg, const struct ro
- 
- 	n = mdoc->first->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,
--					    mdoc, n->child->next->child);
--				term_newln(p);
-+		for (nn = NULL; n != NULL; n = n->next) {
-+			if (n->tok != MDOC_Sh)
-+				continue;
-+			if (n->sec == SEC_SYNOPSIS)
- 				break;
--			}
--			n = n->next;
-+			if (nn == NULL && n->sec == SEC_NAME)
-+				nn = n;
- 		}
-+		if (n == NULL)
-+			n = nn;
-+		p->flags |= TERMP_NOSPACE;
-+		if (n != NULL && (n = n->child->next->child) != NULL)
-+			print_mdoc_nodelist(p, NULL, mdoc, n);
-+		term_newln(p);
- 	} else {
- 		save_defindent = p->defindent;
- 		if (p->defindent == 0)
-@@ -352,6 +358,7 @@ print_mdoc_node(DECL_ARGS)
- 	 * produce output.  Note that some pre-handlers do so.
- 	 */
- 
-+	act = NULL;
- 	switch (n->type) {
- 	case ROFFT_TEXT:
- 		if (n->flags & NODE_LINE) {
-@@ -1287,7 +1294,7 @@ termp_sh_pre(DECL_ARGS)
- 		term_tab_set(p, ".5i");
- 		switch (n->sec) {
- 		case SEC_DESCRIPTION:
--			fn_prio = 0;
-+			fn_prio = TAG_STRONG;
- 			break;
- 		case SEC_AUTHORS:
- 			p->flags &= ~(TERMP_SPLIT|TERMP_NOSPLIT);
-@@ -1376,7 +1383,7 @@ termp_fn_pre(DECL_ARGS)
- 	term_fontpop(p);
- 
- 	if (n->sec == SEC_DESCRIPTION || n->sec == SEC_CUSTOM)
--		tag_put(n->string, ++fn_prio, p->line);
-+		tag_put(n->string, fn_prio++, p->line);
- 
- 	if (pretty) {
- 		term_flushln(p);
-@@ -1607,7 +1614,7 @@ termp_in_post(DECL_ARGS)
- static int
- termp_pp_pre(DECL_ARGS)
- {
--	fn_prio = 0;
-+	fn_prio = TAG_STRONG;
- 	term_vspace(p);
- 	return 0;
- }
-@@ -2032,7 +2039,7 @@ termp_em_pre(DECL_ARGS)
- {
- 	if (n->child != NULL &&
- 	    n->child->type == ROFFT_TEXT)
--		tag_put(n->child->string, 0, p->line);
-+		tag_put(n->child->string, TAG_FALLBACK, p->line);
- 	term_fontpush(p, TERMFONT_UNDER);
- 	return 1;
- }
-@@ -2042,7 +2049,7 @@ termp_sy_pre(DECL_ARGS)
- {
- 	if (n->child != NULL &&
- 	    n->child->type == ROFFT_TEXT)
--		tag_put(n->child->string, 0, p->line);
-+		tag_put(n->child->string, TAG_FALLBACK, p->line);
- 	term_fontpush(p, TERMFONT_BOLD);
- 	return 1;
- }
-@@ -2055,7 +2062,7 @@ termp_er_pre(DECL_ARGS)
- 	    (n->parent->tok == MDOC_It ||
- 	     (n->parent->tok == MDOC_Bq &&
- 	      n->parent->parent->parent->tok == MDOC_It)))
--		tag_put(n->child->string, 1, p->line);
-+		tag_put(n->child->string, TAG_STRONG, p->line);
- 	return 1;
- }
- 
-@@ -2072,11 +2079,18 @@ termp_tag_pre(DECL_ARGS)
- 	     (n->parent->tok == MDOC_Xo &&
- 	      n->parent->parent->prev == NULL &&
- 	      n->parent->parent->parent->tok == MDOC_It)))
--		tag_put(n->child->string, 1, p->line);
-+		tag_put(n->child->string, TAG_STRONG, p->line);
- 	return 1;
- }
- 
- static int
-+termp_tg_pre(DECL_ARGS)
-+{
-+	tag_put(n->child->string, TAG_MANUAL, p->line);
-+	return 0;
-+}
-+
-+static int
- termp_abort_pre(DECL_ARGS)
- {
- 	abort();
---- a/mdoc_validate.c
-+++ b/mdoc_validate.c
-@@ -1,7 +1,7 @@
- /*	$Id: mdoc_validate.c,v 1.371 2019/03/04 13:01:57 schwarze Exp $ */
- /*
-  * Copyright (c) 2008-2012 Kristaps Dzonsons 
-- * Copyright (c) 2010-2019 Ingo Schwarze 
-+ * Copyright (c) 2010-2020 Ingo Schwarze 
-  * Copyright (c) 2010 Joerg Sonnenberger 
-  *
-  * Permission to use, copy, modify, and distribute this software for any
-@@ -64,7 +64,7 @@ static	size_t		macro2len(enum roff_tok);
- static	void	 rewrite_macro2len(struct roff_man *, char **);
- static	int	 similar(const char *, const char *);
- 
--static	void	 post_abort(POST_ARGS);
-+static	void	 post_abort(POST_ARGS) __attribute__((__noreturn__));
- static	void	 post_an(POST_ARGS);
- static	void	 post_an_norm(POST_ARGS);
- static	void	 post_at(POST_ARGS);
-@@ -113,6 +113,7 @@ static	void	 post_sm(POST_ARGS);
- static	void	 post_st(POST_ARGS);
- static	void	 post_std(POST_ARGS);
- static	void	 post_sx(POST_ARGS);
-+static	void	 post_tg(POST_ARGS);
- static	void	 post_useless(POST_ARGS);
- static	void	 post_xr(POST_ARGS);
- static	void	 post_xx(POST_ARGS);
-@@ -238,6 +239,7 @@ static	const v_post mdoc_valids[MDOC_MAX
- 	NULL,		/* %Q */
- 	NULL,		/* %U */
- 	NULL,		/* Ta */
-+	post_tg,	/* Tg */
- };
- 
- #define	RSORD_MAX 14 /* Number of `Rs' blocks. */
-@@ -1090,6 +1092,41 @@ post_st(POST_ARGS)
- }
- 
- static void
-+post_tg(POST_ARGS)
-+{
-+	struct roff_node	*n, *nch;
-+	size_t			len;
-+
-+	n = mdoc->last;
-+	nch = n->child;
-+	if (nch == NULL && n->next != NULL &&
-+	    n->next->child->type == ROFFT_TEXT) {
-+		mdoc->next = ROFF_NEXT_CHILD;
-+		roff_word_alloc(mdoc, n->line, n->pos, n->next->child->string);
-+		nch = mdoc->last;
-+		nch->flags |= NODE_NOSRC;
-+		mdoc->last = n;
-+	}
-+	if (nch == NULL || *nch->string == '\0') {
-+		mandoc_msg(MANDOCERR_MACRO_EMPTY, n->line, n->pos, "Tg");
-+		roff_node_delete(mdoc, n);
-+		return;
-+	}
-+	len = strcspn(nch->string, " \t");
-+	if (nch->string[len] != '\0')
-+		mandoc_msg(MANDOCERR_TG_SPC, nch->line, nch->pos + len + 1,
-+		    "Tg %s", nch->string);
-+	if (nch->next != NULL) {
-+		mandoc_msg(MANDOCERR_ARG_EXCESS, nch->next->line,
-+		    nch->next->pos, "Tg ... %s", nch->next->string);
-+		while (nch->next != NULL)
-+			roff_node_delete(mdoc, nch->next);
-+	}
-+	if (nch->string[len] != '\0')
-+		roff_node_delete(mdoc, n);
-+}
-+
-+static void
- post_obsolete(POST_ARGS)
- {
- 	struct roff_node *n;
-@@ -1186,11 +1223,17 @@ post_fname(POST_ARGS)
- 	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, n->line, n->pos + pos,
--		    "%s", n->string);
-+	cp = n->string;
-+	if (*cp == '(') {
-+		if (cp[strlen(cp + 1)] == ')')
-+			return;
-+		pos = 0;
-+	} else {
-+		pos = strcspn(cp, "()");
-+		if (cp[pos] == '\0')
-+			return;
-+	}
-+	mandoc_msg(MANDOCERR_FN_PAREN, n->line, n->pos + pos, "%s", cp);
- }
- 
- static void
-@@ -1748,7 +1791,7 @@ post_bl(POST_ARGS)
- 	while (nchild != NULL) {
- 		nnext = nchild->next;
- 		if (nchild->tok == MDOC_It ||
--		    (nchild->tok == MDOC_Sm &&
-+		    ((nchild->tok == MDOC_Sm || nchild->tok == MDOC_Tg) &&
- 		     nnext != NULL && nnext->tok == MDOC_It)) {
- 			nchild = nnext;
- 			continue;
-@@ -1903,8 +1946,7 @@ post_root(POST_ARGS)
- 	/* Add missing prologue data. */
- 
- 	if (mdoc->meta.date == NULL)
--		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
--		    mandoc_normdate(mdoc, NULL, 0, 0);
-+		mdoc->meta.date = mandoc_normdate(NULL, NULL);
- 
- 	if (mdoc->meta.title == NULL) {
- 		mandoc_msg(MANDOCERR_DT_NOTITLE, 0, 0, "EOF");
-@@ -2502,7 +2544,6 @@ static void
- post_dd(POST_ARGS)
- {
- 	struct roff_node *n;
--	char		 *datestr;
- 
- 	n = mdoc->last;
- 	n->flags |= NODE_NOPRT;
-@@ -2519,21 +2560,10 @@ post_dd(POST_ARGS)
- 		mandoc_msg(MANDOCERR_PROLOG_ORDER,
- 		    n->line, n->pos, "Dd after Os");
- 
--	if (n->child == NULL || n->child->string[0] == '\0') {
--		mdoc->meta.date = mdoc->quick ? mandoc_strdup("") :
--		    mandoc_normdate(mdoc, NULL, n->line, n->pos);
--		return;
--	}
--
--	datestr = NULL;
--	deroff(&datestr, n);
--	if (mdoc->quick)
--		mdoc->meta.date = datestr;
--	else {
--		mdoc->meta.date = mandoc_normdate(mdoc,
--		    datestr, n->line, n->pos);
--		free(datestr);
--	}
-+	if (mdoc->quick && n != NULL)
-+		mdoc->meta.date = mandoc_strdup("");
-+	else
-+		mdoc->meta.date = mandoc_normdate(n->child, n);
- }
- 
- static void
---- a/out.c
-+++ b/out.c
-@@ -149,7 +149,7 @@ tblcalc(struct rofftbl *tbl, const struc
- 		gp = &first_group;
- 		for (dp = sp->first; dp != NULL; dp = dp->next) {
- 			icol = dp->layout->col;
--			while (icol > maxcol)
-+			while (maxcol < icol + dp->hspans)
- 				tbl->cols[++maxcol].spacing = SIZE_MAX;
- 			col = tbl->cols + icol;
- 			col->flags |= dp->layout->flags;
-@@ -209,13 +209,25 @@ tblcalc(struct rofftbl *tbl, const struc
- 	}
- 
- 	/*
--	 * Column spacings are needed for span width calculations,
--	 * so set the default values now.
-+	 * The minimum width of columns explicitly specified
-+	 * in the layout is 1n.
- 	 */
- 
--	for (icol = 0; icol <= maxcol; icol++)
--		if (tbl->cols[icol].spacing == SIZE_MAX || icol == maxcol)
--			tbl->cols[icol].spacing = 3;
-+	if (maxcol < sp_first->opts->cols - 1)
-+		maxcol = sp_first->opts->cols - 1;
-+	for (icol = 0; icol <= maxcol; icol++) {
-+		col = tbl->cols + icol;
-+		if (col->width < 1)
-+			col->width = 1;
-+
-+		/*
-+		 * Column spacings are needed for span width
-+		 * calculations, so set the default values now.
-+		 */
-+
-+		if (col->spacing == SIZE_MAX || icol == maxcol)
-+			col->spacing = 3;
-+	}
- 
- 	/*
- 	 * Replace the minimum widths with the missing widths,
---- a/read.c
-+++ b/read.c
-@@ -157,7 +157,7 @@ mparse_buf_r(struct mparse *curp, struct
- 	ln.sz = 256;
- 	ln.buf = mandoc_malloc(ln.sz);
- 	ln.next = NULL;
--	firstln = loop = NULL;
-+	firstln = lastln = loop = NULL;
- 	lnn = curp->line;
- 	pos = 0;
- 	inloop = 0;
-@@ -255,6 +255,8 @@ mparse_buf_r(struct mparse *curp, struct
- 		/* XXX Ugly hack to mark the end of the input. */
- 
- 		if (i == blk.sz || blk.buf[i] == '\0') {
-+			if (pos + 2 > ln.sz)
-+				resize_buf(&ln, 256);
- 			ln.buf[pos++] = '\n';
- 			ln.buf[pos] = '\0';
- 		}
-@@ -429,9 +431,8 @@ read_whole_file(struct mparse *curp, int
- 	int		 gzerrnum, retval;
- 
- 	if (fstat(fd, &st) == -1) {
--		mandoc_msg(MANDOCERR_FILE, 0, 0,
--		    "fstat: %s", strerror(errno));
--		return 0;
-+		mandoc_msg(MANDOCERR_FSTAT, 0, 0, "%s", strerror(errno));
-+		return -1;
- 	}
- 
- 	/*
-@@ -444,13 +445,13 @@ read_whole_file(struct mparse *curp, int
- 	if (curp->gzip == 0 && S_ISREG(st.st_mode)) {
- 		if (st.st_size > 0x7fffffff) {
- 			mandoc_msg(MANDOCERR_TOOLARGE, 0, 0, NULL);
--			return 0;
-+			return -1;
- 		}
- 		*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;
-+			return 0;
- 	}
- 
- 	if (curp->gzip) {
-@@ -462,15 +463,15 @@ read_whole_file(struct mparse *curp, int
- 		 * which this function must not do.
- 		 */
- 		if ((fd = dup(fd)) == -1) {
--			mandoc_msg(MANDOCERR_FILE, 0, 0,
--			    "dup: %s", strerror(errno));
--			return 0;
-+			mandoc_msg(MANDOCERR_DUP, 0, 0,
-+			    "%s", strerror(errno));
-+			return -1;
- 		}
- 		if ((gz = gzdopen(fd, "rb")) == NULL) {
--			mandoc_msg(MANDOCERR_FILE, 0, 0,
--			    "gzdopen: %s", strerror(errno));
-+			mandoc_msg(MANDOCERR_GZDOPEN, 0, 0,
-+			    "%s", strerror(errno));
- 			close(fd);
--			return 0;
-+			return -1;
- 		}
- 	} else
- 		gz = NULL;
-@@ -482,7 +483,7 @@ read_whole_file(struct mparse *curp, int
- 
- 	*with_mmap = 0;
- 	off = 0;
--	retval = 0;
-+	retval = -1;
- 	fb->sz = 0;
- 	fb->buf = NULL;
- 	for (;;) {
-@@ -498,13 +499,13 @@ read_whole_file(struct mparse *curp, int
- 		    read(fd, fb->buf + (int)off, fb->sz - off);
- 		if (ssz == 0) {
- 			fb->sz = off;
--			retval = 1;
-+			retval = 0;
- 			break;
- 		}
- 		if (ssz == -1) {
- 			if (curp->gzip)
- 				(void)gzerror(gz, &gzerrnum);
--			mandoc_msg(MANDOCERR_FILE, 0, 0, "read: %s",
-+			mandoc_msg(MANDOCERR_READ, 0, 0, "%s",
- 			    curp->gzip && gzerrnum != Z_ERRNO ?
- 			    zError(gzerrnum) : strerror(errno));
- 			break;
-@@ -513,10 +514,10 @@ read_whole_file(struct mparse *curp, int
- 	}
- 
- 	if (curp->gzip && (gzerrnum = gzclose(gz)) != Z_OK)
--		mandoc_msg(MANDOCERR_FILE, 0, 0, "gzclose: %s",
-+		mandoc_msg(MANDOCERR_GZCLOSE, 0, 0, "%s",
- 		    gzerrnum == Z_ERRNO ? strerror(errno) :
- 		    zError(gzerrnum));
--	if (retval == 0) {
-+	if (retval == -1) {
- 		free(fb->buf);
- 		fb->buf = NULL;
- 	}
-@@ -555,7 +556,7 @@ mparse_readfd(struct mparse *curp, int f
- 		mandoc_msg(MANDOCERR_ROFFLOOP, curp->line, 0, NULL);
- 		return;
- 	}
--	if (read_whole_file(curp, fd, &blk, &with_mmap) == 0)
-+	if (read_whole_file(curp, fd, &blk, &with_mmap) == -1)
- 		return;
- 
- 	/*
---- a/regress/char/space/zerowidth.out_html
-+++ b/regress/char/space/zerowidth.out_html
-@@ -1,6 +1,4 @@
--BEGINTEST
- zero width space \& between A and B: AB
- hyphenation allowed \% between A and B: AB
- half-narrow (1/12) space \^ between A and B: AB
- narrow space (1/6) \| between A and B: AB
--ENDTEST
---- a/regress/char/unicode/ascii.out_html
-+++ b/regress/char/unicode/ascii.out_html
-@@ -1,4 +1,3 @@
--BEGINTEST
- """	QUOTATION MARK
- ###	NUMBER SIGN
- $$$	DOLLAR SIGN
-@@ -19,4 +18,3 @@ ____	LOW LINE
- ||||	VERTICAL LINE
- }}}	RIGHT CURLY BRACKET
- ~~~~	TILDE
--ENDTEST
---- a/regress/char/unicode/invalid.out_html
-+++ b/regress/char/unicode/invalid.out_html
-@@ -1,8 +1,6 @@
--BEGINTEST
- too short: >.<
- just right: >+<
- too long: >..<
- too large: >..<
- trailing garbage: ><
- not unicode: >_.↑.⇑<
--ENDTEST
---- a/regress/char/unicode/latin1.out_html
-+++ b/regress/char/unicode/latin1.out_html
-@@ -1,4 +1,3 @@
--BEGINTEST
- ¡¡	INVERTED EXCLAMATION MARK
- ¢¢	CENT SIGN
- ££	POUND SIGN
-@@ -93,4 +92,3 @@ BEGINTEST
- ýý	LATIN SMALL LETTER Y WITH ACUTE
- þþ	LATIN SMALL LETTER THORN
- ÿÿ	LATIN SMALL LETTER Y WITH DIAERESIS
--ENDTEST
---- a/regress/char/unicode/latin1diff.out_html
-+++ b/regress/char/unicode/latin1diff.out_html
-@@ -1,3 +1 @@
--BEGINTEST
- ¯¯	MACRON
--ENDTEST
---- a/regress/char/unicode/named.out_html
-+++ b/regress/char/unicode/named.out_html
-@@ -1,4 +1,3 @@
--BEGINTEST
- ıı	LATIN SMALL LETTER DOTLESS I
- IJIJ	LATIN CAPITAL LIGATURE IJ
- ijij	LATIN SMALL LIGATURE IJ
-@@ -167,4 +166,3 @@ BEGINTEST
- ♦♦	BLACK DIAMOND SUIT
- ⟨⟨	MATHEMATICAL LEFT ANGLE BRACKET
- ⟩⟩	MATHEMATICAL RIGHT ANGLE BRACKET
--ENDTEST
---- a/regress/char/unicode/namediff.out_html
-+++ b/regress/char/unicode/namediff.out_html
-@@ -1,4 +1,3 @@
--BEGINTEST
- ‾‾ OVERLINE
- ℏℏℏ PLANCK CONSTANT OVER TWO PI
- ↕↕ UP DOWN ARROW
-@@ -22,4 +21,3 @@ BEGINTEST
- ⎫⎫ RIGHT CURLY BRACKET UPPER HOOK
- ⎬⎬ RIGHT CURLY BRACKET MIDDLE PIECE
- ⎭⎭ RIGHT CURLY BRACKET LOWER HOOK
--ENDTEST
---- a/regress/char/unicode/nogroff.out_html
-+++ b/regress/char/unicode/nogroff.out_html
-@@ -1,4 +1,3 @@
--BEGINTEST
- ��	<control> NULL
- ��	<control> START OF HEADING
- ��	<control> BELL
-@@ -34,4 +33,3 @@ BEGINTEST
- 􏿽	<Plane 16 Private Use, Last>
- 􏿾	<undefined>
- 􏿿	<undefined>
--ENDTEST
---- a/regress/eqn/Makefile.inc
-+++ b/regress/eqn/Makefile.inc
-@@ -1,6 +1,4 @@
--# $OpenBSD: Makefile.inc,v 1.2 2015/02/03 19:37:25 schwarze Exp $
--
--EQN = /usr/local/bin/eqn
-+# $OpenBSD: Makefile.inc,v 1.4 2020/01/08 10:17:15 schwarze Exp $
- 
- SKIP_GROFF ?= ${REGRESS_TARGETS}
- SKIP_TMAN ?= ALL
---- a/regress/eqn/delim/Makefile
-+++ b/regress/eqn/delim/Makefile
-@@ -1,5 +1,8 @@
--# $OpenBSD: Makefile,v 1.1.1.1 2015/01/01 12:53:46 schwarze Exp $
-+# $OpenBSD: Makefile,v 1.3 2020/01/08 12:09:14 schwarze Exp $
- 
- REGRESS_TARGETS	 = basic
-+UTF8_TARGETS	 = basic
-+GOPTS		 = -e
-+SKIP_GROFF	 =
- 
- .include 
---- a/regress/eqn/delim/basic.in
-+++ b/regress/eqn/delim/basic.in
-@@ -8,15 +8,24 @@
- .Sh DESCRIPTION
- initial text
- .EQ
--delim <>alpha
-+delim []alpha
- .EN
--inline 
-+inline [beta]
- .EQ
- delim offgamma
- .EN
--inline 
-+inline [delta]
- .EQ
- delim onepsilon
- .EN
--inline 
-+inline [zeta]
-+.EQ
-+delim $$
-+delim off
-+.EN
-+inline $eta$
-+.EQ
-+delim on
-+.EN
-+inline $theta$
- final text
---- a/regress/eqn/delim/basic.out_ascii
-+++ b/regress/eqn/delim/basic.out_ascii
-@@ -4,7 +4,7 @@ NNAAMMEE
-      ddeelliimm--bbaassiicc - inline eqn delimiters
- 
- DDEESSCCRRIIPPTTIIOONN
--     initial text  inline   inline  
--     inline  final text
-+     initial text  inline   inline [delta] 
-+     inline  inline $eta$ inline  final text
- 
--OpenBSD                          July 4, 2017                          OpenBSD
-+OpenBSD                         January 8, 2020                        OpenBSD
---- /dev/null
-+++ b/regress/eqn/delim/basic.out_utf8
-@@ -0,0 +1,10 @@
-+DELIM-BASIC(1)              General Commands Manual             DELIM-BASIC(1)
-+
-+NNAAMMEE
-+     ddeelliimm--bbaassiicc – inline eqn delimiters
-+
-+DDEESSCCRRIIPPTTIIOONN
-+     initial text α inline β γ inline [delta] ε inline ζ inline $eta$ inline θ
-+     final text
-+
-+OpenBSD                         January 8, 2020                        OpenBSD
---- a/regress/eqn/nullary/roman.out_html
-+++ b/regress/eqn/nullary/roman.out_html
-@@ -1,15 +1,5 @@
- unquoted
--words:sincostan
--seccsc
--asinacosatanasec
--acscsinh
--coshtanhcotharc
--maxminlimloglnexpReImandiffordetquoted
--words:
--sincos
--tanseccscasinacosatanasecacscsinhcoshtanhcotharcmaxminlimloglnexpReImandiffordetfont
--operations:
--sinsinsuperstring:sinuscomposite
--word:
--tan=sin/
--cos
-+words:sincostanseccscasinacosatanasecacscsinhcoshtanhcotharcmaxminlimloglnexpReImandiffordetquoted
-+words:sincostanseccscasinacosatanasecacscsinhcoshtanhcotharcmaxminlimloglnexpReImandiffordetfont
-+operations:sinsinsuperstring:sinuscomposite
-+word:tan=sin/cos
---- a/regress/eqn/nullary/symbol.out_html
-+++ b/regress/eqn/nullary/symbol.out_html
-@@ -1,6 +1,4 @@
- unquoted
- words:εquoted
--words:
--epsilonprimecomposite
--word:
--epsilon-prime
-+words:epsilonprimecomposite
-+word:epsilon-prime
---- a/regress/man/HP/literal.out_html
-+++ b/regress/man/HP/literal.out_html
-@@ -1,4 +1,3 @@
--BEGINTEST before hanged paragraph
- 

tag indented text

-

regular paragraph

-
-@@ -17,4 +16,3 @@ paragraph
- 
- regular text -
--ENDTEST ---- a/regress/man/IP/literal.out_html -+++ b/regress/man/IP/literal.out_html -@@ -1,4 +1,3 @@ --BEGINTEST before indentation -
-
tag
-
indented regular text
-@@ -27,7 +26,7 @@ regular text -
-

--regular text -+

regular text

-
- literal
- text
-@@ -47,7 +46,7 @@ text
- 
-

--regular text -+

regular text

-
-
tag
-
indented regular text -@@ -65,4 +64,3 @@ paragraph -
- regular text -
--ENDTEST ---- a/regress/man/RS/literal.out_html -+++ b/regress/man/RS/literal.out_html -@@ -1,6 +1,5 @@ --BEGINTEST --
--initial regular text -+
-+ initial regular text

-
- literal text
- before display
-@@ -15,6 +14,5 @@ This is a very long line that would wrap
- literal text
- after display
- 
--final regular text --
--ENDTEST -+

final regular text -+
---- a/regress/man/RS/paragraph.out_html -+++ b/regress/man/RS/paragraph.out_html -@@ -1,8 +1,6 @@ --BEGINTEST before paragraph -

regular paragraph

-
indented paragraph -

nested paragraph

-
- regular text after display -
--ENDTEST ---- a/regress/man/SH/paragraph.out_html -+++ b/regress/man/SH/paragraph.out_html -@@ -1,10 +1,8 @@ --BEGINTEST -
-
-

--This text immediately follows a section header. -+

This text immediately follows a section header.

-

This is a paragraph.

-
-
-

--ENDTEST ---- a/regress/man/SS/paragraph.out_html -+++ b/regress/man/SS/paragraph.out_html -@@ -1,11 +1,9 @@ --BEGINTEST -
-

--This text immediately follows a subsection header. -+

This text immediately follows a subsection header.

-

This is a paragraph.

-
-
-

--ENDTEST ---- a/regress/man/SY/literal.out_html -+++ b/regress/man/SY/literal.out_html -@@ -1,6 +1,5 @@ --BEGINTEST --
--initial regular text -+
-+ initial regular text

- - - -@@ -26,6 +25,5 @@ before display - literal text - after display - --final regular text --
--ENDTEST -+

final regular text -+
---- a/regress/man/TH/baddate.out_lint -+++ b/regress/man/TH/baddate.out_lint -@@ -1 +1 @@ --mandoc: baddate.in:2:18: WARNING: cannot parse date, using it verbatim: three bad words -+mandoc: baddate.in:2:18: WARNING: cannot parse date, using it verbatim: TH three bad words ---- a/regress/man/TH/emptydate.out_lint -+++ b/regress/man/TH/emptydate.out_lint -@@ -1 +1 @@ --mandoc: emptydate.in:2:20: WARNING: missing date, using today's date: TH -+mandoc: emptydate.in:2:20: WARNING: missing date, using "": TH ---- a/regress/man/TH/longdate.out_lint -+++ b/regress/man/TH/longdate.out_lint -@@ -1 +1 @@ --mandoc: longdate.in:2:19: WARNING: cannot parse date, using it verbatim: 1234567890123456789012345678901234567890123456789012345678901234567890123456789012 -+mandoc: longdate.in:2:19: WARNING: cannot parse date, using it verbatim: TH 1234567890123456789012345678901234567890123456789012345678901234567890123456789012 ---- a/regress/man/TH/noTH.out_lint -+++ b/regress/man/TH/noTH.out_lint -@@ -1,2 +1,2 @@ - mandoc: noTH.in: WARNING: missing manual title, using "" --mandoc: noTH.in: WARNING: missing date, using today's date -+mandoc: noTH.in: WARNING: missing date, using "" ---- a/regress/man/TH/noarg.out_lint -+++ b/regress/man/TH/noarg.out_lint -@@ -1,3 +1,3 @@ - mandoc: noarg.in:2:2: WARNING: missing manual title, using "": TH - mandoc: noarg.in:2:2: WARNING: missing manual section, using "": TH --mandoc: noarg.in:2:2: WARNING: missing date, using today's date: TH -+mandoc: noarg.in:2:2: WARNING: missing date, using "": TH ---- a/regress/man/TH/onearg.out_lint -+++ b/regress/man/TH/onearg.out_lint -@@ -1,2 +1,2 @@ - mandoc: onearg.in:2:2: WARNING: missing manual section, using "": TH TH-ONEARG --mandoc: onearg.in:2:2: WARNING: missing date, using today's date: TH -+mandoc: onearg.in:2:2: WARNING: missing date, using "": TH ---- a/regress/man/TH/twoargs.out_lint -+++ b/regress/man/TH/twoargs.out_lint -@@ -1,2 +1,2 @@ - mandoc: twoargs.in:2:2: WARNING: missing manual section, using "": TH TH-TWOARGS --mandoc: twoargs.in:2:2: WARNING: missing date, using today's date: TH -+mandoc: twoargs.in:2:2: WARNING: missing date, using "": TH ---- a/regress/man/TP/literal.out_html -+++ b/regress/man/TP/literal.out_html -@@ -1,4 +1,3 @@ --BEGINTEST before indentation -

-
tag
-
regular indented text
-@@ -24,4 +23,3 @@ paragraph - - regular text -
--ENDTEST ---- a/regress/man/TS/Makefile -+++ b/regress/man/TS/Makefile -@@ -1,7 +1,8 @@ --# $OpenBSD: Makefile,v 1.3 2015/01/30 21:28:21 schwarze Exp $ -+# $OpenBSD: Makefile,v 1.4 2020/01/08 10:17:15 schwarze Exp $ - - REGRESS_TARGETS = break vspace - LINT_TARGETS = break -+GOPTS = -t - - # groff-1.22.3 defect: - # - Starting a table in next-line scope confuses font handling, -@@ -9,14 +10,4 @@ LINT_TARGETS = break - - SKIP_GROFF = break - -- --# OpenBSD only: maintainer targets -- --TBL=/usr/local/bin/tbl -- --.for t in ${REGRESS_TARGETS} --${t}.out_ascii: ${t}.in -- ${TBL} ${.ALLSRC} | ${NROFF} ${NOPTS} -Tascii > ${.TARGET} --.endfor -- - .include ---- a/regress/mdoc/Bd/nf.out_html -+++ b/regress/mdoc/Bd/nf.out_html -@@ -1,9 +1,8 @@ --BEGINTEST initial text -
- after .nf
- request
- 
--after .fi request -+

after .fi request

-
-
- in unfilled
-@@ -19,4 +18,3 @@ in filled block
- 
- after end of filled block -
--ENDTEST ---- a/regress/mdoc/Bd/paragraph.out_html -+++ b/regress/mdoc/Bd/paragraph.out_html -@@ -1,4 +1,3 @@ --BEGINTEST initial text -

normal paragraph

-
filled display -

paragraph in display

-@@ -16,4 +15,3 @@ paragraph -
- again back to normal -
--ENDTEST ---- a/regress/mdoc/Bf/paragraph.out_html -+++ b/regress/mdoc/Bf/paragraph.out_html -@@ -1,6 +1,4 @@ --BEGINTEST -

normal text

-
literal text -

literal paragraph

-
--ENDTEST ---- a/regress/mdoc/Bl/column.in -+++ b/regress/mdoc/Bl/column.in -@@ -81,7 +81,8 @@ - .\" Mixed tab and Ta - .Bl -column a b c d - .It a b c d --.It a b c Ta d -+.It "a b c" Ta -+d - .It a b Ta c d - .It a b Ta c Ta d - .It a Ta b c d ---- a/regress/mdoc/Bl/column.out_ascii -+++ b/regress/mdoc/Bl/column.out_ascii -@@ -71,4 +71,4 @@ DDEESSCCRRIIPPTTIIOONN - aa bb tab at eol - aa bb cc dd - --OpenBSD July 4, 2017 OpenBSD -+OpenBSD July 11, 2019 OpenBSD ---- a/regress/mdoc/Bl/column.out_lint -+++ b/regress/mdoc/Bl/column.out_lint -@@ -4,4 +4,4 @@ mandoc: column.in:75:2: WARNING: skippin - mandoc: column.in:77:2: WARNING: wrong number of cells: 2 columns, 4 cells - mandoc: column.in:78:2: WARNING: wrong number of cells: 2 columns, 5 cells - mandoc: column.in:79:2: WARNING: skipping empty macro: It --mandoc: column.in:107:18: WARNING: skipping -width argument: Bl -column -+mandoc: column.in:108:18: WARNING: skipping -width argument: Bl -column ---- a/regress/mdoc/Bl/column.out_markdown -+++ b/regress/mdoc/Bl/column.out_markdown -@@ -75,4 +75,4 @@ BL-COLUMN(1) - General Commands Manual - - aa bb cc dd - --OpenBSD - July 4, 2017 -+OpenBSD - July 11, 2019 ---- a/regress/mdoc/D1/spacing.out_html -+++ b/regress/mdoc/D1/spacing.out_html -@@ -1,8 +1,6 @@ --BEGINTEST -

preceding paragraph

-
spacing in and around one-line displays
- empty display: -
- following text -
--ENDTEST ---- a/regress/mdoc/Dd/Makefile -+++ b/regress/mdoc/Dd/Makefile -@@ -1,23 +1,15 @@ --# $OpenBSD: Makefile,v 1.2 2014/11/21 01:52:45 schwarze Exp $ -+# $OpenBSD: Makefile,v 1.6 2020/01/19 16:16:33 schwarze Exp $ - - REGRESS_TARGETS = badarg dupe late long manarg noarg order - LINT_TARGETS = badarg dupe late long manarg noarg order - --# noarg output contains the date when the file is formatted -+# groff-1.22.4 prints footer fields of excessive length on top of -+# each other rather than breaking the output line. - --SKIP_ASCII ?= noarg --SKIP_MARKDOWN ?= noarg -- --# If groff finds exactly three arguments, it assumes they are month, --# day and year without further checking. If there are no arguments, --# groff uses the string "Epoch". Otherwise, it silently falls back --# to today's date. --# That is not at all sane behaviour, we are not going to imitate it. -- --SKIP_GROFF = badarg long manarg noarg -+SKIP_GROFF = long - - # Autodetection fails for late .Dd, so specify -mdoc explicitly. - --MOPTS += -mdoc -+MOPTS = -mdoc - - .include ---- a/regress/mdoc/Dd/badarg.out_lint -+++ b/regress/mdoc/Dd/badarg.out_lint -@@ -1,2 +1,2 @@ --mandoc: badarg.in:2:2: WARNING: cannot parse date, using it verbatim: bad date -+mandoc: badarg.in:2:5: WARNING: cannot parse date, using it verbatim: Dd bad date - mandoc: badarg.in:2:5: STYLE: Mdocdate missing: Dd bad date (OpenBSD) ---- a/regress/mdoc/Dd/dupe.out_lint -+++ b/regress/mdoc/Dd/dupe.out_lint -@@ -1,3 +1,3 @@ --mandoc: dupe.in:2:5: STYLE: Mdocdate missing: Dd August (OpenBSD) -+mandoc: dupe.in:2:5: STYLE: Mdocdate missing: Dd August 1, 2014 (OpenBSD) - mandoc: dupe.in:5:2: ERROR: duplicate prologue macro: Dd - mandoc: dupe.in:11:2: ERROR: duplicate prologue macro: Dd ---- a/regress/mdoc/Dd/long.out_lint -+++ b/regress/mdoc/Dd/long.out_lint -@@ -1,2 +1,2 @@ --mandoc: long.in:2:2: WARNING: cannot parse date, using it verbatim: 1234567890123456789012345678901234567890123456789012345678901234567890123456789 -+mandoc: long.in:2:5: WARNING: cannot parse date, using it verbatim: Dd 1234567890123456789012345678901234567890123456789012345678901234567890123456789 - mandoc: long.in:2:5: STYLE: Mdocdate missing: Dd 1234567890123456789012345678901234567890123456789012345678901234567890123456789 (OpenBSD) ---- a/regress/mdoc/Dd/manarg.out_lint -+++ b/regress/mdoc/Dd/manarg.out_lint -@@ -1,2 +1,2 @@ --mandoc: manarg.in:2:2: STYLE: legacy man(7) date format: Dd 2014-08-07 -+mandoc: manarg.in:2:5: STYLE: legacy man(7) date format: Dd 2014-08-07 - mandoc: manarg.in:2:5: STYLE: Mdocdate missing: Dd 2014-08-07 (OpenBSD) ---- /dev/null -+++ b/regress/mdoc/Dd/noarg.out_ascii -@@ -0,0 +1,9 @@ -+DD-NOARG(1) General Commands Manual DD-NOARG(1) -+ -+NNAAMMEE -+ DDdd--nnooaarrgg - date macro without an argument -+ -+DDEESSCCRRIIPPTTIIOONN -+ some text -+ -+OpenBSD OpenBSD ---- a/regress/mdoc/Dd/noarg.out_lint -+++ b/regress/mdoc/Dd/noarg.out_lint -@@ -1 +1 @@ --mandoc: noarg.in:2:2: WARNING: missing date, using today's date -+mandoc: noarg.in:2:2: WARNING: missing date, using "": Dd ---- /dev/null -+++ b/regress/mdoc/Dd/noarg.out_markdown -@@ -0,0 +1,11 @@ -+DD-NOARG(1) - General Commands Manual -+ -+# NAME -+ -+**Dd-noarg** - date macro without an argument -+ -+# DESCRIPTION -+ -+some text -+ -+OpenBSD - ---- a/regress/mdoc/Dd/order.out_lint -+++ b/regress/mdoc/Dd/order.out_lint -@@ -1,2 +1,2 @@ - mandoc: order.in:3:2: WARNING: prologue macros out of order: Dd after Dt --mandoc: order.in:3:5: STYLE: Mdocdate missing: Dd August (OpenBSD) -+mandoc: order.in:3:5: STYLE: Mdocdate missing: Dd August 5, 2014 (OpenBSD) ---- a/regress/mdoc/Fo/warn.in -+++ b/regress/mdoc/Fo/warn.in -@@ -12,3 +12,17 @@ - .Fc - .Ft double - .Fn atan2 "double y, double x" -+.Ft int -+.Fn close) "int fd" -+.Ft typedef void -+.Fn (handler) int -+.Ft typedef void -+.Fn (*fp) int -+.Ft int -+.Fn (open "const char *path" -+.Ft FILE * -+.Fn (*popen "const char *cmd" -+.Ft void -+.Fn (trail)x void -+.Ft void -+.Fn *star void ---- a/regress/mdoc/Fo/warn.out_ascii -+++ b/regress/mdoc/Fo/warn.out_ascii -@@ -10,4 +10,25 @@ SSYYNNOOPPSSIISS - _d_o_u_b_l_e - aattaann22(_d_o_u_b_l_e _y_, _d_o_u_b_l_e _x); - --OpenBSD July 4, 2017 OpenBSD -+ _i_n_t -+ cclloossee))(_i_n_t _f_d); -+ -+ _t_y_p_e_d_e_f _v_o_i_d -+ ((hhaannddlleerr))(_i_n_t); -+ -+ _t_y_p_e_d_e_f _v_o_i_d -+ ((**ffpp))(_i_n_t); -+ -+ _i_n_t -+ ((ooppeenn(_c_o_n_s_t _c_h_a_r _*_p_a_t_h); -+ -+ _F_I_L_E _* -+ ((**ppooppeenn(_c_o_n_s_t _c_h_a_r _*_c_m_d); -+ -+ _v_o_i_d -+ ((ttrraaiill))xx(_v_o_i_d); -+ -+ _v_o_i_d -+ **ssttaarr(_v_o_i_d); -+ -+OpenBSD September 13, 2019 OpenBSD ---- a/regress/mdoc/Fo/warn.out_lint -+++ b/regress/mdoc/Fo/warn.out_lint -@@ -1,2 +1,6 @@ - mandoc: warn.in:10:8: WARNING: parenthesis in function name: sin() - mandoc: warn.in:14:19: WARNING: comma in function argument: double y, double x -+mandoc: warn.in:16:10: WARNING: parenthesis in function name: close) -+mandoc: warn.in:22:5: WARNING: parenthesis in function name: (open -+mandoc: warn.in:24:5: WARNING: parenthesis in function name: (*popen -+mandoc: warn.in:26:5: WARNING: parenthesis in function name: (trail)x ---- a/regress/mdoc/Fo/warn.out_markdown -+++ b/regress/mdoc/Fo/warn.out_markdown -@@ -12,4 +12,25 @@ FO-WARN(1) - General Commands Manual - *double* - **atan2**(*double y, double x*); - --OpenBSD - July 4, 2017 -+*int* -+**close)**(*int fd*); -+ -+*typedef void* -+**(handler)**(*int*); -+ -+*typedef void* -+**(\*fp)**(*int*); -+ -+*int* -+**(open**(*const char \*path*); -+ -+*FILE \*‌* -+**(\*popen**(*const char \*cmd*); -+ -+*void* -+**(trail)x**(*void*); -+ -+*void* -+**\*star**(*void*); -+ -+OpenBSD - September 13, 2019 ---- a/regress/mdoc/Os/dupe.out_ascii -+++ b/regress/mdoc/Os/dupe.out_ascii -@@ -6,4 +6,4 @@ NNAAMMEE - DDEESSCCRRIIPPTTIIOONN - initial text final text - --OpenBSD July 4, 2017 OpenBSD -+OpenBSD January 19, 2020 OpenBSD ---- a/regress/mdoc/Os/dupe.out_lint -+++ b/regress/mdoc/Os/dupe.out_lint -@@ -1,9 +1,9 @@ - mandoc: dupe.in:3:5: STYLE: operating system explicitly specified: Os NetBSD (NetBSD) --mandoc: dupe.in:2:5: STYLE: Mdocdate found: Dd $Mdocdate: (NetBSD) -+mandoc: dupe.in:2:5: STYLE: Mdocdate found: Dd $Mdocdate$ (NetBSD) - mandoc: dupe.in:4:2: WARNING: prologue macros out of order: Dt after Os - mandoc: dupe.in:5:2: ERROR: duplicate prologue macro: Os - mandoc: dupe.in:5:5: STYLE: operating system explicitly specified: Os FreeBSD (NetBSD) --mandoc: dupe.in:2:5: STYLE: Mdocdate found: Dd $Mdocdate: (NetBSD) -+mandoc: dupe.in:2:5: STYLE: Mdocdate found: Dd $Mdocdate$ (NetBSD) - mandoc: dupe.in:11:2: ERROR: duplicate prologue macro: Os - mandoc: dupe.in:11:5: STYLE: operating system explicitly specified: Os OpenBSD (NetBSD) - mandoc: dupe.in: STYLE: RCS id missing: (NetBSD) ---- a/regress/mdoc/Os/dupe.out_markdown -+++ b/regress/mdoc/Os/dupe.out_markdown -@@ -9,4 +9,4 @@ OS-DUPE(1) - General Commands Manual - initial text - final text - --OpenBSD - July 4, 2017 -+OpenBSD - January 19, 2020 ---- a/regress/mdoc/Rs/paragraph.out_html -+++ b/regress/mdoc/Rs/paragraph.out_html -@@ -1,18 +1,16 @@ --BEGINTEST --
--initial reference: author name, -- book title. -+
-+ initial reference: author name, -+ book title.

-

in a paragraph: another - author, another book.

- -
-

--initial reference: -+

initial reference:

-

author name, - book title.

-

in a paragraph:

-

another author, - another book.

-
--ENDTEST
---- a/regress/mdoc/Sh/paragraph.out_html
-+++ b/regress/mdoc/Sh/paragraph.out_html
-@@ -1,11 +1,9 @@
--BEGINTEST
- 

descriptive text

-
-

--initial subsection text -+

initial subsection text

-

subsection paragraph

-
-
-
-

--ENDTEST ---- a/regress/regress.pl -+++ b/regress/regress.pl -@@ -86,8 +86,12 @@ sub syshtml ($@) { - if (!$state && s/.*//) { - $state = 'math'; - next unless length; -- } elsif (/^BEGINTEST/) { -+ } elsif (/BEGINTEST/) { - $state = 'other'; -+ next; -+ } elsif (/ENDTEST/) { -+ $state = 0; -+ next; - } - if ($state eq 'math') { - s/^ *//; -@@ -98,7 +102,6 @@ sub syshtml ($@) { - } - } - print $outfd "$_\n" if $state; -- $state = 0 if /^ENDTEST/; - } - close $outfd; - close $infd; -@@ -162,10 +165,9 @@ foreach my $module (qw(roff char mdoc ma - my %subvars = (MOPTS => ''); - parse_makefile "$module/$subdir/Makefile", \%subvars; - parse_makefile "$module/Makefile.inc", \%subvars; -+ delete $subvars{GOPTS}; - delete $subvars{SKIP_GROFF}; - delete $subvars{SKIP_GROFF_ASCII}; -- delete $subvars{TBL}; -- delete $subvars{EQN}; - my @mandoc = ('../mandoc', split ' ', $subvars{MOPTS}); - delete $subvars{MOPTS}; - my @regress_testnames; -@@ -424,7 +426,7 @@ if ($count_total == 1) { - print "\n"; - } else { - print "No tests were run.\n"; --} -+} - if ($targets{clean}) { - if ($count_rm) { - print "Deleted $count_rm test output files.\n"; ---- a/regress/roff/de/Makefile -+++ b/regress/roff/de/Makefile -@@ -1,7 +1,7 @@ --# $OpenBSD: Makefile,v 1.12 2019/02/06 20:54:28 schwarze Exp $ -+# $OpenBSD: Makefile,v 1.13 2019/04/21 23:45:50 schwarze Exp $ - --REGRESS_TARGETS = append cond escname factorial indir infinite startde tab --REGRESS_TARGETS += TH Dd -+REGRESS_TARGETS = append cond empty escname factorial -+REGRESS_TARGETS += indir infinite startde tab TH Dd - LINT_TARGETS = escname indir infinite - - # groff-1.22.4 defect: ---- /dev/null -+++ b/regress/roff/de/empty.in -@@ -0,0 +1,18 @@ -+.\" $OpenBSD: empty.in,v 1.1 2019/04/21 23:45:50 schwarze Exp $ -+.Dd $Mdocdate$ -+.Dt DE-EMPTY 1 -+.Os -+.Sh NAME -+.Nm de-empty -+.Nd empty user-defined macro with arguments -+.Sh DESCRIPTION -+initial text -+.de empty -+.. -+.de real -+arg=\\$1 -+.empty wrong -+arg=\\$1 -+.. -+.real right -+final text ---- /dev/null -+++ b/regress/roff/de/empty.out_ascii -@@ -0,0 +1,9 @@ -+DE-EMPTY(1) General Commands Manual DE-EMPTY(1) -+ -+NNAAMMEE -+ ddee--eemmppttyy - empty user-defined macro with arguments -+ -+DDEESSCCRRIIPPTTIIOONN -+ initial text arg=right arg=right final text -+ -+OpenBSD April 21, 2019 OpenBSD ---- a/regress/roff/esc/f.out_html -+++ b/regress/roff/esc/f.out_html -@@ -1,6 +1,4 @@ --BEGINTEST - numbers: bolditalicbolditalicroman - letters: bolditalicbackbolditalicroman - multiletter: boldemptyitalicbackbolditalicroman - typewriter: romanboldromanitalicroman --ENDTEST ---- a/regress/roff/ft/badargs.out_html -+++ b/regress/roff/ft/badargs.out_html -@@ -1,9 +1,6 @@ --BEGINTEST --
--default font italic bold italic -- typeqriter -- roman bold -- italic bold still bold -- italic back to bold back to italic --
--ENDTEST -+
-+ default font italic bold italic -+ typeqriter roman bold -+ italic bold still bold italic back to -+ bold back to italic -+
---- a/regress/roff/sp/fill-man.out_html -+++ b/regress/roff/sp/fill-man.out_html -@@ -1,4 +1,3 @@ --BEGINTEST in fill mode: -

switch to no-fill mode:

-
- in no-fill mode:
-@@ -6,4 +5,3 @@ in no-fill mode:
- back to
- fill mode:
- 
--ENDTEST ---- a/regress/roff/string/dotT.out_html -+++ b/regress/roff/string/dotT.out_html -@@ -1,5 +1,3 @@ --BEGINTEST -

We are using the html device.

-

The device name can be overridden.

-
--ENDTEST
---- a/regress/roff/while/Makefile
-+++ b/regress/roff/while/Makefile
-@@ -1,6 +1,6 @@
--# $OpenBSD: Makefile,v 1.1 2018/08/24 22:56:37 schwarze Exp $
-+# $OpenBSD: Makefile,v 1.2 2019/04/21 22:43:00 schwarze Exp $
- 
--REGRESS_TARGETS	= basic badargs into nesting outof
-+REGRESS_TARGETS	= basic badargs break into nesting outof
- LINT_TARGETS	= badargs into nesting outof
- 
- # mandoc defects:
---- /dev/null
-+++ b/regress/roff/while/break.in
-@@ -0,0 +1,16 @@
-+.\" $OpenBSD: break.in,v 1.1 2019/04/21 22:43:00 schwarze Exp $
-+.Dd $Mdocdate$
-+.Dt WHILE-BREAK 1
-+.Os
-+.Sh NAME
-+.Nm while-break
-+.Nd break request inside a while loop
-+.Sh DESCRIPTION
-+initial text
-+.nr cnt 11 1
-+.while n \{\
-+\n-[cnt]
-+.if !\n[cnt] .break
-+\(en
-+.\}
-+final text
---- /dev/null
-+++ b/regress/roff/while/break.out_ascii
-@@ -0,0 +1,9 @@
-+WHILE-BREAK(1)              General Commands Manual             WHILE-BREAK(1)
-+
-+NNAAMMEE
-+     wwhhiillee--bbrreeaakk - break request inside a while loop
-+
-+DDEESSCCRRIIPPTTIIOONN
-+     initial text 10 - 9 - 8 - 7 - 6 - 5 - 4 - 3 - 2 - 1 - 0 final text
-+
-+OpenBSD                         April 21, 2019                         OpenBSD
---- a/regress/tbl/Makefile.inc
-+++ b/regress/tbl/Makefile.inc
-@@ -1,16 +1,7 @@
--# $OpenBSD: Makefile.inc,v 1.2 2015/02/03 19:37:25 schwarze Exp $
--
--TBL = /usr/local/bin/tbl
-+# $OpenBSD: Makefile.inc,v 1.4 2020/01/08 10:17:15 schwarze Exp $
- 
- SKIP_TMAN ?= ALL
- SKIP_MARKDOWN ?= ALL
--
--
--# OpenBSD only: maintainer targets
--
--.for t in ${REGRESS_TARGETS}
--${t}.out_ascii: ${t}.in
--	${TBL} ${.ALLSRC} | ${NROFF} ${NOPTS} -Tascii > ${.TARGET}
--.endfor
-+GOPTS = -t
- 
- .include "../Makefile.inc"
---- a/regress/tbl/data/Makefile
-+++ b/regress/tbl/data/Makefile
-@@ -1,6 +1,7 @@
--# $OpenBSD: Makefile,v 1.4 2017/07/04 20:59:17 schwarze Exp $
-+# $OpenBSD: Makefile,v 1.5 2019/07/18 14:38:47 schwarze Exp $
- 
--REGRESS_TARGETS  = blankline block_unclosed block_width block_wrap empty insert
-+REGRESS_TARGETS	 = blankline block_empty block_unclosed block_width
-+REGRESS_TARGETS	+= block_wrap empty insert
- LINT_TARGETS	 = block_unclosed empty insert
- 
- # groff-1.22.3 defect:
---- /dev/null
-+++ b/regress/tbl/data/block_empty.in
-@@ -0,0 +1,19 @@
-+.\" $OpenBSD: block_empty.in,v 1.1 2019/07/18 14:38:47 schwarze Exp $
-+.TH TBL-DATA-BLOCK_EMPTY 1 "July 17, 2019"
-+.SH NAME
-+tbl-data-block_empty \- empty text block
-+.SH DESCRIPTION
-+normal text
-+.TS
-+|l|l|.
-+_
-+A	test
-+_
-+table	T{
-+T}
-+_
-+.TE
-+.SH AUTHORS
-+.MT rea@FreeBSD.org
-+Eygene Ryabinkin
-+.ME
---- /dev/null
-+++ b/regress/tbl/data/block_empty.out_ascii
-@@ -0,0 +1,22 @@
-+TBL-DATA-BLOCK_EMPTY(1)     General Commands Manual    TBL-DATA-BLOCK_EMPTY(1)
-+
-+
-+
-+NNAAMMEE
-+       tbl-data-block_empty - empty text block
-+
-+DDEESSCCRRIIPPTTIIOONN
-+       normal text
-+
-+       +------+------+
-+       |A     | test |
-+       +------+------+
-+       |table |      |
-+       +------+------+
-+
-+AAUUTTHHOORRSS
-+       Eygene Ryabinkin 
-+
-+
-+
-+OpenBSD                          July 17, 2019         TBL-DATA-BLOCK_EMPTY(1)
---- a/regress/tbl/layout/Makefile
-+++ b/regress/tbl/layout/Makefile
-@@ -1,8 +1,8 @@
--# $OpenBSD: Makefile,v 1.2 2015/01/30 00:27:09 schwarze Exp $
-+# $OpenBSD: Makefile,v 1.6 2020/01/11 20:56:26 schwarze Exp $
- 
--REGRESS_TARGETS	 = center complex empty emptyline
--REGRESS_TARGETS	+= lines lines-nogroff numbers span
--LINT_TARGETS	 = complex empty
-+REGRESS_TARGETS	 = badspan center complex empty emptycol emptyline
-+REGRESS_TARGETS	+= lines lines-nogroff numbers shortlines span
-+LINT_TARGETS	 = badspan complex empty
- 
- # groff-1.22.3 defects:
- # - When the layout is completely empty,
---- /dev/null
-+++ b/regress/tbl/layout/badspan.in
-@@ -0,0 +1,13 @@
-+.\" $OpenBSD: badspan.in,v 1.1 2020/01/11 20:56:26 schwarze Exp $
-+.TH TBL-LAYOUT-BADSPAN 1 "January 11, 2020"
-+.SH NAME
-+tbl-layout-badspan \- invalid spanned cells
-+.SH DESCRIPTION
-+normal text
-+.TS
-+allbox tab(:);
-+S L S S
-+L L L L L L.
-+span:end
-+1:2:3:4:5:6
-+.TE
---- /dev/null
-+++ b/regress/tbl/layout/badspan.out_ascii
-@@ -0,0 +1,18 @@
-+TBL-LAYOUT-BADSPAN(1)       General Commands Manual      TBL-LAYOUT-BADSPAN(1)
-+
-+
-+
-+NNAAMMEE
-+       tbl-layout-badspan - invalid spanned cells
-+
-+DDEESSCCRRIIPPTTIIOONN
-+       normal text
-+
-+       +--+-----------+-----+---+
-+       |  | span      | end |   |
-+       +--+---+---+---+-----+---+
-+       |1 | 2 | 3 | 4 | 5   | 6 |
-+       +--+---+---+---+-----+---+
-+
-+
-+OpenBSD                        January 11, 2020          TBL-LAYOUT-BADSPAN(1)
---- /dev/null
-+++ b/regress/tbl/layout/badspan.out_lint
-@@ -0,0 +1 @@
-+mandoc: badspan.in:9:1: WARNING: tbl line starts with span
---- /dev/null
-+++ b/regress/tbl/layout/emptycol.in
-@@ -0,0 +1,49 @@
-+.\" $OpenBSD: emptycol.in,v 1.1 2019/12/31 22:49:17 schwarze Exp $
-+.TH TBL-LAYOUT-EMPTYCOL 1 "December 31, 2019"
-+.SH NAME
-+tbl-layout-emptycol \- empty columns in tables
-+.SH DESCRIPTION
-+missing final column:
-+.TS
-+allbox tab(:);
-+L L L
-+L L.
-+1:2
-+a:b
-+.TE
-+.sp
-+empty final column:
-+.TS
-+allbox tab(:);
-+L L L
-+L L.
-+1:2:
-+a:b
-+.TE
-+.sp
-+final column with zero-width content:
-+.TS
-+allbox tab(:);
-+L L L
-+L L.
-+1:2:\&
-+a:b
-+.TE
-+.sp
-+empty middle column:
-+.TS
-+allbox tab(:);
-+L L L
-+L.
-+1::3
-+a
-+.TE
-+.sp
-+span crossing empty middle column:
-+.TS
-+allbox tab(:);
-+L L L
-+L S S.
-+1::3
-+span
-+.TE
---- /dev/null
-+++ b/regress/tbl/layout/emptycol.out_ascii
-@@ -0,0 +1,46 @@
-+TBL-LAYOUT-EMPTYCOL(1)      General Commands Manual     TBL-LAYOUT-EMPTYCOL(1)
-+
-+
-+
-+NNAAMMEE
-+       tbl-layout-emptycol - empty columns in tables
-+
-+DDEESSCCRRIIPPTTIIOONN
-+       missing final column:
-+
-+       +--+---+---+
-+       |1 | 2 |   |
-+       +--+---+---+
-+       |a | b |   |
-+       +--+---+---+
-+       empty final column:
-+
-+       +--+---+---+
-+       |1 | 2 |   |
-+       +--+---+---+
-+       |a | b |   |
-+       +--+---+---+
-+       final column with zero-width content:
-+
-+       +--+---+---+
-+       |1 | 2 |   |
-+       +--+---+---+
-+       |a | b |   |
-+       +--+---+---+
-+       empty middle column:
-+
-+       +--+---+---+
-+       |1 |   | 3 |
-+       +--+---+---+
-+       |a |   |   |
-+       +--+---+---+
-+       span crossing empty middle column:
-+
-+       +--+---+---+
-+       |1 |   | 3 |
-+       +--+---+---+
-+       |span      |
-+       +----------+
-+
-+
-+OpenBSD                        December 31, 2019        TBL-LAYOUT-EMPTYCOL(1)
---- /dev/null
-+++ b/regress/tbl/layout/shortlines.in
-@@ -0,0 +1,47 @@
-+.\" $OpenBSD: shortlines.in,v 1.2 2020/01/11 20:56:26 schwarze Exp $
-+.TH TBL-LAYOUT-SHORTLINES 1 "January 11, 2020"
-+.SH NAME
-+tbl-layout-shortlines \- table lines of different length
-+.SH DESCRIPTION
-+normal text
-+.TS
-+allbox tab(:);
-+L L
-+L
-+L L.
-+left:right
-+short
-+left:right
-+.TE
-+.sp
-+.TS
-+allbox tab(:);
-+L L
-+L
-+L
-+L L.
-+left:right
-+first short
-+second short
-+left:right
-+.TE
-+.sp
-+.TS
-+allbox tab(:);
-+L L L
-+L L
-+L.
-+left:middle:right
-+short:line
-+very short
-+.TE
-+.sp
-+.TS
-+allbox tab(:);
-+L
-+L
-+L L L.
-+very short
-+short:line
-+left:middle:right
-+.TE
---- /dev/null
-+++ b/regress/tbl/layout/shortlines.out_ascii
-@@ -0,0 +1,46 @@
-+TBL-LAYOUT-SHORTLINES(1)    General Commands Manual   TBL-LAYOUT-SHORTLINES(1)
-+
-+
-+
-+NNAAMMEE
-+       tbl-layout-shortlines - table lines of different length
-+
-+DDEESSCCRRIIPPTTIIOONN
-+       normal text
-+
-+       +------+-------+
-+       |left  | right |
-+       +------+-------+
-+       |short |       |
-+       +------+-------+
-+       |left  | right |
-+       +------+-------+
-+
-+       +-------------+-------+
-+       |left         | right |
-+       +-------------+-------+
-+       |first short  |       |
-+       +-------------+-------+
-+       |second short |       |
-+       +-------------+-------+
-+       |left         | right |
-+       +-------------+-------+
-+
-+       +-----------+--------+-------+
-+       |left       | middle | right |
-+       +-----------+--------+-------+
-+       |short      | line   |       |
-+       +-----------+--------+-------+
-+       |very short |        |       |
-+       +-----------+--------+-------+
-+
-+       +-----------+--------+-------+
-+       |very short |        |       |
-+       +-----------+--------+-------+
-+       |short      | line   |       |
-+       +-----------+--------+-------+
-+       |left       | middle | right |
-+       +-----------+--------+-------+
-+
-+
-+OpenBSD                        January 11, 2020       TBL-LAYOUT-SHORTLINES(1)
---- a/roff.7
-+++ b/roff.7
-@@ -315,12 +315,18 @@ delimiters
- The proper spacing is also intelligently preserved if a sentence ends at
- the boundary of a macro line.
- .Pp
-+If an input line happens to end with a period, exclamation or question
-+mark that isn't the end of a sentence, append a zero-width space
-+.Pq Sq \e& .
-+.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 \&.
-+An abbreviation at the end of an input line needs escaping, e.g.\e&
-+like this.
- .Ed
- .Sh REQUEST SYNTAX
- A request or macro line consists of:
-@@ -503,10 +509,9 @@ This is a Heirloom extension and current
- .It Ic \&br
- Break the output line.
- .It Ic \&break
--Break out of a
-+Break out of the innermost
- .Ic \&while
- loop.
--Currently unsupported.
- .It Ic \&breakchar Ar char ...
- Optional line break characters.
- This is a Heirloom extension and currently ignored.
-@@ -2279,7 +2284,7 @@ code.
- .Pp
- The special semantics of the
- .Cm nS
--number register is an idiosyncracy of
-+number register is an idiosyncrasy of
- .Ox
- manuals and not supported by other
- .Xr mdoc 7
---- a/roff.c
-+++ b/roff.c
-@@ -1,7 +1,7 @@
- /*	$Id: roff.c,v 1.363 2019/02/06 21:11:43 schwarze Exp $ */
- /*
-  * Copyright (c) 2008-2012, 2014 Kristaps Dzonsons 
-- * Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze 
-+ * Copyright (c) 2010-2015, 2017-2020 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
-@@ -133,15 +133,18 @@ struct	roff {
- 	char		 escape; /* escape character */
- };
- 
-+/*
-+ * A macro definition, condition, or ignored block.
-+ */
- struct	roffnode {
- 	enum roff_tok	 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 */
-+	char		*end; /* custom end macro of the block */
-+	int		 endspan; /* scope to: 1=eol 2=next line -1=\} */
-+	int		 rule; /* content is: 1=evaluated 0=skipped */
- };
- 
- #define	ROFF_ARGS	 struct roff *r, /* parse ctx */ \
-@@ -181,6 +184,7 @@ static	int		 roff_als(ROFF_ARGS);
- static	int		 roff_block(ROFF_ARGS);
- static	int		 roff_block_text(ROFF_ARGS);
- static	int		 roff_block_sub(ROFF_ARGS);
-+static	int		 roff_break(ROFF_ARGS);
- static	int		 roff_cblock(ROFF_ARGS);
- static	int		 roff_cc(ROFF_ARGS);
- static	int		 roff_ccond(struct roff *, int, int);
-@@ -351,7 +355,7 @@ const char *__roff_name[MAN_MAX + 1] = {
- 	"Lk",		"Mt",		"Brq",		"Bro",
- 	"Brc",		"%C",		"Es",		"En",
- 	"Dx",		"%Q",		"%U",		"Ta",
--	NULL,
-+	"Tg",		NULL,
- 	"TH",		"SH",		"SS",		"TP",
- 	"TQ",
- 	"LP",		"PP",		"P",		"IP",
-@@ -400,7 +404,7 @@ static	struct roffmac	 roffs[TOKEN_NONE]
- 	{ roff_unsupp, NULL, NULL, 0 },  /* boxa */
- 	{ roff_line_ignore, NULL, NULL, 0 },  /* bp */
- 	{ roff_unsupp, NULL, NULL, 0 },  /* BP */
--	{ roff_unsupp, NULL, NULL, 0 },  /* break */
-+	{ roff_break, NULL, NULL, 0 },  /* break */
- 	{ roff_line_ignore, NULL, NULL, 0 },  /* breakchar */
- 	{ roff_line_ignore, NULL, NULL, 0 },  /* brnl */
- 	{ roff_noarg, NULL, NULL, 0 },  /* brp */
-@@ -685,7 +689,7 @@ roffhash_find(struct ohash *htab, const
- 
- /*
-  * Pop the current node off of the stack of roff instructions currently
-- * pending.
-+ * pending.  Return 1 if it is a loop or 0 otherwise.
-  */
- static int
- roffnode_pop(struct roff *r)
-@@ -767,6 +771,7 @@ void
- roff_reset(struct roff *r)
- {
- 	roff_free1(r);
-+	r->options |= MPARSE_COMMENT;
- 	r->format = r->options & (MPARSE_MDOC | MPARSE_MAN);
- 	r->control = '\0';
- 	r->escape = '\\';
-@@ -779,7 +784,7 @@ roff_reset(struct roff *r)
- void
- roff_free(struct roff *r)
- {
--	int	 	 i;
-+	int		 i;
- 
- 	roff_free1(r);
- 	for (i = 0; i < r->mstacksz; i++)
-@@ -796,7 +801,7 @@ roff_alloc(int options)
- 
- 	r = mandoc_calloc(1, sizeof(struct roff));
- 	r->reqtab = roffhash_alloc(0, ROFF_RENAMED);
--	r->options = options;
-+	r->options = options | MPARSE_COMMENT;
- 	r->format = options & (MPARSE_MDOC | MPARSE_MAN);
- 	r->mstackpos = -1;
- 	r->rstackpos = -1;
-@@ -1242,7 +1247,7 @@ roff_expand(struct roff *r, struct buf *
- 		 * in the syntax tree.
- 		 */
- 
--		if (newesc != ASCII_ESC && r->format == 0) {
-+		if (newesc != ASCII_ESC && r->options & MPARSE_COMMENT) {
- 			while (*ep == ' ' || *ep == '\t')
- 				ep--;
- 			ep[1] = '\0';
-@@ -1586,7 +1591,7 @@ char *
- roff_getarg(struct roff *r, char **cpp, int ln, int *pos)
- {
- 	struct buf	 buf;
--	char	 	*cp, *start;
-+	char		*cp, *start;
- 	int		 newesc, pairs, quoted, white;
- 
- 	/* Quoting can only start with a new word. */
-@@ -1811,8 +1816,10 @@ roff_parseln(struct roff *r, int ln, str
- 		roff_addtbl(r->man, ln, r->tbl);
- 		return e;
- 	}
--	if ( ! ctl)
-+	if ( ! ctl) {
-+		r->options &= ~MPARSE_COMMENT;
- 		return roff_parsetext(r, buf, pos, offs) | e;
-+	}
- 
- 	/* Skip empty request lines. */
- 
-@@ -1835,6 +1842,7 @@ roff_parseln(struct roff *r, int ln, str
- 
- 	/* No scope is open.  This is a new request or macro. */
- 
-+	r->options &= ~MPARSE_COMMENT;
- 	spos = pos;
- 	t = roff_parse(r, buf->buf, &pos, ln, ppos);
- 
-@@ -2002,6 +2010,10 @@ roff_cblock(ROFF_ARGS)
- 
- }
- 
-+/*
-+ * Pop all nodes ending at the end of the current input line.
-+ * Return the number of loops ended.
-+ */
- static int
- roffnode_cleanscope(struct roff *r)
- {
-@@ -2016,6 +2028,11 @@ roffnode_cleanscope(struct roff *r)
- 	return inloop;
- }
- 
-+/*
-+ * Handle the closing \} of a conditional block.
-+ * Apart from generating warnings, this only pops nodes.
-+ * Return the number of loops ended.
-+ */
- static int
- roff_ccond(struct roff *r, int ln, int ppos)
- {
-@@ -2235,6 +2252,7 @@ roff_block_text(ROFF_ARGS)
- static int
- roff_cond_sub(ROFF_ARGS)
- {
-+	struct roffnode	*bl;
- 	char		*ep;
- 	int		 endloop, irc, rr;
- 	enum roff_tok	 t;
-@@ -2276,15 +2294,39 @@ roff_cond_sub(ROFF_ARGS)
- 		}
- 	}
- 
-+	t = roff_parse(r, buf->buf, &pos, ln, ppos);
-+
-+	/* For now, let high level macros abort .ce mode. */
-+
-+	if (roffce_node != NULL &&
-+	    (t == TOKEN_NONE || t == ROFF_Dd || t == ROFF_EQ ||
-+             t == ROFF_TH || t == ROFF_TS)) {
-+		r->man->last = roffce_node;
-+		r->man->next = ROFF_NEXT_SIBLING;
-+		roffce_lines = 0;
-+		roffce_node = NULL;
-+	}
-+
- 	/*
- 	 * Fully handle known macros when they are structurally
- 	 * required or when the conditional evaluated to true.
- 	 */
- 
--	t = roff_parse(r, buf->buf, &pos, ln, ppos);
--	irc |= t != TOKEN_NONE && (rr || roffs[t].flags & ROFFMAC_STRUCT) ?
--	    (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs) :
--	    rr ? ROFF_CONT : ROFF_IGN;
-+	if (t == ROFF_break) {
-+		if (irc & ROFF_LOOPMASK)
-+			irc = ROFF_IGN | ROFF_LOOPEXIT;
-+		else if (rr) {
-+			for (bl = r->last; bl != NULL; bl = bl->parent) {
-+				bl->rule = 0;
-+				if (bl->tok == ROFF_while)
-+					break;
-+			}
-+		}
-+	} else if (t != TOKEN_NONE &&
-+	    (rr || roffs[t].flags & ROFFMAC_STRUCT))
-+		irc |= (*roffs[t].proc)(r, t, buf, ln, ppos, pos, offs);
-+	else
-+		irc |= rr ? ROFF_CONT : ROFF_IGN;
- 	return irc;
- }
- 
-@@ -3482,6 +3524,17 @@ roff_als(ROFF_ARGS)
- 	return ROFF_IGN;
- }
- 
-+/*
-+ * The .break request only makes sense inside conditionals,
-+ * and that case is already handled in roff_cond_sub().
-+ */
-+static int
-+roff_break(ROFF_ARGS)
-+{
-+	mandoc_msg(MANDOCERR_BLK_NOTOPEN, ln, pos, "break");
-+	return ROFF_IGN;
-+}
-+
- static int
- roff_cc(ROFF_ARGS)
- {
-@@ -3804,6 +3857,11 @@ roff_userdef(ROFF_ARGS)
- 	char		 *arg, *ap, *dst, *src;
- 	size_t		  sz;
- 
-+	/* If the macro is empty, ignore it altogether. */
-+
-+	if (*r->current_string == '\0')
-+		return ROFF_IGN;
-+
- 	/* Initialize a new macro stack context. */
- 
- 	if (++r->mstackpos == r->mstacksz) {
-@@ -3851,7 +3909,7 @@ roff_userdef(ROFF_ARGS)
- 	buf->sz = strlen(buf->buf) + 1;
- 	*offs = 0;
- 
--	return buf->sz > 1 && buf->buf[buf->sz - 2] == '\n' ?
-+	return buf->buf[buf->sz - 2] == '\n' ?
- 	    ROFF_REPARSE | ROFF_USERCALL : ROFF_IGN | ROFF_APPEND;
- }
- 
---- a/roff.h
-+++ b/roff.h
-@@ -1,7 +1,7 @@
- /*	$Id: roff.h,v 1.69 2019/03/04 13:01:57 schwarze Exp $	*/
- /*
-  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons 
-- * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze 
-+ * Copyright (c) 2013-2015, 2017-2020 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
-@@ -437,6 +437,7 @@ enum	roff_tok {
- 	MDOC__Q,
- 	MDOC__U,
- 	MDOC_Ta,
-+	MDOC_Tg,
- 	MDOC_MAX,
- 	MAN_TH,
- 	MAN_SH,
---- a/roff_html.c
-+++ b/roff_html.c
-@@ -94,7 +94,7 @@ roff_html_pre_ft(ROFF_HTML_ARGS)
- 	const char	*cp;
- 
- 	cp = n->child->string;
--	print_metaf(h, mandoc_font(cp, (int)strlen(cp)));
-+	html_setfont(h, mandoc_font(cp, (int)strlen(cp)));
- }
- 
- static void
---- a/tag.c
-+++ b/tag.c
-@@ -1,6 +1,6 @@
- /*	$Id: tag.c,v 1.21 2018/11/22 11:30:23 schwarze Exp $ */
- /*
-- * Copyright (c) 2015, 2016, 2018 Ingo Schwarze 
-+ * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze 
-  *
-  * Permission to use, copy, modify, and distribute this software for any
-  * purpose with or without fee is hereby granted, provided that the above
-@@ -18,9 +18,8 @@
- 
- #include 
- 
--#if HAVE_ERR
--#include 
--#endif
-+#include 
-+#include 
- #include 
- #include 
- #include 
-@@ -32,6 +31,7 @@
- 
- #include "mandoc_aux.h"
- #include "mandoc_ohash.h"
-+#include "mandoc.h"
- #include "tag.h"
- 
- struct tag_entry {
-@@ -54,7 +54,7 @@ static struct tag_files	 tag_files;
-  * but for simplicity, create it anyway.
-  */
- struct tag_files *
--tag_init(void)
-+tag_init(char *tagname)
- {
- 	struct sigaction	 sa;
- 	int			 ofd;
-@@ -62,6 +62,7 @@ tag_init(void)
- 	ofd = -1;
- 	tag_files.tfd = -1;
- 	tag_files.tcpgid = -1;
-+	tag_files.tagname = tagname;
- 
- 	/* Clean up when dying from a signal. */
- 
-@@ -83,8 +84,10 @@ tag_init(void)
- 
- 	/* Save the original standard output for use by the pager. */
- 
--	if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1)
-+	if ((tag_files.ofd = dup(STDOUT_FILENO)) == -1) {
-+		mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno));
- 		goto fail;
-+	}
- 
- 	/* Create both temporary output files. */
- 
-@@ -92,12 +95,20 @@ tag_init(void)
- 	    sizeof(tag_files.ofn));
- 	(void)strlcpy(tag_files.tfn, "/tmp/man.XXXXXXXXXX",
- 	    sizeof(tag_files.tfn));
--	if ((ofd = mkstemp(tag_files.ofn)) == -1)
-+	if ((ofd = mkstemp(tag_files.ofn)) == -1) {
-+		mandoc_msg(MANDOCERR_MKSTEMP, 0, 0,
-+		    "%s: %s", tag_files.ofn, strerror(errno));
- 		goto fail;
--	if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1)
-+	}
-+	if ((tag_files.tfd = mkstemp(tag_files.tfn)) == -1) {
-+		mandoc_msg(MANDOCERR_MKSTEMP, 0, 0,
-+		    "%s: %s", tag_files.tfn, strerror(errno));
- 		goto fail;
--	if (dup2(ofd, STDOUT_FILENO) == -1)
-+	}
-+	if (dup2(ofd, STDOUT_FILENO) == -1) {
-+		mandoc_msg(MANDOCERR_DUP, 0, 0, "%s", strerror(errno));
- 		goto fail;
-+	}
- 	close(ofd);
- 
- 	/*
-@@ -120,6 +131,7 @@ fail:
- 	*tag_files.tfn = '\0';
- 	tag_files.ofd = -1;
- 	tag_files.tfd = -1;
-+	tag_files.tagname = NULL;
- 	return NULL;
- }
- 
-@@ -135,6 +147,7 @@ tag_put(const char *s, int prio, size_t
- 	size_t			 len;
- 	unsigned int		 slot;
- 
-+	assert(prio <= TAG_FALLBACK);
- 	if (tag_files.tfd <= 0)
- 		return;
- 
-@@ -142,17 +155,17 @@ tag_put(const char *s, int prio, size_t
- 		s += 2;
- 
- 	/*
--	 * Skip whitespace and whatever follows it,
-+	 * Skip whitespace and escapes and whatever follows,
- 	 * and if there is any, downgrade the priority.
- 	 */
- 
--	len = strcspn(s, " \t");
-+	len = strcspn(s, " \t\\");
- 	if (len == 0)
- 		return;
- 
- 	se = s + len;
--	if (*se != '\0')
--		prio = INT_MAX;
-+	if (*se != '\0' && prio < TAG_WEAK)
-+		prio = TAG_WEAK;
- 
- 	slot = ohash_qlookupi(&tag_data, s, &se);
- 	entry = ohash_find(&tag_data, slot);
-@@ -172,25 +185,25 @@ tag_put(const char *s, int prio, size_t
- 
- 		/*
- 		 * Lower priority numbers take precedence,
--		 * but 0 is special.
--		 * A tag with priority 0 is only used
-+		 * but TAG_FALLBACK is special.
-+		 * A tag with priority TAG_FALLBACK is only used
- 		 * if the tag occurs exactly once.
- 		 */
- 
--		if (prio == 0) {
--			if (entry->prio == 0)
--				entry->prio = -1;
-+		if (prio == TAG_FALLBACK) {
-+			if (entry->prio == TAG_FALLBACK)
-+				entry->prio = TAG_DELETE;
- 			return;
- 		}
- 
- 		/* A better entry is already present, ignore the new one. */
- 
--		if (entry->prio > 0 && entry->prio < prio)
-+		if (entry->prio < prio)
- 			return;
- 
- 		/* The existing entry is worse, clear it. */
- 
--		if (entry->prio < 1 || entry->prio > prio)
-+		if (entry->prio > prio)
- 			entry->nlines = 0;
- 	}
- 
-@@ -216,21 +229,27 @@ tag_write(void)
- 	struct tag_entry	*entry;
- 	size_t			 i;
- 	unsigned int		 slot;
-+	int			 empty;
- 
- 	if (tag_files.tfd <= 0)
- 		return;
- 	if (tag_files.tagname != NULL && ohash_find(&tag_data,
-             ohash_qlookup(&tag_data, tag_files.tagname)) == NULL) {
--		warnx("%s: no such tag", tag_files.tagname);
-+		mandoc_msg(MANDOCERR_TAG, 0, 0, "%s", tag_files.tagname);
- 		tag_files.tagname = NULL;
- 	}
--	stream = fdopen(tag_files.tfd, "w");
-+	if ((stream = fdopen(tag_files.tfd, "w")) == NULL)
-+		mandoc_msg(MANDOCERR_FDOPEN, 0, 0, "%s", strerror(errno));
-+	empty = 1;
- 	entry = ohash_first(&tag_data, &slot);
- 	while (entry != NULL) {
--		if (stream != NULL && entry->prio >= 0)
--			for (i = 0; i < entry->nlines; i++)
-+		if (stream != NULL && entry->prio < TAG_DELETE) {
-+			for (i = 0; i < entry->nlines; i++) {
- 				fprintf(stream, "%s %s %zu\n",
- 				    entry->s, tag_files.ofn, entry->lines[i]);
-+				empty = 0;
-+			}
-+		}
- 		free(entry->lines);
- 		free(entry);
- 		entry = ohash_next(&tag_data, &slot);
-@@ -241,6 +260,10 @@ tag_write(void)
- 	else
- 		close(tag_files.tfd);
- 	tag_files.tfd = -1;
-+	if (empty) {
-+		unlink(tag_files.tfn);
-+		*tag_files.tfn = '\0';
-+	}
- }
- 
- void
---- a/tag.h
-+++ b/tag.h
-@@ -1,6 +1,6 @@
- /*      $Id: tag.h,v 1.8 2018/11/22 11:30:23 schwarze Exp $    */
- /*
-- * Copyright (c) 2015 Ingo Schwarze 
-+ * Copyright (c) 2015, 2018, 2019, 2020 Ingo Schwarze 
-  *
-  * Permission to use, copy, modify, and distribute this software for any
-  * purpose with or without fee is hereby granted, provided that the above
-@@ -15,6 +15,17 @@
-  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-  */
- 
-+/*
-+ * Tagging priorities.
-+ * Lower numbers indicate higher importance.
-+ */
-+#define	TAG_MANUAL	1		/* Set with a .Tg macro. */
-+#define	TAG_STRONG	2		/* Good automatic tagging. */
-+#define	TAG_WEAK	(INT_MAX - 2)	/* Dubious automatic tagging. */
-+#define	TAG_FALLBACK	(INT_MAX - 1)	/* Tag only used if unique. */
-+#define	TAG_DELETE	(INT_MAX)	/* Tag not used at all. */
-+
-+
- struct	tag_files {
- 	char	 ofn[20];
- 	char	 tfn[20];
-@@ -26,7 +37,7 @@ struct	tag_files {
- };
- 
- 
--struct tag_files *tag_init(void);
-+struct tag_files *tag_init(char *);
- void	 tag_put(const char *, int, size_t);
- void	 tag_write(void);
- void	 tag_unlink(void);
---- a/tbl_data.c
-+++ b/tbl_data.c
-@@ -21,6 +21,7 @@
- 
- #include 
- #include 
-+#include 
- #include 
- #include 
- #include 
-@@ -73,6 +74,7 @@ getdata(struct tbl_node *tbl, struct tbl
- 		if (dp->layout->last->col + 1 < dp->opts->cols) {
- 			cp = mandoc_calloc(1, sizeof(*cp));
- 			cp->pos = TBL_CELL_LEFT;
-+			cp->spacing = SIZE_MAX;
- 			dp->layout->last->next = cp;
- 			cp->col = dp->layout->last->col + 1;
- 			dp->layout->last = cp;
---- a/tbl_html.c
-+++ b/tbl_html.c
-@@ -25,6 +25,7 @@
- #include 
- 
- #include "mandoc.h"
-+#include "roff.h"
- #include "tbl.h"
- #include "out.h"
- #include "html.h"
---- a/tbl_term.c
-+++ b/tbl_term.c
-@@ -1,7 +1,7 @@
- /*	$Id: tbl_term.c,v 1.68 2019/02/09 21:02:47 schwarze Exp $ */
- /*
-  * Copyright (c) 2009, 2011 Kristaps Dzonsons 
-- * Copyright (c) 2011-2019 Ingo Schwarze 
-+ * Copyright (c) 2011-2020 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
-@@ -46,7 +46,8 @@ static	void	tbl_fill_border(struct termp
- static	void	tbl_fill_char(struct termp *, char, size_t);
- static	void	tbl_fill_string(struct termp *, const char *, size_t);
- static	void	tbl_hrule(struct termp *, const struct tbl_span *,
--			const struct tbl_span *, int);
-+			const struct tbl_span *, 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 *,
-@@ -163,7 +164,7 @@ term_tbl(struct termp *tp, const struct
- 	const struct tbl_cell	*cp, *cpn, *cpp, *cps;
- 	const struct tbl_dat	*dp;
- 	static size_t		 offset;
--	size_t		 	 save_offset;
-+	size_t			 save_offset;
- 	size_t			 coloff, tsz;
- 	int			 hspans, ic, more;
- 	int			 dvert, fc, horiz, lhori, rhori, uvert;
-@@ -222,9 +223,9 @@ term_tbl(struct termp *tp, const struct
- 
- 		if (tp->enc == TERMENC_ASCII &&
- 		    sp->opts->opts & TBL_OPT_DBOX)
--			tbl_hrule(tp, NULL, sp, TBL_OPT_DBOX);
-+			tbl_hrule(tp, NULL, sp, sp, TBL_OPT_DBOX);
- 		if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX))
--			tbl_hrule(tp, NULL, sp, TBL_OPT_BOX);
-+			tbl_hrule(tp, NULL, sp, sp, TBL_OPT_BOX);
- 	}
- 
- 	/* Set up the columns. */
-@@ -266,11 +267,11 @@ term_tbl(struct termp *tp, const struct
- 				hspans--;
- 				continue;
- 			}
--			if (dp == NULL)
--				continue;
--			hspans = dp->hspans;
--			if (ic || sp->layout->first->pos != TBL_CELL_SPAN)
-+			if (dp != NULL &&
-+			    (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
-+				hspans = dp->hspans;
- 				dp = dp->next;
-+			}
- 		}
- 
- 		/* Set up a column for a right vertical frame. */
-@@ -301,11 +302,11 @@ term_tbl(struct termp *tp, const struct
- 			tp->tcol++;
- 			tp->col = 0;
- 			tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
--			if (dp == NULL)
--				continue;
--			hspans = dp->hspans;
--			if (cp->pos != TBL_CELL_SPAN)
-+			if (dp != NULL &&
-+			    (ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
-+				hspans = dp->hspans;
- 				dp = dp->next;
-+			}
- 		}
- 		break;
- 	}
-@@ -342,7 +343,7 @@ term_tbl(struct termp *tp, const struct
- 
- 		more = 0;
- 		if (horiz)
--			tbl_hrule(tp, sp->prev, sp, 0);
-+			tbl_hrule(tp, sp->prev, sp, sp->next, 0);
- 		else {
- 			cp = sp->layout->first;
- 			cpn = sp->next == NULL ? NULL :
-@@ -424,11 +425,10 @@ term_tbl(struct termp *tp, const struct
- 					cp = cp->next;
- 					continue;
- 				}
--				if (dp != NULL) {
-+				if (dp != NULL && (ic ||
-+				    sp->layout->first->pos != TBL_CELL_SPAN)) {
- 					hspans = dp->hspans;
--					if (ic || sp->layout->first->pos
--					    != TBL_CELL_SPAN)
--						dp = dp->next;
-+					dp = dp->next;
- 				}
- 
- 				/*
-@@ -557,12 +557,12 @@ term_tbl(struct termp *tp, const struct
- 	tp->tcol->rmargin = tp->maxrmargin;
- 	if (sp->next == NULL) {
- 		if (sp->opts->opts & (TBL_OPT_DBOX | TBL_OPT_BOX)) {
--			tbl_hrule(tp, sp, NULL, TBL_OPT_BOX);
-+			tbl_hrule(tp, sp, sp, NULL, TBL_OPT_BOX);
- 			tp->skipvsp = 1;
- 		}
- 		if (tp->enc == TERMENC_ASCII &&
- 		    sp->opts->opts & TBL_OPT_DBOX) {
--			tbl_hrule(tp, sp, NULL, TBL_OPT_DBOX);
-+			tbl_hrule(tp, sp, sp, NULL, TBL_OPT_DBOX);
- 			tp->skipvsp = 2;
- 		}
- 		assert(tp->tbl.cols);
-@@ -571,7 +571,7 @@ term_tbl(struct termp *tp, const struct
- 	} else if (horiz == 0 && sp->opts->opts & TBL_OPT_ALLBOX &&
- 	    (sp->next == NULL || sp->next->pos == TBL_SPAN_DATA ||
- 	     sp->next->next != NULL))
--		tbl_hrule(tp, sp, sp->next, TBL_OPT_ALLBOX);
-+		tbl_hrule(tp, sp, sp, sp->next, TBL_OPT_ALLBOX);
- 
- 	tp->tcol->offset = save_offset;
- 	tp->flags &= ~TERMP_NONOSPACE;
-@@ -579,9 +579,10 @@ term_tbl(struct termp *tp, const struct
- 
- static void
- tbl_hrule(struct termp *tp, const struct tbl_span *spp,
--    const struct tbl_span *spn, int flags)
-+    const struct tbl_span *sp, const struct tbl_span *spn, int flags)
- {
- 	const struct tbl_cell	*cpp;    /* Layout cell above this line. */
-+	const struct tbl_cell	*cp;     /* Layout cell in this line. */
- 	const struct tbl_cell	*cpn;    /* Layout cell below this line. */
- 	const struct tbl_dat	*dpn;	 /* Data cell below this line. */
- 	const struct roffcol	*col;    /* Contains width and spacing. */
-@@ -592,6 +593,7 @@ tbl_hrule(struct termp *tp, const struct
- 	int			 uw, dw; /* Vertical line widths. */
- 
- 	cpp = spp == NULL ? NULL : spp->layout->first;
-+	cp  = sp  == NULL ? NULL : sp->layout->first;
- 	cpn = spn == NULL ? NULL : spn->layout->first;
- 	dpn = NULL;
- 	if (spn != NULL) {
-@@ -600,11 +602,11 @@ tbl_hrule(struct termp *tp, const struct
- 		else if (spn->next != NULL)
- 			dpn = spn->next->first;
- 	}
--	opts = spn == NULL ? spp->opts->opts : spn->opts->opts;
-+	opts = sp->opts->opts;
- 	bw = opts & TBL_OPT_DBOX ? (tp->enc == TERMENC_UTF8 ? 2 : 1) :
- 	    opts & (TBL_OPT_BOX | TBL_OPT_ALLBOX) ? 1 : 0;
- 	hw = flags == TBL_OPT_DBOX || flags == TBL_OPT_BOX ? bw :
--	    spn->pos == TBL_SPAN_DHORIZ ? 2 : 1;
-+	    sp->pos == TBL_SPAN_DHORIZ ? 2 : 1;
- 
- 	/* Print the left end of the line. */
- 
-@@ -619,14 +621,19 @@ tbl_hrule(struct termp *tp, const struct
- 		    (spp == NULL || cpn == NULL ||
- 		     cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1);
- 
-+	col = tp->tbl.cols;
- 	for (;;) {
--		col = tp->tbl.cols + (cpn == NULL ? cpp->col : cpn->col);
-+		if (cp == NULL)
-+			col++;
-+		else
-+			col = tp->tbl.cols + cp->col;
- 
- 		/* Print the horizontal line inside this column. */
- 
- 		lw = cpp == NULL || cpn == NULL ||
- 		    (cpn->pos != TBL_CELL_DOWN &&
--		     (dpn == NULL || strcmp(dpn->string, "\\^") != 0))
-+		     (dpn == NULL || dpn->string == NULL ||
-+		      strcmp(dpn->string, "\\^") != 0))
- 		    ? hw : 0;
- 		tbl_direct_border(tp, BHORIZ * lw,
- 		    col->width + col->spacing / 2);
-@@ -645,7 +652,10 @@ tbl_hrule(struct termp *tp, const struct
- 					uw = 1;
- 			}
- 			cpp = cpp->next;
--		}
-+		} else if (spp != NULL && opts & TBL_OPT_ALLBOX)
-+			uw = 1;
-+		if (cp != NULL)
-+			cp = cp->next;
- 		if (cpn != NULL) {
- 			if (flags != TBL_OPT_DBOX) {
- 				dw = cpn->vert;
-@@ -655,8 +665,9 @@ tbl_hrule(struct termp *tp, const struct
- 			cpn = cpn->next;
- 			while (dpn != NULL && dpn->layout != cpn)
- 				dpn = dpn->next;
--		}
--		if (cpp == NULL && cpn == NULL)
-+		} else if (spn != NULL && opts & TBL_OPT_ALLBOX)
-+			dw = 1;
-+		if (col + 1 == tp->tbl.cols + sp->opts->cols)
- 			break;
- 
- 		/* Vertical lines do not cross spanned cells. */
-@@ -670,7 +681,8 @@ tbl_hrule(struct termp *tp, const struct
- 
- 		rw = cpp == NULL || cpn == NULL ||
- 		    (cpn->pos != TBL_CELL_DOWN &&
--		     (dpn == NULL || strcmp(dpn->string, "\\^") != 0))
-+		     (dpn == NULL || dpn->string == NULL ||
-+		      strcmp(dpn->string, "\\^") != 0))
- 		    ? hw : 0;
- 
- 		/* The line crossing at the end of this column. */
---- a/term.c
-+++ b/term.c
-@@ -281,6 +281,8 @@ term_fill(struct termp *p, size_t *nbr,
- 			case ASCII_BREAK:
- 				vn = vis;
- 				break;
-+			default:
-+				abort();
- 			}
- 			/* Can break at the end of a word. */
- 			if (breakline || vn > vtarget)
---- a/tree.c
-+++ b/tree.c
-@@ -1,7 +1,7 @@
- /*	$Id: tree.c,v 1.84 2019/01/01 05:56:34 schwarze Exp $ */
- /*
-  * Copyright (c) 2008, 2009, 2011, 2014 Kristaps Dzonsons 
-- * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze 
-+ * Copyright (c) 2013-2015, 2017-2020 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
-@@ -34,6 +34,7 @@
- #include "main.h"
- 
- static	void	print_box(const struct eqn_box *, int);
-+static	void	print_cellt(enum tbl_cellt);
- static	void	print_man(const struct roff_node *, int);
- static	void	print_meta(const struct roff_meta *);
- static	void	print_mdoc(const struct roff_node *, int);
-@@ -374,11 +375,72 @@ print_box(const struct eqn_box *ep, int
- }
- 
- static void
-+print_cellt(enum tbl_cellt pos)
-+{
-+	switch(pos) {
-+	case TBL_CELL_LEFT:
-+		putchar('L');
-+		break;
-+	case TBL_CELL_LONG:
-+		putchar('a');
-+		break;
-+	case TBL_CELL_CENTRE:
-+		putchar('c');
-+		break;
-+	case TBL_CELL_RIGHT:
-+		putchar('r');
-+		break;
-+	case TBL_CELL_NUMBER:
-+		putchar('n');
-+		break;
-+	case TBL_CELL_SPAN:
-+		putchar('s');
-+		break;
-+	case TBL_CELL_DOWN:
-+		putchar('^');
-+		break;
-+	case TBL_CELL_HORIZ:
-+		putchar('-');
-+		break;
-+	case TBL_CELL_DHORIZ:
-+		putchar('=');
-+		break;
-+	case TBL_CELL_MAX:
-+		putchar('#');
-+		break;
-+	}
-+}
-+
-+static void
- print_span(const struct tbl_span *sp, int indent)
- {
- 	const struct tbl_dat *dp;
-+	const struct tbl_cell *cp;
- 	int		 i;
- 
-+	if (sp->prev == NULL) {
-+		for (i = 0; i < indent; i++)
-+			putchar(' ');
-+		printf("%d", sp->opts->cols);
-+		if (sp->opts->opts & TBL_OPT_CENTRE)
-+			fputs(" center", stdout);
-+		if (sp->opts->opts & TBL_OPT_EXPAND)
-+			fputs(" expand", stdout);
-+		if (sp->opts->opts & TBL_OPT_ALLBOX)
-+			fputs(" allbox", stdout);
-+		if (sp->opts->opts & TBL_OPT_BOX)
-+			fputs(" box", stdout);
-+		if (sp->opts->opts & TBL_OPT_DBOX)
-+			fputs(" doublebox", stdout);
-+		if (sp->opts->opts & TBL_OPT_NOKEEP)
-+			fputs(" nokeep", stdout);
-+		if (sp->opts->opts & TBL_OPT_NOSPACE)
-+			fputs(" nospaces", stdout);
-+		if (sp->opts->opts & TBL_OPT_NOWARN)
-+			fputs(" nowarn", stdout);
-+		printf(" (tbl options) %d:1\n", sp->line);
-+	}
-+
- 	for (i = 0; i < indent; i++)
- 		putchar(' ');
- 
-@@ -392,31 +454,52 @@ print_span(const struct tbl_span *sp, in
- 		putchar(' ');
- 		break;
- 	default:
-+		for (cp = sp->layout->first; cp != NULL; cp = cp->next)
-+			print_cellt(cp->pos);
-+		putchar(' ');
- 		for (dp = sp->first; dp; dp = dp->next) {
-+			if ((cp = dp->layout) == NULL)
-+				putchar('*');
-+			else {
-+				printf("%d", cp->col);
-+				print_cellt(dp->layout->pos);
-+				if (cp->flags & TBL_CELL_BOLD)
-+					putchar('b');
-+				if (cp->flags & TBL_CELL_ITALIC)
-+					putchar('i');
-+				if (cp->flags & TBL_CELL_TALIGN)
-+					putchar('t');
-+				if (cp->flags & TBL_CELL_UP)
-+					putchar('u');
-+				if (cp->flags & TBL_CELL_BALIGN)
-+					putchar('d');
-+				if (cp->flags & TBL_CELL_WIGN)
-+					putchar('z');
-+				if (cp->flags & TBL_CELL_EQUAL)
-+					putchar('e');
-+				if (cp->flags & TBL_CELL_WMAX)
-+					putchar('x');
-+			}
- 			switch (dp->pos) {
- 			case TBL_DATA_HORIZ:
- 			case TBL_DATA_NHORIZ:
- 				putchar('-');
--				putchar(' ');
--				continue;
-+				break;
- 			case TBL_DATA_DHORIZ:
- 			case TBL_DATA_NDHORIZ:
- 				putchar('=');
--				putchar(' ');
--				continue;
-+				break;
- 			default:
-+				putchar(dp->block ? '{' : '[');
-+				if (dp->string != NULL)
-+					fputs(dp->string, stdout);
-+				putchar(dp->block ? '}' : ']');
- 				break;
- 			}
--			printf("[\"%s\"", dp->string ? dp->string : "");
- 			if (dp->hspans)
- 				printf(">%d", dp->hspans);
- 			if (dp->vspans)
- 				printf("v%d", dp->vspans);
--			if (dp->layout == NULL)
--				putchar('*');
--			else if (dp->layout->pos == TBL_CELL_DOWN)
--				putchar('^');
--			putchar(']');
- 			putchar(' ');
- 		}
- 		break;
diff --git a/mandoc-1.14.5-dummy.diff b/mandoc-1.14.5-dummy.diff
deleted file mode 100644
index ba9a227..0000000
--- a/mandoc-1.14.5-dummy.diff
+++ /dev/null
@@ -1,26 +0,0 @@
-Index: mandoc-1.14.5/compat_getline.c
-===================================================================
---- mandoc-1.14.5.orig/compat_getline.c
-+++ mandoc-1.14.5/compat_getline.c
-@@ -2,7 +2,7 @@
- 
- #if HAVE_GETLINE
- 
--int dummy;
-+static int dummy;
- 
- #else
- 
-Index: mandoc-1.14.5/compat_reallocarray.c
-===================================================================
---- mandoc-1.14.5.orig/compat_reallocarray.c
-+++ mandoc-1.14.5/compat_reallocarray.c
-@@ -2,7 +2,7 @@
- 
- #if HAVE_REALLOCARRAY
- 
--int dummy;
-+static int dummy;
- 
- #else
- 
diff --git a/mandoc-1.14.5.tar.gz b/mandoc-1.14.5.tar.gz
deleted file mode 100644
index fdf9c90..0000000
--- a/mandoc-1.14.5.tar.gz
+++ /dev/null
@@ -1,3 +0,0 @@
-version https://git-lfs.github.com/spec/v1
-oid sha256:8219b42cb56fc07b2aa660574e6211ac38eefdbf21f41b698d3348793ba5d8f7
-size 651846
diff --git a/mandoc-1.14.6.tar.gz b/mandoc-1.14.6.tar.gz
new file mode 100644
index 0000000..844d6d6
--- /dev/null
+++ b/mandoc-1.14.6.tar.gz
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8bf0d570f01e70a6e124884088870cbed7537f36328d512909eb10cd53179d9c
+size 697150
diff --git a/mandoc.changes b/mandoc.changes
index 7025f22..1221bbf 100644
--- a/mandoc.changes
+++ b/mandoc.changes
@@ -1,3 +1,210 @@
+-------------------------------------------------------------------
+Fri Sep 24 20:31:52 UTC 2021 - Matej Cepl 
+
+- Update to 1.14.6:
+   --- MAJOR NEW FEATURES ---
+ * mdoc(7): automatic tagging improved in many respects
+ * mdoc(7): new .Tg (tag) macro to explicitly mark a place as
+   defining a term
+ * man(7): implement some automatic tagging support
+ * man(1): let -w without argument show the manpath, like in
+   man-db and man-1.6
+ * -T html: wrap text and phrasing elements in paragraphs unless
+   already contained in flow containers; never put them directly
+   into sections. This helps to format paragraphs with the CSS
+   class selector .Pp.
+ * man.conf(5): remove support for the "_whatdb" configuration directive
+   that was deprecated in 2015; please use "manpath" instead
+    --- MINOR NEW FEATURES ---
+ * man(1): switch the default pager from "more -s" to "less"
+ * man(1): in the fallback code to look for manual pages without using
+   mandoc.db(5), accept files "man/."
+   in addition to the already supported "man/name.[01-9]*"
+ * if messages are shown and output is printed without a pager, display
+   a heads-up on stderr at the end because otherwise, users may easily
+   miss the messages
+ * man.cgi(8): add a Content-Security-Policy HTTP header
+ * man.cgi(8): switch off autocomplete and autocapitalize
+ * mandoc.css: support prefers-color-scheme: dark
+ * -T html: add meta viewport element to help mobile devices
+ * -T html -O tag: let this pass a file:// URI to the pager
+ * tbl(7): implement the "nospaces" option
+ * tbl(7) -T html: implement the "a" (em indent) layout specification
+ * tbl(7) -T html: implement the "b" (bold) and "i" (italic) layout modifiers
+ * tbl(7): support two-character font names in the layout font modifier
+ * tbl(7) -T html: support horinzontal rulers in individual cells
+ * tbl(7) -T tree: print more details about columns, options, rows, and cells
+ * roff(7): implement the .break request (break out of a .while loop)
+ * roff(7): support the CB and CI fonts in \f and .ft
+ * -T lint: new STYLE message if a file name extension contradicts .Dt/.TH
+ * -T lint: new STYLE message about overlong text lines
+ * -W style: check .Xr links along the full manpath
+    --- RELIABILITY BUGFIXES ---
+ * man(1): do not segfault if /tmp/ is not writeable
+ * man(1): do not access a NULL pointer when both -l and -w are given
+ * makewhatis(8): do not crash when a manpath directory contains
+   a symbolic link that points to a directory 
+ * man(7): fix an assertion failure caused by doubly nested next-line scopes
+ * tbl(7): fix a crash when the last column is only reached by spans
+ * tbl(7): fix a NULL pointer access in some cases of two spans on one row
+ * tbl(7) -T ascii: fix a NULL pointer access on empty data cells
+ * tbl(7) -T ascii: fix a NULL pointer access on a line next to a short row
+ * tbl(7): fix an assertion failure caused by excessive spacing modifiers
+ * tbl(7): fix an infinite loop for some overlapping horizontal spans
+ * roff(7): fix a rare case of writing one byte past the end of the input buffer
+ * roff(7): do not call abort(3) when \*[.T] is encountered
+ * roff(7): fix an assertion failure caused by a macro inside .ce .if
+ * roff(7): fix assertion failures for .ti and .po with excessive arguments
+ * roff(7): avoid near-infinte output for .ce inside explicit no-fill mode
+ * -T ascii/utf8: fix assertion failures caused by excessive spacing
+ * -T html: fix an assertion failure caused by .ft in rare situations
+ * -T man: fix an assertion failure caused by tbl(7) and eqn(7) input
+    --- PORTABILITY IMPROVEMENTS ---
+ * rename HOMEBREWDIR to READ_ALLOWED_PATH, allow it to contain more than
+   one directory, and explain how to use that for NixOS and GNU Guix Linux
+ * configure: stop trying to ask make(1) what the default compiler is
+   because that test was too fragile; just use "cc" by default
+ * configure: various simplifications and improved robustness
+ * configure: only compile compat_*.c implementations that are needed
+ * configure: provide feature tests for __attribute__(()) and mkstemps(3)
+ * compat_*: sync with upstreams for security, functionality, and style
+ * in regress.pl, avoid the non-portable options sed(1) -i and echo(1) -n
+ * in the regression suite, avoid file names that differ only by case
+    --- MINOR FUNCTIONAL IMPROVEMENTS ---
+ * man(1) -h: for pages lacking a SYNOPSIS, show the NAME section
+ * man(1): when the first argument starts with a digit, optionally
+   followed by a letter, and at least one more argument follows,
+   interpret the first argument as a section name even when additional
+   characters follow after the digit and letter
+ * man(1): with a specific section requested, try harder to find
+   the best match; use this order of preference:
+   1. The section in both the directory name and the file name matches exactly.
+   2. The section in the file name matches exactly.
+   3. The section in the directory name matches exactly.
+   4. Neither of them matches exactly.
+ * man(1): if no tags were generated at all, unlink(2) the empty tags file
+   as soon as the condition can be detected and do not pass it to less(1)
+ * makewhatis(8): handle both dangling symlinks and .so links
+   in manual page directories more gracefully
+ * man.cgi(8): for invalid queries and for valid queries returning
+   no result, return the appropriate 40x status code rather than 200
+ * mdoc(7): let .Dd concatenate all arguments and default to the empty string
+ * mdoc(7): convert ".Fl Fl" to ".Fl \-" during validation, improving -T html
+ * mdoc(7): improve output of .At 32v
+ * man(7): no longer print multiple blank lines before NAME and page footer
+ * tbl(7) -T utf8: improved rendering of horizontal lines
+ * tbl(7) -T html: in "n" cells, align by padding numbers on the right
+ * tbl(7): no longer leak tabulator settings to subsequent roff(7) code
+ * mdoc(7) -T html: for .Bl -tag, use "column-count: 1" rather
+   than "overflow: auto" to avoid the ugly side effects
+ * mdoc(7) -T html: render .Bd -unfilled in proportionally-spaced font
+ * mdoc(7) -T html: format .Nd with  rather than 
+ * mdoc(7) -T lint: do not warn about Mdocdate without an actual date + * mdoc(7) -T lint: do not complain about function types of the + form "ret_type (fname)(args)", but otherwise check names more strictly + * -T html: append .html suffix to temporary files to please browsers + * -T markdown: print a BAGARG message if called on man(7) input + --- MINOR BUGFIXES --- + * man(1): do the search for each name independently, and + show the results in the order of the command line argument + * man(1): escape shell wildcard characters in name arguments before glob(3) + * man(1): when asking for a single manual page by name, prefer file name + matches over .Dt/.TH matches over first NAME matches over later NAME + matches, but do not change the ordering for apropos(1) nor for man -a + * man(1): correctly extract the section name from the file name extension + of gzipped manual page files + * makewhatis(8): fix file type tests putting wrong data into mandoc.db(5) + * man.cgi(8): fix section number in the element for preformatted pages + * tbl(7): correct handling of T& after horizontal rulers in the layout + * tbl(7): correct column widths if rows have different numbers of cells + * tbl(7): empty columns are 1n wide rather than 0n + * tbl(7): correctly calculate required column widths for tables containing + cells that horizontally span columns which contains "n" (number) formatted + cells on other rows + * tbl(7): skip escape sequences when looking for column separators + * eqn(7): skip whitespace before tokens + * roff(7): when calling an empty macro, do not clobber existing arguments + * roff(7): recognize \} on lines closing a macro definition request + * roff(7): do not throw a bogus warning for "'br\}" and similar lines + * roff(7): stop generating comment nodes when encountering the first content + * mandoc_char(7): make \0 (digit-width space) non-breaking + * mdoc(7) .Bl -column: parse Macro in .It "word<tab>word" Ta word Macro<eol> + * mdoc(7) -T html: display straight quotes, not curly quotes, for .Qq/.Qo + * -T html: remove some spurious line breaks, in particular inside <pre> + * -T html: use <br/> for a space character at the beginning of an input line + * -T html: use ~%d for ordinal fragment suffixes, reserve '~' for that purpose + --- STRUCTURAL IMPROVEMENTS --- + * introduce the concept of semantically transparent syntax tree nodes, + allowing improved decisions in various validators and formatters + * move some code out of the giant main() into separate functions + doing one well-defined task each + * clearly separate parser state (struct curparse) and formatter state + (struct outstate), don't mix them in the same struct + * in the HTML formatter, assert(3) that no HTML nesting violation occurs + * let html_close_paragraph() close any phrasing context + --- THANKS TO --- + * Anthony Bentley and Klemens Nanni (OpenBSD) for many patches and bug + reports, for useful discussions, and for checking patches + * Anton Lindqvist (OpenBSD) for two patches and a bug report + * Marc Espie (OpenBSD) for a patch, many bug reports, and useful discussions + * Lukas Epple (NixOS) for a patch, bug reports, suggesting a minor + portability feature, checking patches, and extensive release testing + * Abel Romero Perez for a patch, a bug report, and suggesting a new feature + * nabijaczleweli for a patch and for suggesting feature improvements + * Jonathan Gray (OpenBSD) for a patch and for bug reports + * Otto Moerbeek (OpenBSD) and Alexander Gromnitsky for a patch + * Armin Besirovic for a contribution to mandoc.css + * Jason McIntyre (OpenBSD) for manual page patches, suggesting a new feature, + checking many patches, and useful discussions + * Martin Vahlensieck for a manual page patch and reporting a code style issue + * Frederic Cambus and Ian Sutton (OpenBSD) for a manual page patch + * Jan Schreiber for many bug reports found with afl(1) + * G. Branden Robinson (GNU troff) for several bug reports, feature + suggestions, and for checking many groff patches + * Michael Stapelberg (Debian) for several bug reports and feature + suggestions, and for extensive release testing + * Ian Ropers, Lorenzo Beretta, and Oliver Corff for several bug reports + and feature suggestions + * Stephen Gregoratto for several bug reports + * Theo de Raadt (OpenBSD) for two bug reports, checking a patch, + and a useful discussion + * Thomas Klausner (NetBSD) for two bug reports and for release testing + * Andreas Kahari and Jason A. Donenfeld for two bug reports + * Soeren Tempel (Alpine Linux) for a bug report, suggesting a feature + improvement, and checking two patches + * Aman Verma, Jan Stary, and John Gardner for a bug report + and for suggesting a feature impovement + * Todd Miller (OpenBSD) for a bug report, checking a patch, + and a useful discussion + * Andrew Fresh, Brian Callahan, Christian Weisgerber, Paul de Weerd (OpenBSD), + Havard Eidnes, Jason Thorpe (NetBSD), Yuri Pankov (FreeBSD), + Bjarni Ingi Gislason, Chris Bennett, Edgar Pettijohn, Eldred Habert, + Jamie Landeg-Jones, Kazuo KUROI, and Wynn Wolf Arbor for a bug report + * Theo Buehler (OpenBSD) for suggesting two feature impovements + and for checking a patch + * Leah Neukirchen (Void Linux) for suggesting a feature impovement + and for release testing + * Colin Watson (Debian) for suggesting a feature impovement + and for checking groff patches + * Matej Cepl (SUSE Linux), Matthew Martin, Steffen Nurpmeso, + and Tim Baumgard for suggesting a feature impovement + * Christos Zoulas (NetBSD) for a report regarding portability + * Daniel Dickman (OpenBSD) for suggesting a portability improvement + * Werner Lemberg (GNU troff) and Douglas McIlroy + for reporting bugs in manual pages + * Baptiste Daroussin and Eygene Ryabinkin (FreeBSD) + for an additional regression test + * Michal Nowak for reporting several code style issues + * TJ Townsend (OpenBSD) for help with CSS + * Sevan Janiyan (NetBSD) and Robert Mustacchi (Illumos) + for extensive release testing + * Job Snijders, Kinichiro INOGUCHI, and Martijn van Duren (OpenBSD) + for checking patches + * Bertrand Garrigues and Ralph Corderoy (GNU troff) for checking groff patches +- Deleted upstreamed patches: + - 1.14.5-master.patch + - mandoc-1.14.5-dummy.diff + ------------------------------------------------------------------- Tue Sep 22 15:12:55 UTC 2020 - Ludwig Nussel <lnussel@suse.de> diff --git a/mandoc.spec b/mandoc.spec index 292bf0b..5736379 100644 --- a/mandoc.spec +++ b/mandoc.spec @@ -1,7 +1,7 @@ # # spec file for package mandoc # -# Copyright (c) 2020 SUSE LLC +# Copyright (c) 2021 SUSE LLC # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: mandoc -Version: 1.14.5 +Version: 1.14.6 Release: 0 %define nvr %{name}-%{version}-%{release} Summary: UNIX manpage compiler @@ -25,17 +25,12 @@ License: ISC Group: Productivity/Publishing/Troff URL: http://mandoc.bsd.lv/ Source: http://mandoc.bsd.lv/snapshots/mandoc-%{version}.tar.gz -# PATCH-FEATURE-UPSTREAM empty_w-manpath.patch gh#neovim/neovim#11794 mcepl@suse.com -# Add man -w producing manpath (among many other things) -Patch0: 1.14.5-master.patch -# PATCH-FIX-openSUSE looks like newer gcc doesn't like those duplicated dummy variables lnussel@suse.com -Patch1: mandoc-1.14.5-dummy.diff BuildRequires: zlib-devel Provides: man = %{version} -Conflicts: man Conflicts: groff Conflicts: groff-full Conflicts: makewhat +Conflicts: man # file triggers use rpm.execute() Conflicts: rpm < 4.15 @@ -49,8 +44,7 @@ It includes a man(1) manual viewer and additional tools. For general information, see <http://mandoc.bsd.lv/>. %prep -%setup -q -%autopatch -p1 +%autosetup -p1 %build %{?!make_build:%define make_build make %{?_smp_mflags} V=1 VERBOSE=1}
command