The branch stable/14 has been updated by ziaee:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=a1f4ccd6c79470eb0180f4d9a012b24c267d2574

commit a1f4ccd6c79470eb0180f4d9a012b24c267d2574
Author:     Alexander Ziaee <[email protected]>
AuthorDate: 2025-08-15 19:03:22 +0000
Commit:     Alexander Ziaee <[email protected]>
CommitDate: 2026-02-06 00:30:48 +0000

    mandoc: Vendor import of upstream at 2025-07-27
    
    Groff Compat Edition -- Interesting changes:
    + italics in section/subsection headers are now also bold, like groff
    + display "LOCAL" in response to invalid section numbers, like groff
    + tbl(7) rendering has been tweaked to be more like groff
    + scaling has been improved to better render poorly generated manuals
    + display "UNTITLED" in response to invalid man(7) titles, like groff
    + improve mandocd error handling of broken pipes
    + manual footers now always show NAME(SECTION) on the right, like groff
    
    MFC after:      3 days
    
    (cherry picked from commit 06410c1b51637e5e1f392d553b5008948af58014)
---
 contrib/mandoc/Makefile        |   2 +-
 contrib/mandoc/TODO            |  11 +-
 contrib/mandoc/catman.8        | 238 ++++++++++++++++++++++++++++++----
 contrib/mandoc/catman.c        | 231 +++++++++++++++++++++++++++++----
 contrib/mandoc/gmdiff          |   4 +-
 contrib/mandoc/man.7           | 151 +++++++++++++---------
 contrib/mandoc/man.options.1   |   9 +-
 contrib/mandoc/man_html.c      |  19 ++-
 contrib/mandoc/man_term.c      | 133 +++++++++----------
 contrib/mandoc/man_validate.c  |  18 ++-
 contrib/mandoc/mandoc.1        |  31 ++---
 contrib/mandoc/mandoc.css      |   4 +-
 contrib/mandoc/mandocd.8       |  26 +++-
 contrib/mandoc/mandocd.c       |  34 ++++-
 contrib/mandoc/manpath.c       |  11 +-
 contrib/mandoc/mdoc_html.c     |  15 ++-
 contrib/mandoc/mdoc_man.c      |   5 +-
 contrib/mandoc/mdoc_markdown.c |  10 +-
 contrib/mandoc/mdoc_term.c     | 122 ++++++++++--------
 contrib/mandoc/mdoc_validate.c |  10 +-
 contrib/mandoc/out.c           |  63 ++++++---
 contrib/mandoc/out.h           |  24 ++--
 contrib/mandoc/roff.7          | 195 +++++++++++++++++++++-------
 contrib/mandoc/roff_term.c     |  31 +++--
 contrib/mandoc/tbl.h           |  16 +--
 contrib/mandoc/tbl_html.c      |  34 +----
 contrib/mandoc/tbl_layout.c    |  10 +-
 contrib/mandoc/tbl_term.c      | 287 +++++++++++++++++++++++------------------
 contrib/mandoc/term.c          | 201 ++++++++++++++++-------------
 contrib/mandoc/term.h          |  38 +++---
 contrib/mandoc/term_ascii.c    |  91 +++++++------
 contrib/mandoc/term_ps.c       |  18 +--
 contrib/mandoc/term_tab.c      |  27 +---
 33 files changed, 1365 insertions(+), 754 deletions(-)

diff --git a/contrib/mandoc/Makefile b/contrib/mandoc/Makefile
index 7ec34a560504..0830c9f289a3 100644
--- a/contrib/mandoc/Makefile
+++ b/contrib/mandoc/Makefile
@@ -15,7 +15,7 @@
 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-VERSION = 1.14.6s20250613
+VERSION = 1.14.6s20250727
 
 # === LIST OF FILES ====================================================
 
diff --git a/contrib/mandoc/TODO b/contrib/mandoc/TODO
index 3f5a449af68f..5d582b85b154 100644
--- a/contrib/mandoc/TODO
+++ b/contrib/mandoc/TODO
@@ -1,6 +1,6 @@
 ************************************************************************
 * Official mandoc TODO.
-* $Id: TODO,v 1.337 2025/04/08 21:53:14 schwarze Exp $
+* $Id: TODO,v 1.338 2025/07/22 13:36:54 schwarze Exp $
 ************************************************************************
 
 Many issues are annotated for difficulty as follows:
@@ -505,6 +505,15 @@ are mere guesses, and some may be wrong.
   re-reported by tb@ Mon, 16 Mar 2015 16:47:21 +0100
   loc **  exist **  algo **  size *  imp **
 
