From 4a2433bd1fdc6d4b03f0dc3ebab568ac8f9ed6548976d5e0ebb1fb3fdb34562f Mon Sep 17 00:00:00 2001 From: Matej Cepl Date: Sat, 25 Sep 2021 19:26:08 +0000 Subject: [PATCH] Accepting request 921372 from home:mcepl:branches:Documentation - 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 OBS-URL: https://build.opensuse.org/request/show/921372 OBS-URL: https://build.opensuse.org/package/show/Documentation/mandoc?expand=0&rev=12 --- 1.14.5-master.patch | 7696 -------------------------------------- mandoc-1.14.5-dummy.diff | 26 - mandoc-1.14.5.tar.gz | 3 - mandoc-1.14.6.tar.gz | 3 + mandoc.changes | 207 + mandoc.spec | 14 +- 6 files changed, 214 insertions(+), 7735 deletions(-) delete mode 100644 1.14.5-master.patch delete mode 100644 mandoc-1.14.5-dummy.diff delete mode 100644 mandoc-1.14.5.tar.gz create mode 100644 mandoc-1.14.6.tar.gz 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 <span> rather than <div> -+ * 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 "word<tab>word" Ta word Macro<eol> -+ * -T html: remove some spurious line breaks, in particular inside <pre> -+ --- 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 <body> to the max-width given for <html>. - 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 <kristaps@bsd.lv> -- * Copyright (c) 2014, 2015, 2016, 2017, 2018 Ingo Schwarze <schwarze@usta.de> -+ * Copyright (c) 2014-2019 Ingo Schwarze <schwarze@usta.de> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -77,7 +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("<form action=\"/%s\" method=\"get\">\n" -+ printf("<form action=\"/%s\" method=\"get\" " -+ "autocomplete=\"off\" autocapitalize=\"none\">\n" - " <fieldset>\n" - " <legend>Manual Page Search Parameters</legend>\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("<p>"); -- puts(msg); -+ puts(user_msg); - puts("</p>"); - 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 <stdint.h>, -+# which may require this line: -+CC=gcc -+ -+# IBM AIX may need: -+CC=xlc -+ - # For -Tutf8 and -Tlocale operation, mandoc(1) requires <locale.h> - # providing setlocale(3) and <wchar.h> 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 <stdint.h>, --# 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 <kristaps@bsd.lv> -- * Copyright (c) 2014, 2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2014,2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <string.h> - - #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 <kristaps@bsd.lv> -- * Copyright (c) 2013,2014,2015,2017,2018 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2013-2015,2017,2018,2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> -- * Copyright (c) 2010-2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2010-2012, 2014-2020 Ingo Schwarze <schwarze@openbsd.org> - * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any -@@ -21,6 +21,7 @@ - #include <sys/types.h> - #include <sys/ioctl.h> - #include <sys/param.h> /* MACHINE */ -+#include <sys/stat.h> - #include <sys/wait.h> - - #include <assert.h> -@@ -31,6 +32,7 @@ - #include <errno.h> - #include <fcntl.h> - #include <glob.h> -+#include <limits.h> - #if HAVE_SANDBOX_INIT - #include <sandbox.h> - #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 = "<stdin>"; -- 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 = "<stdin>"; -+ -+ 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 <jmc@openbsd.org> --.\" Copyright (c) 2010, 2011, 2014-2018 Ingo Schwarze <schwarze@openbsd.org> -+.\" Copyright (c) 2010, 2011, 2014-2020 Ingo Schwarze <schwarze@openbsd.org> - .\" - .\" 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 <kristaps@bsd.lv> -- * Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -27,10 +27,12 @@ - #include <string.h> - - #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 <kristaps@bsd.lv> -- * Copyright (c) 2010, 2012-2018 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> --.\" Copyright (c) 2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org> -+.\" Copyright (c) 2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org> - .\" - .\" Permission to use, copy, modify, and distribute this software for any - .\" purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> -- * Copyright (c) 2011-2015, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2011-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> -- * Copyright (c) 2012-2018 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2012-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <schwarze@openbsd.org> -+.\" -+.\" Permission to use, copy, modify, and distribute this software for any -+.\" purpose with or without fee is hereby granted, provided that the above -+.\" 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 <kristaps@bsd.lv> -- * Copyright (c) 2014,2015,2016,2017,2018 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2014-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -29,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 <kristaps@bsd.lv> -- * Copyright (c) 2011-2018 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2011-2020 Ingo Schwarze <schwarze@openbsd.org> - * Copyright (c) 2016 Ed Maste <emaste@freebsd.org> - * - * 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 <schwarze@openbsd.org> -+ * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org> - * Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv> - * - * Permission to use, copy, modify, and distribute this software for any -@@ -21,20 +21,19 @@ - #include <sys/stat.h> - - #include <ctype.h> --#if HAVE_ERR --#include <err.h> --#endif -+#include <errno.h> - #include <limits.h> - #include <stdio.h> - #include <stdlib.h> - #include <string.h> - - #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 <kristaps@bsd.lv> --.\" Copyright (c) 2010, 2011, 2013-2018 Ingo Schwarze <schwarze@openbsd.org> -+.\" Copyright (c) 2010, 2011, 2013-2020 Ingo Schwarze <schwarze@openbsd.org> - .\" - .\" Permission to use, copy, modify, and distribute this software for any - .\" purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> -- * Copyright (c) 2012, 2014-2018 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2012, 2014-2019 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> -- * Copyright (c) 2014-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2014-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <PRE>) 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 <kristaps@bsd.lv> -- * Copyright (c) 2010, 2012-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> -- * Copyright (c) 2010, 2012-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2010, 2012-2020 Ingo Schwarze <schwarze@openbsd.org> - * Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de> - * - * 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 <kristaps@bsd.lv> -- * Copyright (c) 2010-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2010-2020 Ingo Schwarze <schwarze@openbsd.org> - * Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org> - * - * 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 <bsd.regress.mk> ---- 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 <beta> -+inline [beta] - .EQ - delim offgamma - .EN --inline <delta> -+inline [delta] - .EQ - delim onepsilon - .EN --inline <zeta> -+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 <alpha> inline <beta> <gamma> inline <delta> <epsilon> -- inline <zeta> final text -+ initial text <alpha> inline <beta> <gamma> inline [delta] <epsilon> -+ inline <zeta> inline $eta$ inline <theta> 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 @@ - <mrow><mrow><mi>unquoted --words:</mi></mrow><mi>sin</mi><mi>cos</mi><mi>tan</mi> --<mi>sec</mi><mi>csc</mi> --<mi>asin</mi><mi>acos</mi><mi>atan</mi><mi>asec</mi> --<mi>acsc</mi><mi>sinh</mi> --<mi>cosh</mi><mi>tanh</mi><mi>coth</mi><mi>arc</mi> --<mi>max</mi><mi>min</mi><mi>lim</mi><mi>log</mi><mi>ln</mi><mi>exp</mi><mi>Re</mi><mi>Im</mi><mi>and</mi><mi>if</mi><mi>for</mi><mi>det</mi><mo>—</mo><mrow><mi>quoted --words:</mi></mrow> --<mi fontstyle="italic">sin</mi><mi fontstyle="italic">cos</mi> --<mi fontstyle="italic">tan</mi><mi fontstyle="italic">sec</mi><mi fontstyle="italic">csc</mi><mi fontstyle="italic">asin</mi><mi fontstyle="italic">acos</mi><mi fontstyle="italic">atan</mi><mi fontstyle="italic">asec</mi><mi fontstyle="italic">acsc</mi><mi fontstyle="italic">sinh</mi><mi fontstyle="italic">cosh</mi><mi fontstyle="italic">tanh</mi><mi fontstyle="italic">coth</mi><mi fontstyle="italic">arc</mi><mi fontstyle="italic">max</mi><mi fontstyle="italic">min</mi><mi fontstyle="italic">lim</mi><mi fontstyle="italic">log</mi><mi fontstyle="italic">ln</mi><mi fontstyle="italic">exp</mi><mi fontstyle="italic">Re</mi><mi fontstyle="italic">Im</mi><mi fontstyle="italic">and</mi><mi fontstyle="italic">if</mi><mi fontstyle="italic">for</mi><mi fontstyle="italic">det</mi><mo>—</mo><mrow><mi>font --operations:</mi></mrow> --<mrow><mi>sin</mi></mrow><mrow><mi fontweight="bold">sin</mi></mrow><mo>—</mo><mrow><mi>superstring:</mi></mrow><mi fontstyle="italic">sinus</mi><mo>—</mo><mrow><mi>composite --word:</mi></mrow> --<mi>tan</mi><mo>=</mo><mi fontstyle="italic">sin</mi><mo>/</mo> --<mi fontstyle="italic">cos</mi></mrow> -+words:</mi></mrow><mi>sin</mi><mi>cos</mi><mi>tan</mi><mi>sec</mi><mi>csc</mi><mi>asin</mi><mi>acos</mi><mi>atan</mi><mi>asec</mi><mi>acsc</mi><mi>sinh</mi><mi>cosh</mi><mi>tanh</mi><mi>coth</mi><mi>arc</mi><mi>max</mi><mi>min</mi><mi>lim</mi><mi>log</mi><mi>ln</mi><mi>exp</mi><mi>Re</mi><mi>Im</mi><mi>and</mi><mi>if</mi><mi>for</mi><mi>det</mi><mo>—</mo><mrow><mi>quoted -+words:</mi></mrow><mi fontstyle="italic">sin</mi><mi fontstyle="italic">cos</mi><mi fontstyle="italic">tan</mi><mi fontstyle="italic">sec</mi><mi fontstyle="italic">csc</mi><mi fontstyle="italic">asin</mi><mi fontstyle="italic">acos</mi><mi fontstyle="italic">atan</mi><mi fontstyle="italic">asec</mi><mi fontstyle="italic">acsc</mi><mi fontstyle="italic">sinh</mi><mi fontstyle="italic">cosh</mi><mi fontstyle="italic">tanh</mi><mi fontstyle="italic">coth</mi><mi fontstyle="italic">arc</mi><mi fontstyle="italic">max</mi><mi fontstyle="italic">min</mi><mi fontstyle="italic">lim</mi><mi fontstyle="italic">log</mi><mi fontstyle="italic">ln</mi><mi fontstyle="italic">exp</mi><mi fontstyle="italic">Re</mi><mi fontstyle="italic">Im</mi><mi fontstyle="italic">and</mi><mi fontstyle="italic">if</mi><mi fontstyle="italic">for</mi><mi fontstyle="italic">det</mi><mo>—</mo><mrow><mi>font -+operations:</mi></mrow><mrow><mi>sin</mi></mrow><mrow><mi fontweight="bold">sin</mi></mrow><mo>—</mo><mrow><mi>superstring:</mi></mrow><mi fontstyle="italic">sinus</mi><mo>—</mo><mrow><mi>composite -+word:</mi></mrow><mi>tan</mi><mo>=</mo><mi fontstyle="italic">sin</mi><mo>/</mo><mi fontstyle="italic">cos</mi></mrow> ---- a/regress/eqn/nullary/symbol.out_html -+++ b/regress/eqn/nullary/symbol.out_html -@@ -1,6 +1,4 @@ - <mrow><mrow><mi>unquoted - words:</mi></mrow><mo>ε</mo><mo>′</mo><mo>—</mo><mrow><mi>quoted --words:</mi></mrow> --<mi fontstyle="italic">epsilon</mi><mi fontstyle="italic">prime</mi><mo>—</mo><mrow><mi>composite --word:</mi></mrow> --<mi fontstyle="italic">epsilon</mi><mo>-</mo><mi fontstyle="italic">prime</mi></mrow> -+words:</mi></mrow><mi fontstyle="italic">epsilon</mi><mi fontstyle="italic">prime</mi><mo>—</mo><mrow><mi>composite -+word:</mi></mrow><mi fontstyle="italic">epsilon</mi><mo>-</mo><mi fontstyle="italic">prime</mi></mrow> ---- a/regress/man/HP/literal.out_html -+++ b/regress/man/HP/literal.out_html -@@ -1,4 +1,3 @@ --BEGINTEST before hanged paragraph - <p class="Pp HP">tag indented text</p> - <p class="Pp">regular paragraph</p> - <pre> -@@ -17,4 +16,3 @@ paragraph - </pre> - regular text - <br/> --ENDTEST ---- a/regress/man/IP/literal.out_html -+++ b/regress/man/IP/literal.out_html -@@ -1,4 +1,3 @@ --BEGINTEST before indentation - <dl class="Bl-tag"> - <dt>tag</dt> - <dd>indented regular text</dd> -@@ -27,7 +26,7 @@ regular text - <section class="Ss"> - <h2 class="Ss" id="literal_into_indented_paragraph"><a class="permalink" href="#literal_into_indented_paragraph">literal - into indented paragraph</a></h2> --regular text -+<p class="Pp">regular text</p> - <pre> - literal - text -@@ -47,7 +46,7 @@ text - <section class="Ss"> - <h2 class="Ss" id="literal_out_of_indented_paragraph"><a class="permalink" href="#literal_out_of_indented_paragraph">literal - out of indented paragraph</a></h2> --regular text -+<p class="Pp">regular text</p> - <dl class="Bl-tag"> - <dt>tag</dt> - <dd>indented regular text -@@ -65,4 +64,3 @@ paragraph - </pre> - regular text - <br/> --ENDTEST ---- a/regress/man/RS/literal.out_html -+++ b/regress/man/RS/literal.out_html -@@ -1,6 +1,5 @@ --BEGINTEST --<br/> --initial regular text -+ <br/> -+ initial regular text</p> - <pre> - literal text - before display -@@ -15,6 +14,5 @@ This is a very long line that would wrap - literal text - after display - </pre> --final regular text --<br/> --ENDTEST -+<p class="Pp">final regular text -+ <br/> ---- a/regress/man/RS/paragraph.out_html -+++ b/regress/man/RS/paragraph.out_html -@@ -1,8 +1,6 @@ --BEGINTEST before paragraph - <p class="Pp">regular paragraph</p> - <div class="Bd-indent">indented paragraph - <p class="Pp">nested paragraph</p> - </div> - regular text after display - <br/> --ENDTEST ---- a/regress/man/SH/paragraph.out_html -+++ b/regress/man/SH/paragraph.out_html -@@ -1,10 +1,8 @@ --BEGINTEST - </section> - <section class="Sh"> - <h1 class="Sh" id="DESCRIPTION"><a class="permalink" href="#DESCRIPTION">DESCRIPTION</a></h1> --This text immediately follows a section header. -+<p class="Pp">This text immediately follows a section header.</p> - <p class="Pp">This is a paragraph.</p> - </section> - <section class="Sh"> - <h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1> --ENDTEST ---- a/regress/man/SS/paragraph.out_html -+++ b/regress/man/SS/paragraph.out_html -@@ -1,11 +1,9 @@ --BEGINTEST - <section class="Ss"> - <h2 class="Ss" id="First_subsection"><a class="permalink" href="#First_subsection">First - subsection</a></h2> --This text immediately follows a subsection header. -+<p class="Pp">This text immediately follows a subsection header.</p> - <p class="Pp">This is a paragraph.</p> - </section> - <section class="Ss"> - <h2 class="Ss" id="Second_subsection"><a class="permalink" href="#Second_subsection">Second - subsection</a></h2> --ENDTEST ---- a/regress/man/SY/literal.out_html -+++ b/regress/man/SY/literal.out_html -@@ -1,6 +1,5 @@ --BEGINTEST --<br/> --initial regular text -+ <br/> -+ initial regular text</p> - <table class="Nm"> - <tr> - <td><code class="Nm">command</code></td> -@@ -26,6 +25,5 @@ before display - literal text - after display - </pre> --final regular text --<br/> --ENDTEST -+<p class="Pp">final regular text -+ <br/> ---- 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 - <dl class="Bl-tag"> - <dt>tag</dt> - <dd>regular indented text</dd> -@@ -24,4 +23,3 @@ paragraph - </pre> - regular text - <br/> --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 <bsd.regress.mk> ---- a/regress/mdoc/Bd/nf.out_html -+++ b/regress/mdoc/Bd/nf.out_html -@@ -1,9 +1,8 @@ --BEGINTEST initial text - <pre> - after .nf - request - </pre> --after .fi request -+<p class="Pp">after .fi request</p> - <div class="Bd Pp"> - <pre> - in unfilled -@@ -19,4 +18,3 @@ in filled block - </div> - after end of filled block - <br/> --ENDTEST ---- a/regress/mdoc/Bd/paragraph.out_html -+++ b/regress/mdoc/Bd/paragraph.out_html -@@ -1,4 +1,3 @@ --BEGINTEST initial text - <p class="Pp">normal paragraph</p> - <div class="Bd Pp">filled display - <p class="Pp">paragraph in display</p> -@@ -16,4 +15,3 @@ paragraph - </div> - again back to normal - <br/> --ENDTEST ---- a/regress/mdoc/Bf/paragraph.out_html -+++ b/regress/mdoc/Bf/paragraph.out_html -@@ -1,6 +1,4 @@ --BEGINTEST - <p class="Pp">normal text</p> - <div class="Bf Li">literal text - <p class="Pp">literal paragraph</p> - </div> --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 - <p class="Pp">preceding paragraph</p> - <div class="Bd Bd-indent">spacing in and around one-line displays</div> - empty display: - <div class="Bd Bd-indent"></div> - following text - <br/> --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 <bsd.regress.mk> ---- 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 --<br/> --initial reference: <cite class="Rs"><span class="RsA">author name</span>, -- <i class="RsB">book title</i>.</cite> -+ <br/> -+ initial reference: <cite class="Rs"><span class="RsA">author name</span>, -+ <i class="RsB">book title</i>.</cite></p> - <p class="Pp">in a paragraph: <cite class="Rs"><span class="RsA">another - author</span>, <i class="RsB">another book</i>.</cite></p> - </section> - <section class="Sh"> - <h1 class="Sh" id="SEE_ALSO"><a class="permalink" href="#SEE_ALSO">SEE - ALSO</a></h1> --initial reference: -+<p class="Pp">initial reference:</p> - <p class="Pp"><cite class="Rs"><span class="RsA">author name</span>, - <i class="RsB">book title</i>.</cite></p> - <p class="Pp">in a paragraph:</p> - <p class="Pp"><cite class="Rs"><span class="RsA">another author</span>, - <i class="RsB">another book</i>.</cite></p> - <pre> --ENDTEST ---- a/regress/mdoc/Sh/paragraph.out_html -+++ b/regress/mdoc/Sh/paragraph.out_html -@@ -1,11 +1,9 @@ --BEGINTEST - <p class="Pp">descriptive text</p> - <section class="Ss"> - <h2 class="Ss" id="Subsection"><a class="permalink" href="#Subsection">Subsection</a></h2> --initial subsection text -+<p class="Pp">initial subsection text</p> - <p class="Pp">subsection paragraph</p> - </section> - </section> - <section class="Sh"> - <h1 class="Sh" id="EXAMPLES"><a class="permalink" href="#EXAMPLES">EXAMPLES</a></h1> --ENDTEST ---- a/regress/regress.pl -+++ b/regress/regress.pl -@@ -86,8 +86,12 @@ sub syshtml ($@) { - if (!$state && s/.*<math class="eqn">//) { - $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: <b><i>bolditalic</i></b><b>bold</b><i>italic</i>roman - letters: <b>bold</b><i>italic</i><b>back</b><b><i>bolditalic</i></b>roman - multiletter: <b>bold</b>empty<i>italic</i>back<b><i>bolditalic</i></b>roman - typewriter: <span class="Li">roman</span><b>bold</b><span class="Li">roman</span><i>italic</i>roman --ENDTEST ---- a/regress/roff/ft/badargs.out_html -+++ b/regress/roff/ft/badargs.out_html -@@ -1,9 +1,6 @@ --BEGINTEST --<br/> --default font <i></i><i>italic</i> <b><i></i></b><b><i>bold italic</i></b> -- <span class="Li"></span><span class="Li">typeqriter</span> -- <span class="Li"></span> <span class="Li">roman</span> <b></b><b>bold</b> -- <i></i> <i>italic</i> <b></b><b>bold</b> <b>still bold</b> -- <i></i><i>italic</i> <i></i><i>back to bold</i> <i></i><i>back to italic</i> --<br/> --ENDTEST -+ <br/> -+ default font <i>italic</i> <b><i>bold italic</i></b> -+ <span class="Li">typeqriter</span> <span class="Li">roman</span> <b>bold</b> -+ <i>italic</i> <b>bold</b> <b>still bold</b> <i>italic</i> <i>back to -+ bold</i> <i>back to italic</i> -+ <br/> ---- a/regress/roff/sp/fill-man.out_html -+++ b/regress/roff/sp/fill-man.out_html -@@ -1,4 +1,3 @@ --BEGINTEST in fill mode: - <p class="Pp">switch to no-fill mode:</p> - <pre> - in no-fill mode: -@@ -6,4 +5,3 @@ in no-fill mode: - back to - fill mode: - </pre> --ENDTEST ---- a/regress/roff/string/dotT.out_html -+++ b/regress/roff/string/dotT.out_html -@@ -1,5 +1,3 @@ --BEGINTEST - <p class="Pp">We are using the html device.</p> - <p class="Pp">The device name can be overridden.</p> - <pre> --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 <rea@FreeBSD.org> -+ -+ -+ -+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 <kristaps@bsd.lv> -- * Copyright (c) 2010-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2010-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> -- * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <schwarze@openbsd.org> -+ * Copyright (c) 2015,2016,2018,2019,2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -18,9 +18,8 @@ - - #include <sys/types.h> - --#if HAVE_ERR --#include <err.h> --#endif -+#include <assert.h> -+#include <errno.h> - #include <limits.h> - #include <signal.h> - #include <stddef.h> -@@ -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 <schwarze@openbsd.org> -+ * Copyright (c) 2015, 2018, 2019, 2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <assert.h> - #include <ctype.h> -+#include <stdint.h> - #include <stdio.h> - #include <stdlib.h> - #include <string.h> -@@ -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 <string.h> - - #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 <kristaps@bsd.lv> -- * Copyright (c) 2011-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2011-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <kristaps@bsd.lv> -- * Copyright (c) 2013-2015, 2017-2019 Ingo Schwarze <schwarze@openbsd.org> -+ * Copyright (c) 2013-2015, 2017-2020 Ingo Schwarze <schwarze@openbsd.org> - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above -@@ -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 <mcepl@suse.com> + +- 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<one-digit-section>/<name>.<full-section>" + in addition to the already supported "man<full-section>/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 <span> rather than <div> + * 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 <title> 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}