+- Check for bad line breaks caused by PostScript and PDF using variable-
+  width fonts, for example in .Bl -width "string".  The difficulty line
+  below describes a naive solution by simply scaling up widths internally
+  or adding default spacing (like in terminal output).  If fixes are
+  needed in width measurements, it might be a bit harder, but likely
+  not unreasonably so.
+  reported by Jan Stary 20 May 2024 10:19:01 +0200
+  loc *  exist *  algo **  size *  imp **
+
 --- HTML issues --------------------------------------------------------
 
 - support the idiom .TP .IP .TP for multi-paragraph list item bodies
diff --git a/contrib/mandoc/catman.8 b/contrib/mandoc/catman.8
index 903fa1fa82c9..c0f14872afc6 100644
--- a/contrib/mandoc/catman.8
+++ b/contrib/mandoc/catman.8
@@ -1,6 +1,6 @@
-.\"    $Id: catman.8,v 1.8 2017/03/18 19:56:01 schwarze Exp $
+.\" $Id: catman.8,v 1.15 2025/07/13 14:15:26 schwarze Exp $
 .\"
-.\" Copyright (c) 2017 Ingo Schwarze <[email protected]>
+.\" Copyright (c) 2017, 2025 Ingo Schwarze <[email protected]>
 .\"
 .\" Permission to use, copy, modify, and distribute this software for any
 .\" purpose with or without fee is hereby granted, provided that the above
@@ -14,7 +14,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: March 18 2017 $
+.Dd $Mdocdate: July 13 2025 $
 .Dt CATMAN 8
 .Os
 .Sh NAME
@@ -37,9 +37,9 @@ and
 format and formats all of them, storing the formatted versions in
 the same relative paths below
 .Ar dstdir .
-Subdirectories of
+Unless they already exist,
 .Ar dstdir
-are created as needed.
+itself and any required subdirectories are created.
 Existing files are not explicitly deleted, but possibly overwritten.
 .Pp
 The options are as follows:
@@ -71,6 +71,18 @@ output mode, the
 .Cm fragment
 output option is implied.
 Other output options are not supported.
+.It Fl v
+Verbose mode, printing additional information to standard error output.
+Specifying this once prints a summary about the number of files
+and directories processed at the end of the iteration.
+Specifying it twice additionally prints debugging information
+about the backchannel from
+.Xr mandocd 8
+to
+.Nm
+that is used to limit the number of files in flight at any given time.
+For details, see
+.Sx DIAGNOSTICS .
 .El
 .Sh IMPLEMENTATION NOTES
 Since this version avoids
@@ -87,33 +99,209 @@ implementations.
 .Sh EXIT STATUS
 .Ex -std
 .Pp
-Possible errors include:
-.Bl -bullet
-.It
-missing, invalid, or excessive command line arguments
-.It
-failure to change the current working directory to
+Failures while trying to open individual manual pages for reading,
+to save individual formatted files to the file system,
+or even to read or create subdirectories do not cause
+.Nm
+to return an error exit status.
+In such cases,
+.Nm
+simply continues with the next file or subdirectory.
+.Sh DIAGNOSTICS
+Some fatal errors cause
+.Nm
+to exit before the iteration over input files is even started:
+.Bl -tag -width Ds -offset indent
+.It unknown option \-\- Ar option
+An invalid option was passed on the command line.
+.It missing arguments: srcdir and dstdir
+No argument was provided.
+Both
 .Ar srcdir
-.It
-failure to open
+and
 .Ar dstdir
-.It
-communication failure with
+are mandatory.
+.It missing argument: dstdir
+Only one argument was provided.
+The second argument,
+.Ar dstdir ,
+is mandatory, too.
+.It too many arguments: Ar third argument
+Three or more arguments were provided, but only two are supported.
+.It Sy socketpair : Ar reason
+The sockets needed for communication with
+.Xr mandocd 8
+could not be created, for example due to file descriptor or memory exhaustion.
+.It Sy fork : Ar reason
+The new process needed to run
 .Xr mandocd 8
-.It
-resource exhaustion, for example file descriptor, process table,
-or memory exhaustion
+could not be created, for example due to process table exhaustion
+or system resource limits.
+.It Sy exec Ns Po Sy mandocd Pc : Ar reason
+The
+.Xr mandocd 8
+child program could not be started, for example because it is not in the
+.Ev PATH
+or has no execute permission.
+.It Sy mkdir No destination Ar dstdir : reason
+The
+.Ar dstdir
+does not exist and could not be created, for example because
+the parent directory does not exist or permission is denied.
+.It Sy open No destination Ar dstdir : reason
+The
+.Ar dstdir
+could not be opened for reading, for example because
+it is not a directory or permission is denied.
+.It Sy chdir No to source Ar srcdir : reason
+The current working directory could not be changed to
+.Ar srcdir ,
+for example because it does not exist, it is not a directory,
+or permission is denied.
+.It Sy fts_open : Ar reason
+Starting the iteration was attempted but failed,
+for example due to memory exhaustion.
 .El
 .Pp
-Except for memory exhaustion and similar system-level failures,
-failures while trying to open, read, parse, or format individual
-manual pages, to save individual formatted files to the file system,
-or even to create directories do not cause
+Some fatal errors cause the iteration over input files to be aborted
+prematurely:
+.Bl -tag -width Ds -offset indent
+.It FATAL: Sy fts_read : Ar reason
+A call to
+.Xr fts_read 3
+returned
+.Dv NULL ,
+meaning that the iteration failed before being complete.
+.It FATAL: mandocd child died: got Ar SIGNAME
+This message appears if
 .Nm
-to return an error exit status.
-In such cases,
+gets the
+.Dv SIGCHLD
+or
+.Dv SIGPIPE
+signal, most likely due to a fatal bug in
+.Xr mandocd 8 .
+.It FATAL: Sy sendmsg : Ar reason
+The file descriptors needed to process one of the manual pages
+could not be sent to
+.Xr mandocd 8 ,
+for example because
+.Xr mandocd 8
+could not be started or died unexpectedly.
+.It FATAL: Sy recv : Ar reason
+Trying to read a reply message from
+.Xr mandocd 8
+failed, most likely because
+.Xr mandocd 8
+unexpectedly died or closed the socket.
+.It FATAL: signal Ar SIGNAME
+This message appears if
+.Nm
+gets a
+.Dv SIGHUP ,
+.Dv SIGINT ,
+or
+.Dv SIGTERM
+signal, for example because the user deliberately killed it.
+.El
+.Pp
+Some non-fatal errors cause a single subdirectory to be skipped.
+The iteration is not aborted but continues with the next subdirectory,
+and the exit status is unaffected:
+.Bl -tag -width Ds -offset indent
+.It directory Ar subdirectory No unreadable : Ar reason
+A directory below
+.Ar srcdir
+could not be read and is skipped.
+.It directory Ar subdirectory No causes cycle
+A directory below
+.Ar srcdir
+is skipped because it would cause cyclic processing.
+.It Sy mkdirat Ar subdirectory : reason
+A required directory below
+.Ar dstdir
+does not exist and could not be created.
+The corresponding subdirectory below
+.Ar srcdir
+is skipped.
+.El
+.Pp
+Some non-fatal errors cause a single source file to be skipped.
+The iteration is not aborted but continues with the next file,
+and the exit status is unaffected:
+.Bl -tag -width Ds -offset indent
+.It file Ar filename : reason
+The function
+.Xr fts_read 3
+reported a non-fatal error with respect to
+.Ar filename .
+.It file Ar filename : No not a regular file
+For example, it might be a symbolic link or a device file.
+.It Sy open Ar filename No for reading : Ar reason
+A file below
+.Ar srcdir
+could not be read, for example due to permission problems.
+.It Sy openat Ar filename No for writing : Ar reason
+A file below
+.Ar dstdir
+could not be created or truncated, for example due to permission problems.
+.El
+.Pp
+If errors occur, the applicable summary messages appear
+after the end of the iteration:
+.Pp
+.Bl -tag -width Ds -offset indent -compact
+.It skipped Ar number No directories due to errors
+.It skipped Ar number No files due to errors
+.It processing aborted due to fatal error
+.El
+.Pp
+If the
+.Fl v
+flag is specified, the following summary message also appears:
+.Bl -tag -width Ds -offset indent
+.It processed Ar nfiles No files in Ar ndirs No directories
+A file is counted if it could be opened for reading and the
+corresponding output file could be opened for writing;
+this does not necessarily mean that it is a useful manual page.
+A directory is counted if it could be opened for reading and the
+corresponding output directory existed or could be created;
+this does not necessarily mean that any files could be
+processed inside.
+.El
+.Pp
+If the
+.Fl v
+flag is specified twice, the following messages also appear:
+.Bl -tag -width Ds -offset indent
+.It allowing up to Ar number No files in flight
+This is printed at the beginning of the iteration,
+showing the maximum number of files that
+.Nm
+allows to be in flight at any given time.
+.It files in flight: Ar old No \- Ar decrement No = Ar new
+This message is printed when
+.Nm
+learns about
+.Xr mandocd 8
+accepting more than one file at the same time.
+The three numbers printed are the old number of files in flight,
+the amount this number is being reduced, and the resulting
+new number of files in flight.
+.It waiting for Ar number No files in flight
+This message is printed at the end of the iteration, after
+.Nm
+has submitted all files to
+.Xr mandocd 8
+that it intends to.
+THe message informs about the number of files still in flight
+at this point.
+The
 .Nm
-will simply continue with the next file or subdirectory.
+program then waits until
+.Xr mandocd 8
+has accepted them all or until an error occurs.
+.El
 .Sh SEE ALSO
 .Xr mandoc 1 ,
 .Xr mandocd 8
diff --git a/contrib/mandoc/catman.c b/contrib/mandoc/catman.c
index e46613eb0e8c..c9eda18bf71c 100644
--- a/contrib/mandoc/catman.c
+++ b/contrib/mandoc/catman.c
@@ -1,7 +1,7 @@
-/*     $Id: catman.c,v 1.23 2021/10/15 15:04:02 schwarze Exp $ */
+/* $Id: catman.c,v 1.30 2025/07/13 14:15:26 schwarze Exp $ */
 /*
+ * Copyright (c) 2017, 2025 Ingo Schwarze <[email protected]>
  * Copyright (c) 2017 Michael Stapelberg <[email protected]>
- * Copyright (c) 2017 Ingo Schwarze <[email protected]>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -25,6 +25,7 @@
 #include <sys/socket.h>
 #include <sys/stat.h>
 
+#include <assert.h>
 #if HAVE_ERR
 #include <err.h>
 #endif
@@ -35,26 +36,44 @@
 #else
 #include "compat_fts.h"
 #endif
+#include <signal.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
 
+int            verbose_flag = 0;
+sig_atomic_t   got_signal = 0;
+
 int     process_manpage(int, int, const char *);
 int     process_tree(int, int);
 void    run_mandocd(int, const char *, const char *)
                __attribute__((__noreturn__));
+void    signal_handler(int);
 ssize_t         sock_fd_write(int, int, int, int);
 void    usage(void) __attribute__((__noreturn__));
 
 
+void
+signal_handler(int signum)
+{
+       got_signal = signum;
+}
+
 void
 run_mandocd(int sockfd, const char *outtype, const char* defos)
 {
        char     sockfdstr[10];
+       int      len;
 
-       if (snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd) == -1)
+       len = snprintf(sockfdstr, sizeof(sockfdstr), "%d", sockfd);
+       if (len >= (int)sizeof(sockfdstr)) {
+               errno = EOVERFLOW;
+               len = -1;
+       }
+       if (len < 0)
                err(1, "snprintf");
        if (defos == NULL)
                execlp("mandocd", "mandocd", "-T", outtype,
@@ -109,10 +128,11 @@ sock_fd_write(int fd, int fd0, int fd1, int fd2)
         * to neither cause more than a handful of retries
         * in normal operation nor unnecessary delays.
         */
-       for (;;) {
-               if ((sz = sendmsg(fd, &msg, 0)) != -1 ||
-                   errno != EAGAIN)
+       while ((sz = sendmsg(fd, &msg, 0)) == -1) {
+               if (errno != EAGAIN) {
+                       warn("FATAL: sendmsg");
                        break;
+               }
                nanosleep(&timeout, NULL);
        }
        return sz;
@@ -125,14 +145,16 @@ process_manpage(int srv_fd, int dstdir_fd, const char 
*path)
        int      irc;
 
        if ((in_fd = open(path, O_RDONLY)) == -1) {
-               warn("open(%s)", path);
+               warn("open %s for reading", path);
+               fflush(stderr);
                return 0;
        }
 
        if ((out_fd = openat(dstdir_fd, path,
            O_WRONLY | O_NOFOLLOW | O_CREAT | O_TRUNC,
            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) {
-               warn("openat(%s)", path);
+               warn("openat %s for writing", path);
+               fflush(stderr);
                close(in_fd);
                return 0;
        }
@@ -142,20 +164,22 @@ process_manpage(int srv_fd, int dstdir_fd, const char 
*path)
        close(in_fd);
        close(out_fd);
 
-       if (irc < 0) {
-               warn("sendmsg");
-               return -1;
-       }
-       return 0;
+       return irc;
 }
 
 int
 process_tree(int srv_fd, int dstdir_fd)
 {
+       const struct timespec timeout = { 0, 10000000 };  /* 0.01 s */
+       const int        max_inflight = 16;
+
        FTS             *ftsp;
        FTSENT          *entry;
        const char      *argv[2];
        const char      *path;
+       int              inflight, irc, decr, fatal;
+       int              gooddirs, baddirs, goodfiles, badfiles;
+       char             dummy[1];
 
        argv[0] = ".";
        argv[1] = (char *)NULL;
@@ -166,13 +190,59 @@ process_tree(int srv_fd, int dstdir_fd)
                return -1;
        }
 
-       while ((entry = fts_read(ftsp)) != NULL) {
+       if (verbose_flag >= 2) {
+               warnx("allowing up to %d files in flight", max_inflight);
+               fflush(stderr);
+       }
+       inflight = fatal = gooddirs = baddirs = goodfiles = badfiles = 0;
+       while (fatal == 0 && got_signal == 0 &&
+           (entry = fts_read(ftsp)) != NULL) {
+               if (inflight >= max_inflight) {
+                       while (recv(srv_fd, dummy, sizeof(dummy), 0) == -1) {
+                               if (errno != EAGAIN) {
+                                       warn("FATAL: recv");
+                                       fatal = errno;
+                                       break;
+                               }
+                               nanosleep(&timeout, NULL);
+                       }
+                       if (fatal != 0)
+                               break;
+                       decr = 1;
+                       while ((irc = recv(srv_fd, dummy, sizeof(dummy),
+                           MSG_DONTWAIT)) > 0)
+                               decr++;
+                       assert(inflight >= decr);
+                       if (verbose_flag >= 2 && decr > 1) {
+                               warnx("files in flight: %d - %d = %d",
+                                   inflight, decr, inflight - decr);
+                               fflush(stderr);
+                       }
+                       inflight -= decr;
+                       if (irc == 0) {
+                               errno = ECONNRESET;
+                               inflight = -1;
+                       }
+                       if (errno != EAGAIN) {
+                               warn("FATAL: recv");
+                               fatal = errno;
+                               break;
+                       }
+               }
                path = entry->fts_path + 2;
                switch (entry->fts_info) {
                case FTS_F:
-                       if (process_manpage(srv_fd, dstdir_fd, path) == -1) {
-                               fts_close(ftsp);
-                               return -1;
+                       switch (process_manpage(srv_fd, dstdir_fd, path)) {
+                       case -1:
+                               fatal = errno;
+                               break;
+                       case 0:
+                               badfiles++;
+                               break;
+                       default:
+                               goodfiles++;
+                               inflight++;
+                               break;
                        }
                        break;
                case FTS_D:
@@ -180,25 +250,96 @@ process_tree(int srv_fd, int dstdir_fd)
                            mkdirat(dstdir_fd, path, S_IRWXU | S_IRGRP |
                              S_IXGRP | S_IROTH | S_IXOTH) == -1 &&
                            errno != EEXIST) {
-                               warn("mkdirat(%s)", path);
+                               warn("mkdirat %s", path);
+                               fflush(stderr);
                                (void)fts_set(ftsp, entry, FTS_SKIP);
-                       }
+                               baddirs++;
+                       } else
+                               gooddirs++;
                        break;
                case FTS_DP:
                        break;
+               case FTS_DNR:
+                       warnx("directory %s unreadable: %s",
+                           path, strerror(entry->fts_errno));
+                       fflush(stderr);
+                       baddirs++;
+                       break;
+               case FTS_DC:
+                       warnx("directory %s causes cycle", path);
+                       fflush(stderr);
+                       baddirs++;
+                       break;
+               case FTS_ERR:
+               case FTS_NS:
+                       warnx("file %s: %s",
+                           path, strerror(entry->fts_errno));
+                       fflush(stderr);
+                       badfiles++;
+                       break;
                default:
-                       warnx("%s: not a regular file", path);
+                       warnx("file %s: not a regular file", path);
+                       fflush(stderr);
+                       badfiles++;
                        break;
                }
        }
+       if (got_signal != 0) {
+               switch (got_signal) {
+               case SIGCHLD:
+                       warnx("FATAL: mandocd child died: got SIGCHLD");
+                       break;
+               case SIGPIPE:
+                       warnx("FATAL: mandocd child died: got SIGPIPE");
+                       break;
+               default:
+                       warnx("FATAL: signal SIG%s", sys_signame[got_signal]);
+                       break;
+               }
+               inflight = -1;
+               fatal = 1;
+       } else if (fatal == 0 && (fatal = errno) != 0)
+               warn("FATAL: fts_read");
 
        fts_close(ftsp);
-       return 0;
+       if (verbose_flag >= 2 && inflight > 0) {
+               warnx("waiting for %d files in flight", inflight);
+               fflush(stderr);
+       }
+       while (inflight > 0) {
+               irc = recv(srv_fd, dummy, sizeof(dummy), 0);
+               if (irc > 0)
+                       inflight--;
+               else if (irc == -1 && errno == EAGAIN)
+                       nanosleep(&timeout, NULL);
+               else {
+                       if (irc == 0)
+                               errno = ECONNRESET;
+                       warn("recv");
+                       inflight = -1;
+               }
+       }
+       if (verbose_flag)
+               warnx("processed %d files in %d directories",
+                   goodfiles, gooddirs);
+       if (baddirs > 0)
+               warnx("skipped %d %s due to errors", baddirs,
+                   baddirs == 1 ? "directory" : "directories");
+       if (badfiles > 0)
+               warnx("skipped %d %s due to errors", badfiles,
+                   badfiles == 1 ? "file" : "files");
+       if (fatal != 0) {
+               warnx("processing aborted due to fatal error, "
+                   "results are probably incomplete");
+               inflight = -1;
+       }
+       return inflight;
 }
 
 int
 main(int argc, char **argv)
 {
+       struct sigaction sa;
        const char      *defos, *outtype;
        int              srv_fds[2];
        int              dstdir_fd;
@@ -207,7 +348,7 @@ main(int argc, char **argv)
 
        defos = NULL;
        outtype = "ascii";
-       while ((opt = getopt(argc, argv, "I:T:")) != -1) {
+       while ((opt = getopt(argc, argv, "I:T:v")) != -1) {
                switch (opt) {
                case 'I':
                        defos = optarg;
@@ -215,6 +356,9 @@ main(int argc, char **argv)
                case 'T':
                        outtype = optarg;
                        break;
+               case 'v':
+                       verbose_flag += 1;
+                       break;
                default:
                        usage();
                }
@@ -224,8 +368,36 @@ main(int argc, char **argv)
                argc -= optind;
                argv += optind;
        }
-       if (argc != 2)
+       if (argc != 2) {
+               switch (argc) {
+               case 0:
+                       warnx("missing arguments: srcdir and dstdir");
+                       break;
+               case 1:
+                       warnx("missing argument: dstdir");
+                       break;
+               default:
+                       warnx("too many arguments: %s", argv[2]);
+                       break;
+               }
                usage();
+       }
+
+       memset(&sa, 0, sizeof(sa));
+       sa.sa_handler = &signal_handler;
+       sa.sa_flags = SA_NOCLDWAIT;
+       if (sigfillset(&sa.sa_mask) == -1)
+               err(1, "sigfillset");
+       if (sigaction(SIGHUP, &sa, NULL) == -1)
+               err(1, "sigaction(SIGHUP)");
+       if (sigaction(SIGINT, &sa, NULL) == -1)
+               err(1, "sigaction(SIGINT)");
+       if (sigaction(SIGPIPE, &sa, NULL) == -1)
+               err(1, "sigaction(SIGPIPE)");
+       if (sigaction(SIGTERM, &sa, NULL) == -1)
+               err(1, "sigaction(SIGTERM)");
+       if (sigaction(SIGCHLD, &sa, NULL) == -1)
+               err(1, "sigaction(SIGCHLD)");
 
        if (socketpair(AF_LOCAL, SOCK_STREAM, AF_UNSPEC, srv_fds) == -1)
                err(1, "socketpair");
@@ -242,11 +414,18 @@ main(int argc, char **argv)
        }
        close(srv_fds[1]);
 
-       if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
-               err(1, "open(%s)", argv[1]);
+       if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1) {
+               if (errno != ENOENT)
+                       err(1, "open destination %s", argv[1]);
+               if (mkdir(argv[1], S_IRWXU |
+                   S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) == -1) 
+                       err(1, "mkdir destination %s", argv[1]);
+               if ((dstdir_fd = open(argv[1], O_RDONLY | O_DIRECTORY)) == -1)
+                       err(1, "open destination %s", argv[1]);
+       }
 
        if (chdir(argv[0]) == -1)
-               err(1, "chdir(%s)", argv[0]);
+               err(1, "chdir to source %s", argv[0]);
 
        return process_tree(srv_fds[0], dstdir_fd) == -1 ? 1 : 0;
 }
diff --git a/contrib/mandoc/gmdiff b/contrib/mandoc/gmdiff
index 69431f703aaf..54025e4cd450 100644
--- a/contrib/mandoc/gmdiff
+++ b/contrib/mandoc/gmdiff
@@ -45,8 +45,8 @@ while [ -n "$1" ]; do
   file=$1
   shift
   echo " ========== $file ========== "
-  $ROFF -mandoc $file | $COLPIPE 2> /tmp/roff.err > /tmp/roff.out
-  ${MANDOC:=mandoc} $MOPT $file | $COLPIPE \
+  ($ROFF -mandoc $file | $COLPIPE) 2> /tmp/roff.err > /tmp/roff.out
+  (${MANDOC:=mandoc} $MOPT $file | $COLPIPE) \
     2> /tmp/mandoc.err > /tmp/mandoc.out
   for i in roff mandoc; do
     [ -s /tmp/$i.err ] && echo "$i errors:" && cat /tmp/$i.err
diff --git a/contrib/mandoc/man.7 b/contrib/mandoc/man.7
index 4d27c76ba110..91eafbb35f70 100644
--- a/contrib/mandoc/man.7
+++ b/contrib/mandoc/man.7
@@ -1,7 +1,8 @@
-.\" $Id: man.7,v 1.150 2023/10/23 22:57:54 schwarze Exp $
+.\" $Id: man.7,v 1.154 2025/08/05 21:16:20 schwarze Exp $
 .\"
+.\" Copyright (c) 2011-2015, 2017-2020, 2023, 2025
+.\"               Ingo Schwarze <[email protected]>
 .\" Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <[email protected]>
-.\" Copyright (c) 2011-2015,2017-2020,2023 Ingo Schwarze <[email protected]>
 .\" Copyright (c) 2017 Anthony Bentley <[email protected]>
 .\" Copyright (c) 2010 Joerg Sonnenberger <[email protected]>
 .\"
@@ -17,7 +18,7 @@
 .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 .\"
-.Dd $Mdocdate: October 23 2023 $
+.Dd $Mdocdate: August 5 2025 $
 .Dt MAN 7
 .Os
 .Sh NAME
@@ -89,7 +90,7 @@ but can be found in the alphabetical reference below.
 .Ss Page header and footer meta-data
 .Bl -column "RS, RE" description
 .It Ic TH Ta set the title: Ar name section date Op Ar source Op Ar volume
-.It Ic AT Ta display AT&T UNIX version in the page footer (<= 1 argument)
+.It Ic AT Ta display AT&T UNIX version in the page footer (<= 2 arguments)
 .It Ic UC Ta display BSD version in the page footer (<= 1 argument)
 .El
 .Ss Sections and paragraphs
@@ -99,6 +100,7 @@ but can be found in the alphabetical reference below.
 .It Ic PP Ta start an undecorated paragraph (no arguments)
 .It Ic IP Ta indented paragraph: Op Ar head Op Ar width
 .It Ic TP Ta tagged paragraph: Op Ar width
+.It Ic HP Ta hanged paragraph: Op Ar width
 .It Ic PD Ta set vertical paragraph distance: Op Ar height
 .It Ic EX , EE Ta display an example (no arguments)
 .It Ic RS , RE Ta reset the left margin: Op Ar width
@@ -198,11 +200,6 @@ argument is a
 scaling width.
 If specified, it's saved for later paragraph left margins;
 if unspecified, the saved or default width is used.
-.Pp
-This macro is portable, but deprecated
-because it has no good representation in HTML output,
-usually ending up indistinguishable from
-.Ic PP .
 .It Ic I
 Text is rendered in italics.
 .It Ic IB
@@ -239,6 +236,17 @@ A synonym for
 End a mailto block started with
 .Ic MT .
 This is a GNU extension.
+.It Ic MR
+Reference another manual page.
+This is a Plan 9 extension also supported by GNU.
+It has the following syntax:
+.Pp
+.D1 Pf . Ic MR Ar name section Op Ar suffix
+.Pp
+The optional, single
+.Ar suffix
+argument is appended without preceding whitespace
+and typically used for trailing punctuation.
 .It Ic MT
 Begin a mailto block.
 This is a GNU extension.
@@ -250,8 +258,12 @@ link description to be shown
 .Ed
 .It Ic OP
 Optional command-line argument.
-This is a rarely used DWB extension.
-It has the following syntax:
+This is a deprecated GNU extension.
+The name and purpose of the macro match an earlier DWB extension,
+but both the syntax and semantics are incompatible.
+In GNU and
+.Xr mandoc 1 ,
+it has the following syntax:
 .Pp
 .D1 Pf . Ic OP Ar key Op Ar value
 .Pp
@@ -503,43 +515,56 @@ raised.
 .Pp
 The syntax is as follows:
 .Bd -literal -offset indent
-\&.YO \(lBbody...\(rB
-\(lBbody...\(rB
+\&.\e" current-line syntax
+\&.YO \(lBbody ...\(rB
+
+\&.\e" next-line syntax
+\&.YO
+body ...
 .Ed
-.Bl -column "MacroX" "ArgumentsX" "ScopeXXXXX" "CompatX" -offset indent
-.It Em Macro Ta Em Arguments Ta Em Scope     Ta Em Notes
-.It Ic AT  Ta    <=1       Ta    current   Ta    \&
-.It Ic B   Ta    n         Ta    next-line Ta    \&
-.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    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    \&
-.It Ic OP  Ta    >=1       Ta    current   Ta    DWB
-.It Ic PD  Ta    1         Ta    current   Ta    \&
-.It Ic RB  Ta    n         Ta    current   Ta    \&
-.It Ic RI  Ta    n         Ta    current   Ta    \&
-.It Ic SB  Ta    n         Ta    next-line Ta    \&
-.It Ic SM  Ta    n         Ta    next-line Ta    \&
-.It Ic TH  Ta    >1, <6    Ta    current   Ta    \&
-.It Ic UC  Ta    <=1       Ta    current   Ta    \&
-.It Ic in  Ta    1         Ta    current   Ta    Xr roff 7
+.Bl -column -offset indent\
+      "Macro"     "Arguments"     "curr and next"     "Version 9 AT&T UNIX"
+.It Em Macro Ta Em Arguments Ta Em Line Scope    Ta Em Notes
+.It Ic AT    Ta    0 to 2    Ta    current       Ta    \&
+.It Ic B     Ta    1 or more Ta    curr or next  Ta    \&
+.It Ic BI    Ta    2 or more Ta    current       Ta    \&
+.It Ic BR    Ta    2 or more Ta    current       Ta    \&
+.It Ic DT    Ta    0         Ta    none          Ta    \&
+.It Ic EE    Ta    0         Ta    none          Ta    Version 9 At
+.It Ic EX    Ta    0         Ta    none          Ta    Version 9 At
+.It Ic I     Ta    1 or more Ta    curr or next  Ta    \&
+.It Ic IB    Ta    2 or more Ta    current       Ta    \&
+.It Ic IR    Ta    2 or more Ta    current       Ta    \&
+.It Ic MR    Ta    2 or 3    Ta    current       Ta    Plan 9
+.It Ic OP    Ta    1 or 2    Ta    current       Ta    GNU
+.It Ic PD    Ta    0 or 1    Ta    current       Ta    \&
+.It Ic RB    Ta    2 or more Ta    current       Ta    \&
+.It Ic RI    Ta    2 or more Ta    current       Ta    \&
+.It Ic SB    Ta    1 or more Ta    curr or next  Ta    \&
+.It Ic SM    Ta    1 or more Ta    curr or next  Ta    \&
+.It Ic TH    Ta    3 to 5    Ta    current       Ta    \&
+.It Ic UC    Ta    0 or 1    Ta    current       Ta    \&
+.It Ic in    Ta    0 or 1    Ta    current       Ta    Xr roff 7
 .El
 .Ss Block Macros
 Block macros comprise a head and body.
-As with in-line macros, the head is scoped to the current line and, in
-one circumstance, the next line (the next-line stipulations as in
+As with in-line macros, the head is scoped to the current line or,
+for some macros, to the next line (the next-line stipulations as in
 .Sx Line Macros
 apply here as well).
 .Pp
 The syntax is as follows:
 .Bd -literal -offset indent
-\&.YO \(lBhead...\(rB
-\(lBhead...\(rB
-\(lBbody...\(rB
+\&.\e" current-line syntax
+\&.YO \(lBhead ...\(rB
+body ...
+\&...
+
+\&.\e" next-line syntax
+\&.YO \(lBhead\(rB
+head ...
+body ...
+\&...
 .Ed
 .Pp
 The closure of body scope may be to the section, where a macro is closed
@@ -547,40 +572,42 @@ by
 .Ic SH ;
 sub-section, closed by a section or
 .Ic SS ;
-or paragraph, closed by a section, sub-section,
+paragraph, closed by a section, sub-section,
 .Ic HP ,
 .Ic IP ,
 .Ic LP ,
 .Ic P ,
 .Ic PP ,
-.Ic RE ,
+.Ic RS ,
 .Ic SY ,
+.Ic TP ,
 or
-.Ic TP .
-No closure refers to an explicit block closing macro.
+.Ic TQ ;
+or to an explicit block closing macro.
 .Pp
 As a rule, block macros may not be nested; thus, calling a block macro
 while another block macro scope is open, and the open scope is not
 implicitly closed, is syntactically incorrect.
-.Bl -column "MacroX" "ArgumentsX" "Head ScopeX" "sub-sectionX" "compatX" 
-offset indent
-.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope  Ta Em Notes
-.It Ic HP  Ta    <2        Ta    current    Ta    paragraph   Ta    \&
-.It Ic IP  Ta    <3        Ta    current    Ta    paragraph   Ta    \&
-.It Ic LP  Ta    0         Ta    current    Ta    paragraph   Ta    \&
-.It Ic ME  Ta    0         Ta    none       Ta    none        Ta    GNU
-.It Ic MT  Ta    1         Ta    current    Ta    to \&ME     Ta    GNU
-.It Ic P   Ta    0         Ta    current    Ta    paragraph   Ta    \&
-.It Ic PP  Ta    0         Ta    current    Ta    paragraph   Ta    \&
-.It Ic RE  Ta    <=1       Ta    current    Ta    none        Ta    \&
-.It Ic RS  Ta    1         Ta    current    Ta    to \&RE     Ta    \&
-.It Ic SH  Ta    >0        Ta    next-line  Ta    section     Ta    \&
-.It Ic SS  Ta    >0        Ta    next-line  Ta    sub-section Ta    \&
-.It Ic SY  Ta    1         Ta    current    Ta    to \&YS     Ta    GNU
-.It Ic TP  Ta    n         Ta    next-line  Ta    paragraph   Ta    \&
-.It Ic TQ  Ta    n         Ta    next-line  Ta    paragraph   Ta    GNU
-.It Ic UE  Ta    0         Ta    current    Ta    none        Ta    GNU
-.It Ic UR  Ta    1         Ta    current    Ta    part        Ta    GNU
-.It Ic YS  Ta    0         Ta    none       Ta    none        Ta    GNU
*** 3592 LINES SKIPPED ***


Reply via email to