aboutsummaryrefslogtreecommitdiff
path: root/contrib/mandoc
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/mandoc')
-rw-r--r--contrib/mandoc/Makefile2
-rw-r--r--contrib/mandoc/TODO11
-rw-r--r--contrib/mandoc/catman.8238
-rw-r--r--contrib/mandoc/catman.c231
-rw-r--r--contrib/mandoc/gmdiff4
-rw-r--r--contrib/mandoc/man.7151
-rw-r--r--contrib/mandoc/man.options.19
-rw-r--r--contrib/mandoc/man_html.c19
-rw-r--r--contrib/mandoc/man_term.c133
-rw-r--r--contrib/mandoc/man_validate.c18
-rw-r--r--contrib/mandoc/mandoc.131
-rw-r--r--contrib/mandoc/mandoc.css4
-rw-r--r--contrib/mandoc/mandocd.826
-rw-r--r--contrib/mandoc/mandocd.c34
-rw-r--r--contrib/mandoc/manpath.c11
-rw-r--r--contrib/mandoc/mdoc_html.c15
-rw-r--r--contrib/mandoc/mdoc_man.c5
-rw-r--r--contrib/mandoc/mdoc_markdown.c10
-rw-r--r--contrib/mandoc/mdoc_term.c122
-rw-r--r--contrib/mandoc/mdoc_validate.c10
-rw-r--r--contrib/mandoc/out.c63
-rw-r--r--contrib/mandoc/out.h24
-rw-r--r--contrib/mandoc/roff.7195
-rw-r--r--contrib/mandoc/roff_term.c31
-rw-r--r--contrib/mandoc/tbl.h16
-rw-r--r--contrib/mandoc/tbl_html.c34
-rw-r--r--contrib/mandoc/tbl_layout.c10
-rw-r--r--contrib/mandoc/tbl_term.c287
-rw-r--r--contrib/mandoc/term.c201
-rw-r--r--contrib/mandoc/term.h38
-rw-r--r--contrib/mandoc/term_ascii.c91
-rw-r--r--contrib/mandoc/term_ps.c18
-rw-r--r--contrib/mandoc/term_tab.c27
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 <schwarze@openbsd.org>
+.\" Copyright (c) 2017, 2025 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
@@ -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 <schwarze@openbsd.org>
* Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
- * Copyright (c) 2017 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
@@ -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 <schwarze@openbsd.org>
.\" Copyright (c) 2009, 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
-.\" Copyright (c) 2011-2015,2017-2020,2023 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2017 Anthony Bentley <bentley@openbsd.org>
.\" Copyright (c) 2010 Joerg Sonnenberger <joerg@netbsd.org>
.\"
@@ -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
+.Bl -column -offset indent\
+ "Macro" "Arguments" "curr and next" "sub-section" "Notes"
+.It Em Macro Ta Em Arguments Ta Em Head Scope Ta Em Body Scope Ta Em Notes
+.It Ic HP Ta 0 or 1 Ta current Ta paragraph Ta \&
+.It Ic IP Ta 0 to 2 Ta current Ta paragraph Ta \&
+.It Ic LP Ta 0 Ta none Ta paragraph Ta \&
+.It Ic ME Ta 0 or 1 Ta current Ta none Ta GNU
+.It Ic MT Ta 1 Ta current Ta to \&ME Ta GNU
+.It Ic P Ta 0 Ta none Ta paragraph Ta \&
+.It Ic PP Ta 0 Ta none Ta paragraph Ta \&
+.It Ic RE Ta 0 or 1 Ta current Ta none Ta \&
+.It Ic RS Ta 0 or 1 Ta current Ta to \&RE Ta \&
+.It Ic SH Ta 1 or more Ta curr or next Ta section Ta \&
+.It Ic SS Ta 1 or more Ta curr or next Ta sub-section Ta \&
+.It Ic SY Ta 1 Ta current Ta to \&YS Ta GNU
+.It Ic TP Ta 0 or 1 Ta curr and next Ta paragraph Ta \&
+.It Ic TQ Ta 0 or 1 Ta curr and next Ta paragraph Ta GNU
+.It Ic UE Ta 0 or 1 Ta current Ta none Ta GNU
+.It Ic UR Ta 1 Ta current Ta to \&UE Ta GNU
+.It Ic YS Ta 0 Ta none Ta none Ta GNU
.El
.Pp
If a block macro is next-line scoped, it may only be followed by in-line
diff --git a/contrib/mandoc/man.options.1 b/contrib/mandoc/man.options.1
index d8c790f4fa04..be65ad98fddc 100644
--- a/contrib/mandoc/man.options.1
+++ b/contrib/mandoc/man.options.1
@@ -1,6 +1,6 @@
-.\" $Id: man.options.1,v 1.7 2017/07/04 23:40:01 schwarze Exp $
+.\" $Id: man.options.1,v 1.8 2025/06/30 00:11:06 schwarze Exp $
.\"
-.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2017, 2025 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
@@ -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: July 4 2017 $
+.Dd $Mdocdate: June 30 2025 $
.Dt MAN.OPTIONS 1
.Os
.Sh NAME
@@ -1113,7 +1113,8 @@ print version number
verbose mode
.br
.Nm catman :
-.Fx Pq March 15, 1995
+.Fx Pq March 15, 1995 ,
+.No mandoc Pq June 30, 2025
.br
.Nm makewhatis :
.man15g
diff --git a/contrib/mandoc/man_html.c b/contrib/mandoc/man_html.c
index 6784171af1e6..fc593be1112c 100644
--- a/contrib/mandoc/man_html.c
+++ b/contrib/mandoc/man_html.c
@@ -1,6 +1,6 @@
-/* $Id: man_html.c,v 1.187 2023/10/24 20:53:12 schwarze Exp $ */
+/* $Id: man_html.c,v 1.188 2025/06/26 17:06:34 schwarze Exp $ */
/*
- * Copyright (c) 2013-15,2017-20,2022-23 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2013-2020,2022-2023,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2012, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -293,21 +293,30 @@ static void
man_root_post(const struct roff_meta *man, struct html *h)
{
struct tag *t;
+ char *title;
+
+ assert(man->title != NULL);
+ if (man->msec == NULL)
+ title = mandoc_strdup(man->title);
+ else
+ mandoc_asprintf(&title, "%s(%s)", man->title, man->msec);
t = print_otag(h, TAG_DIV, "cr?", "foot", "doc-pagefooter",
"aria-label", "Manual footer line");
print_otag(h, TAG_SPAN, "c", "foot-left");
+ if (man->os != NULL)
+ print_text(h, man->os);
print_stagq(h, t);
print_otag(h, TAG_SPAN, "c", "foot-date");
print_text(h, man->date);
print_stagq(h, t);
- print_otag(h, TAG_SPAN, "c", "foot-os");
- if (man->os != NULL)
- print_text(h, man->os);
+ print_otag(h, TAG_SPAN, "c", "foot-right");
+ print_text(h, title);
print_tagq(h, t);
+ free(title);
}
static int
diff --git a/contrib/mandoc/man_term.c b/contrib/mandoc/man_term.c
index 706fab8cd4d1..ac75c2c5ef40 100644
--- a/contrib/mandoc/man_term.c
+++ b/contrib/mandoc/man_term.c
@@ -1,6 +1,6 @@
-/* $Id: man_term.c,v 1.244 2023/11/13 19:13:01 schwarze Exp $ */
+/* $Id: man_term.c,v 1.248 2025/07/27 15:27:28 schwarze Exp $ */
/*
- * Copyright (c) 2010-15,2017-20,2022-23 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2020,2022-23,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -38,14 +38,14 @@
#include "term_tag.h"
#include "main.h"
-#define MAXMARGINS 64 /* maximum number of indented scopes */
+#define MAXMARGINS 64 /* Maximum number of indented scopes. */
struct mtermp {
- int lmargin[MAXMARGINS]; /* margins (incl. vis. page) */
- int lmargincur; /* index of current margin */
- int lmarginsz; /* actual number of nested margins */
- size_t offset; /* default offset to visible page */
- int pardist; /* vert. space before par., unit: [v] */
+ int lmargin[MAXMARGINS]; /* Margins in basic units. */
+ int lmargincur; /* Index of current margin. */
+ int lmarginsz; /* Actual number of nested margins. */
+ size_t offset; /* Default offset in basic units. */
+ int pardist; /* Vert. space before par., unit: [v]. */
};
#define DECL_ARGS struct termp *p, \
@@ -194,12 +194,10 @@ terminal_man(void *arg, const struct roff_meta *man)
}
/*
- * Printing leading vertical space before a block.
- * This is used for the paragraph macros.
- * The rules are pretty simple, since there's very little nesting going
- * on here. Basically, if we're the first within another block (SS/SH),
- * then don't emit vertical space. If we are (RS), then do. If not the
- * first, print it.
+ * Print leading vertical space before a paragraph, unless
+ * it is the first paragraph in a section or subsection.
+ * If it is the first paragraph in an .RS block, consider
+ * that .RS block instead of the paragraph, recursively.
*/
static void
print_bvspace(struct termp *p, struct roff_node *n, int pardist)
@@ -214,9 +212,13 @@ print_bvspace(struct termp *p, struct roff_node *n, int pardist)
nch->type == ROFFT_TBL)
return;
- if (n->parent->tok != MAN_RS && roff_node_prev(n) == NULL)
- return;
-
+ while (roff_node_prev(n) == NULL) {
+ n = n->parent;
+ if (n->tok != MAN_RS)
+ return;
+ if (n->type == ROFFT_BODY)
+ n = n->parent;
+ }
for (i = 0; i < pardist; i++)
term_vspace(p);
}
@@ -372,8 +374,8 @@ static int
pre_in(DECL_ARGS)
{
struct roffsu su;
- const char *cp;
- size_t v;
+ const char *cp; /* Request argument. */
+ size_t v; /* Indentation in basic units. */
int less;
term_newln(p);
@@ -386,17 +388,18 @@ pre_in(DECL_ARGS)
cp = n->child->string;
less = 0;
- if (*cp == '-')
+ if (*cp == '-') {
less = -1;
- else if (*cp == '+')
+ cp++;
+ } else if (*cp == '+') {
less = 1;
- else
- cp--;
+ cp++;
+ }
- if (a2roffsu(++cp, &su, SCALE_EN) == NULL)
+ if (a2roffsu(cp, &su, SCALE_EN) == NULL)
return 0;
- v = term_hen(p, &su);
+ v = term_hspan(p, &su);
if (less < 0)
p->tcol->offset -= p->tcol->offset > v ? v : p->tcol->offset;
@@ -424,7 +427,7 @@ pre_HP(DECL_ARGS)
{
struct roffsu su;
const struct roff_node *nn;
- int len;
+ int len; /* Indentation in basic units. */
switch (n->type) {
case ROFFT_BLOCK:
@@ -450,7 +453,7 @@ pre_HP(DECL_ARGS)
if ((nn = n->parent->head->child) != NULL &&
a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
- len = term_hen(p, &su);
+ len = term_hspan(p, &su);
if (len < 0 && (size_t)(-len) > mt->offset)
len = -mt->offset;
else if (len > SHRT_MAX)
@@ -518,7 +521,7 @@ pre_IP(DECL_ARGS)
{
struct roffsu su;
const struct roff_node *nn;
- int len;
+ int len; /* Indentation in basic units. */
switch (n->type) {
case ROFFT_BLOCK:
@@ -539,7 +542,7 @@ pre_IP(DECL_ARGS)
if ((nn = n->parent->head->child) != NULL &&
(nn = nn->next) != NULL &&
a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
- len = term_hen(p, &su);
+ len = term_hspan(p, &su);
if (len < 0 && (size_t)(-len) > mt->offset)
len = -mt->offset;
else if (len > SHRT_MAX)
@@ -591,7 +594,7 @@ pre_TP(DECL_ARGS)
{
struct roffsu su;
struct roff_node *nn;
- int len;
+ int len; /* Indentation in basic units. */
switch (n->type) {
case ROFFT_BLOCK:
@@ -614,7 +617,7 @@ pre_TP(DECL_ARGS)
if ((nn = n->parent->head->child) != NULL &&
nn->string != NULL && ! (NODE_LINE & nn->flags) &&
a2roffsu(nn->string, &su, SCALE_EN) != NULL) {
- len = term_hen(p, &su);
+ len = term_hspan(p, &su);
if (len < 0 && (size_t)(-len) > mt->offset)
len = -mt->offset;
else if (len > SHRT_MAX)
@@ -691,10 +694,11 @@ pre_SS(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
+ p->fontibi = 1;
term_fontrepl(p, TERMFONT_BOLD);
- p->tcol->offset = term_len(p, 3);
+ p->tcol->offset = term_len(p, p->defindent) / 2 + 1;
p->tcol->rmargin = mt->offset;
- p->trailspace = mt->offset;
+ p->trailspace = mt->offset / term_len(p, 1);
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
break;
case ROFFT_BODY:
@@ -732,10 +736,11 @@ pre_SH(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
+ p->fontibi = 1;
term_fontrepl(p, TERMFONT_BOLD);
p->tcol->offset = 0;
p->tcol->rmargin = mt->offset;
- p->trailspace = mt->offset;
+ p->trailspace = mt->offset / term_len(p, 1);
p->flags |= TERMP_NOBREAK | TERMP_BRIND;
break;
case ROFFT_BODY:
@@ -757,6 +762,8 @@ post_SH(DECL_ARGS)
case ROFFT_BLOCK:
break;
case ROFFT_HEAD:
+ p->fontibi = 0;
+ /* FALLTHROUGH */
case ROFFT_BODY:
term_newln(p);
break;
@@ -787,7 +794,7 @@ pre_RS(DECL_ARGS)
if (n->child == NULL)
n->aux = mt->lmargin[mt->lmargincur];
else if (a2roffsu(n->child->string, &su, SCALE_EN) != NULL)
- n->aux = term_hen(p, &su);
+ n->aux = term_hspan(p, &su);
if (n->aux < 0 && (size_t)(-n->aux) > mt->offset)
n->aux = -mt->offset;
else if (n->aux > SHRT_MAX)
@@ -827,7 +834,7 @@ static int
pre_SY(DECL_ARGS)
{
const struct roff_node *nn;
- int len;
+ int len; /* Indentation in basic units. */
switch (n->type) {
case ROFFT_BLOCK:
@@ -842,7 +849,9 @@ pre_SY(DECL_ARGS)
}
nn = n->parent->head->child;
- len = nn == NULL ? 1 : term_strlen(p, nn->string) + 1;
+ len = term_len(p, 1);
+ if (nn != NULL)
+ len += term_strlen(p, nn->string);
switch (n->type) {
case ROFFT_HEAD:
@@ -1015,40 +1024,26 @@ static void
print_man_foot(struct termp *p, const struct roff_meta *meta)
{
char *title;
- size_t datelen, titlen;
+ size_t datelen, titlen; /* In basic units. */
- assert(meta->title);
- assert(meta->msec);
- assert(meta->date);
+ assert(meta->title != NULL);
+ assert(meta->msec != NULL);
term_fontrepl(p, TERMFONT_NONE);
-
if (meta->hasbody)
term_vspace(p);
- /*
- * Temporary, undocumented option to imitate mdoc(7) output.
- * In the bottom right corner, use the operating system
- * instead of the title.
- */
-
- if ( ! p->mdocstyle) {
- mandoc_asprintf(&title, "%s(%s)",
- meta->title, meta->msec);
- } else if (meta->os != NULL) {
- title = mandoc_strdup(meta->os);
- } else {
- title = mandoc_strdup("");
- }
datelen = term_strlen(p, meta->date);
+ mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
+ titlen = term_strlen(p, title);
/* Bottom left corner: operating system. */
- p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
- p->trailspace = 1;
p->tcol->offset = 0;
p->tcol->rmargin = p->maxrmargin > datelen ?
(p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
+ p->trailspace = 1;
+ p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
if (meta->os)
term_word(p, meta->os);
@@ -1057,7 +1052,6 @@ print_man_foot(struct termp *p, const struct roff_meta *meta)
/* At the bottom in the middle: manual date. */
p->tcol->offset = p->tcol->rmargin;
- titlen = term_strlen(p, title);
p->tcol->rmargin = p->maxrmargin > titlen ?
p->maxrmargin - titlen : 0;
p->flags |= TERMP_NOSPACE;
@@ -1067,11 +1061,11 @@ print_man_foot(struct termp *p, const struct roff_meta *meta)
/* Bottom right corner: manual title and section. */
- p->flags &= ~TERMP_NOBREAK;
- p->flags |= TERMP_NOSPACE;
- p->trailspace = 0;
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->maxrmargin;
+ p->trailspace = 0;
+ p->flags &= ~TERMP_NOBREAK;
+ p->flags |= TERMP_NOSPACE;
term_word(p, title);
term_flushln(p);
@@ -1086,7 +1080,6 @@ print_man_foot(struct termp *p, const struct roff_meta *meta)
p->tcol->offset = 0;
p->flags = 0;
-
free(title);
}
@@ -1095,7 +1088,7 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
{
const char *volume;
char *title;
- size_t vollen, titlen;
+ size_t vollen, titlen; /* In basic units. */
assert(meta->title);
assert(meta->msec);
@@ -1111,7 +1104,8 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
p->trailspace = 1;
p->tcol->offset = 0;
- p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
+ p->tcol->rmargin =
+ titlen * 2 + term_len(p, 2) + vollen < p->maxrmargin ?
(p->maxrmargin - vollen + term_len(p, 1)) / 2 :
vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
@@ -1123,7 +1117,7 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
p->flags |= TERMP_NOSPACE;
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->tcol->offset + vollen + titlen <
- p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
+ p->maxrmargin ? p->maxrmargin - titlen : p->maxrmargin;
term_word(p, volume);
term_flushln(p);
@@ -1143,13 +1137,6 @@ print_man_head(struct termp *p, const struct roff_meta *meta)
p->flags &= ~TERMP_NOSPACE;
p->tcol->offset = 0;
p->tcol->rmargin = p->maxrmargin;
-
- /*
- * Groff prints three blank lines before the content.
- * Do the same, except in the temporary, undocumented
- * mode imitating mdoc(7) output.
- */
-
term_vspace(p);
free(title);
}
diff --git a/contrib/mandoc/man_validate.c b/contrib/mandoc/man_validate.c
index 857adba2798f..57ac9327afd4 100644
--- a/contrib/mandoc/man_validate.c
+++ b/contrib/mandoc/man_validate.c
@@ -1,6 +1,6 @@
-/* $Id: man_validate.c,v 1.159 2023/10/24 20:53:12 schwarze Exp $ */
+/* $Id: man_validate.c,v 1.161 2025/07/09 12:51:06 schwarze Exp $ */
/*
- * Copyright (c) 2010, 2012-2020, 2023 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2020, 2023, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -299,6 +299,14 @@ post_SH(CHKARGS)
nc = n->child;
switch (n->type) {
+ case ROFFT_BLOCK:
+ if ((nc = n->prev) != NULL && nc->tok == ROFF_br) {
+ mandoc_msg(MANDOCERR_PAR_SKIP, nc->line, nc->pos,
+ "%s before first %s", roff_name[nc->tok],
+ roff_name[n->tok]);
+ roff_node_delete(man, nc);
+ }
+ return;
case ROFFT_HEAD:
tag = NULL;
deroff(&tag, n);
@@ -473,7 +481,7 @@ post_TH(CHKARGS)
/* ->TITLE<- MSEC DATE OS VOL */
n = n->child;
- if (n != NULL && n->string != NULL) {
+ if (n != NULL && n->string != NULL && *n->string != '\0') {
for (p = n->string; *p != '\0'; p++) {
/* Only warn about this once... */
if (isalpha((unsigned char)*p) &&
@@ -486,8 +494,8 @@ post_TH(CHKARGS)
}
man->meta.title = mandoc_strdup(n->string);
} else {
- man->meta.title = mandoc_strdup("");
- mandoc_msg(MANDOCERR_TH_NOTITLE, nb->line, nb->pos, "TH");
+ man->meta.title = mandoc_strdup("UNTITLED");
+ mandoc_msg(MANDOCERR_DT_NOTITLE, nb->line, nb->pos, "TH");
}
/* TITLE ->MSEC<- DATE OS VOL */
diff --git a/contrib/mandoc/mandoc.1 b/contrib/mandoc/mandoc.1
index 32a3e2811513..8b6fe7d19b1e 100644
--- a/contrib/mandoc/mandoc.1
+++ b/contrib/mandoc/mandoc.1
@@ -1,4 +1,4 @@
-.\" $Id: mandoc.1,v 1.270 2025/03/03 14:07:51 schwarze Exp $
+.\" $Id: mandoc.1,v 1.272 2025/07/09 13:46:05 schwarze Exp $
.\"
.\" Copyright (c) 2012, 2014-2023, 2025 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -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.
.\"
-.Dd $Mdocdate: March 3 2025 $
+.Dd $Mdocdate: July 9 2025 $
.Dt MANDOC 1
.Os
.Sh NAME
@@ -292,19 +292,6 @@ Increasing this is not recommended; it may result in degraded formatting,
for example overfull lines or ugly line breaks.
When output is to a pager on a terminal that is less than 66 columns
wide, the default is reduced to three columns.
-.It Cm mdoc
-Format
-.Xr man 7
-input files in
-.Xr mdoc 7
-output style.
-This prints the operating system name rather than the page title
-on the right side of the footer line.
-One useful application is for checking that
-.Fl T Cm man
-output formats in the same way as the
-.Xr mdoc 7
-source it was generated from.
.It Cm tag Ns Op = Ns Ar term
If the formatted manual page is opened in a pager,
go to the definition of the
@@ -1121,17 +1108,21 @@ but leaving out the backslash might not be portable.
.Ss Warnings related to the document prologue
.Bl -ohang
.It Sy "missing manual title, using UNTITLED"
-.Pq mdoc
+.Pq mdoc , man
A
.Ic \&Dt
-macro has no arguments, or there is no
+or
+.Ic \&TH
+macro has no arguments, its first argument is an empty string, or there is no
.Ic \&Dt
-macro before the first non-prologue macro.
+macro before the first non-prologue
+.Xr mdoc 7
+macro.
.It Sy "missing manual title, using \(dq\(dq"
.Pq man
-There is no
+An input document does not contain any
.Ic \&TH
-macro, or it has no arguments.
+macro.
.It Sy "missing manual section, using \(dq\(dq"
.Pq mdoc , man
A
diff --git a/contrib/mandoc/mandoc.css b/contrib/mandoc/mandoc.css
index 88432b9322b7..46e03a386ae0 100644
--- a/contrib/mandoc/mandoc.css
+++ b/contrib/mandoc/mandoc.css
@@ -1,4 +1,4 @@
-/* $Id: mandoc.css,v 1.54 2025/01/25 03:18:55 schwarze Exp $ */
+/* $Id: mandoc.css,v 1.55 2025/06/26 17:06:34 schwarze Exp $ */
/*
* Standard style sheet for mandoc(1) -Thtml and man.cgi(8).
*
@@ -73,7 +73,7 @@ div[role=doc-pagefooter] {
.foot-left { flex: 1; }
.foot-date { flex: 0 1 auto;
text-align: center; }
-.foot-os { flex: 1;
+.foot-right { flex: 1;
text-align: right; }
/* Sections and paragraphs. */
diff --git a/contrib/mandoc/mandocd.8 b/contrib/mandoc/mandocd.8
index d679deb1b9e4..aaf4e3dede70 100644
--- a/contrib/mandoc/mandocd.8
+++ b/contrib/mandoc/mandocd.8
@@ -1,6 +1,6 @@
-.\" $Id: mandocd.8,v 1.3 2021/09/28 15:41:41 schwarze Exp $
+.\" $Id: mandocd.8,v 1.5 2025/06/30 15:07:38 schwarze Exp $
.\"
-.\" Copyright (c) 2017 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2017, 2025 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
@@ -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: September 28 2021 $
+.Dd $Mdocdate: June 30 2025 $
.Dt MANDOCD 8
.Os
.Sh NAME
@@ -78,6 +78,19 @@ or
input, the second one for formatted output, and the third one
for error output.
.Pp
+After accepting each message,
+.Nm
+replies with a one-byte message of its own,
+such that the parent process can keep track of how many messages
+.Nm
+has already accepted and how many file descriptors
+consequently are still in flight, such that the parent process
+can limit the number of file descriptors in flight at any given time
+in order to prevent
+.Er EMFILE
+failure of
+.Xr sendmsg 2 .
+.Pp
The options are as follows:
.Bl -tag -width Ds
.It Fl I Cm os Ns = Ns Ar name
@@ -112,7 +125,7 @@ Other output options are not supported.
After exhausting one input file descriptor, all three file descriptors
are closed before reading the next dummy byte and control message.
.Pp
-When a zero-byte message is read, when the
+When a zero-byte message or a misformatted message is read, when the
.Ar socket_fd
is closed by the parent process,
or when an error occurs,
@@ -131,9 +144,10 @@ missing, invalid, or excessive
.Xr exec 3
arguments
.It
+communication failure with the parent, for example failure in
.Xr recvmsg 2
-failure, for example due to
-.Er EMSGSIZE
+or
+.Xr send 2
.It
missing or unexpected control data, in particular a
.Fa cmsg_level
diff --git a/contrib/mandoc/mandocd.c b/contrib/mandoc/mandocd.c
index ccc846bd0310..52ba0cc613fa 100644
--- a/contrib/mandoc/mandocd.c
+++ b/contrib/mandoc/mandocd.c
@@ -1,7 +1,7 @@
-/* $Id: mandocd.c,v 1.13 2022/04/14 16:43:44 schwarze Exp $ */
+/* $Id: mandocd.c,v 1.15 2025/06/30 15:04:57 schwarze Exp $ */
/*
+ * Copyright (c) 2017-2019, 2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2017 Michael Stapelberg <stapelberg@debian.org>
- * Copyright (c) 2017, 2019, 2021 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,11 +27,14 @@
#if HAVE_ERR
#include <err.h>
#endif
+#include <errno.h>
#include <limits.h>
+#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <time.h>
#include <unistd.h>
#include "mandoc.h"
@@ -61,6 +64,7 @@ static void usage(void) __attribute__((__noreturn__));
static int
read_fds(int clientfd, int *fds)
{
+ const struct timespec timeout = { 0, 10000000 }; /* 0.01 s */
struct msghdr msg;
struct iovec iov[1];
unsigned char dummy[1];
@@ -98,6 +102,15 @@ read_fds(int clientfd, int *fds)
break;
}
+ *dummy = '\0';
+ while (send(clientfd, dummy, sizeof(dummy), 0) == -1) {
+ if (errno != EAGAIN) {
+ warn("send");
+ return -1;
+ }
+ nanosleep(&timeout, NULL);
+ }
+
if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
warnx("CMSG_FIRSTHDR: missing control message");
return -1;
@@ -120,6 +133,7 @@ read_fds(int clientfd, int *fds)
int
main(int argc, char *argv[])
{
+ struct sigaction sa;
struct manoutput options;
struct mparse *parser;
void *formatter;
@@ -170,13 +184,25 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
}
- if (argc != 1)
+ if (argc != 1) {
+ if (argc == 0)
+ warnx("missing argument: socket_fd");
+ else
+ warnx("too many arguments: %s", argv[1]);
usage();
+ }
errstr = NULL;
clientfd = strtonum(argv[0], 3, INT_MAX, &errstr);
if (errstr)
- errx(1, "file descriptor %s %s", argv[1], errstr);
+ errx(1, "file descriptor %s is %s", argv[0], errstr);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ if (sigfillset(&sa.sa_mask) == -1)
+ err(1, "sigfillset");
+ if (sigaction(SIGPIPE, &sa, NULL) == -1)
+ err(1, "sigaction(SIGPIPE)");
mchars_alloc();
parser = mparse_alloc(MPARSE_SO | MPARSE_UTF8 | MPARSE_LATIN1 |
diff --git a/contrib/mandoc/manpath.c b/contrib/mandoc/manpath.c
index 3760e2293c3a..f744368b5a38 100644
--- a/contrib/mandoc/manpath.c
+++ b/contrib/mandoc/manpath.c
@@ -1,6 +1,6 @@
-/* $Id: manpath.c,v 1.44 2021/11/05 18:03:08 schwarze Exp $ */
+/* $Id: manpath.c,v 1.45 2025/06/26 17:26:23 schwarze Exp $ */
/*
- * Copyright (c) 2011,2014,2015,2017-2019 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011,2014,2015,2017-2021 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -218,7 +218,7 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile)
/* Token taking an optional argument. */
"tag",
/* Tokens not taking arguments. */
- "fragment", "mdoc", "noval", "toc"
+ "fragment", "noval", "toc"
};
const size_t ntoks = sizeof(toks) / sizeof(toks[0]);
@@ -328,12 +328,9 @@ manconf_output(struct manoutput *conf, const char *cp, int fromfile)
conf->fragment = 1;
return 0;
case 10:
- conf->mdoc = 1;
- return 0;
- case 11:
conf->noval = 1;
return 0;
- case 12:
+ case 11:
conf->toc = 1;
return 0;
default:
diff --git a/contrib/mandoc/mdoc_html.c b/contrib/mandoc/mdoc_html.c
index b67eac4be233..8ac3884c7225 100644
--- a/contrib/mandoc/mdoc_html.c
+++ b/contrib/mandoc/mdoc_html.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_html.c,v 1.353 2025/01/25 00:22:28 schwarze Exp $ */
+/* $Id: mdoc_html.c,v 1.354 2025/06/26 17:06:34 schwarze Exp $ */
/*
* Copyright (c) 2014-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2011, 2014 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -454,20 +454,29 @@ static void
mdoc_root_post(const struct roff_meta *meta, struct html *h)
{
struct tag *t;
+ char *title;
+
+ assert(meta->title != NULL);
+ if (meta->msec == NULL)
+ title = mandoc_strdup(meta->title);
+ else
+ mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
t = print_otag(h, TAG_DIV, "cr?", "foot", "doc-pagefooter",
"aria-label", "Manual footer line");
print_otag(h, TAG_SPAN, "c", "foot-left");
+ print_text(h, meta->os);
print_stagq(h, t);
print_otag(h, TAG_SPAN, "c", "foot-date");
print_text(h, meta->date);
print_stagq(h, t);
- print_otag(h, TAG_SPAN, "c", "foot-os");
- print_text(h, meta->os);
+ print_otag(h, TAG_SPAN, "c", "foot-right");
+ print_text(h, title);
print_tagq(h, t);
+ free(title);
}
static int
diff --git a/contrib/mandoc/mdoc_man.c b/contrib/mandoc/mdoc_man.c
index 5438b2ba5941..99693b5d81dd 100644
--- a/contrib/mandoc/mdoc_man.c
+++ b/contrib/mandoc/mdoc_man.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_man.c,v 1.139 2025/01/24 22:37:24 schwarze Exp $ */
+/* $Id: mdoc_man.c,v 1.141 2025/07/02 19:57:48 schwarze Exp $ */
/*
* Copyright (c) 2011-2021, 2025 Ingo Schwarze <schwarze@openbsd.org>
*
@@ -494,6 +494,7 @@ print_offs(const char *v, int keywords)
const char *end;
int sz;
+ outflags &= ~MMAN_PP;
print_line(".RS", MMAN_Bk_susp);
/* Convert v into a number (of characters). */
@@ -1616,9 +1617,7 @@ pre_lk(DECL_ARGS)
}
/* Link target. */
- font_push('B');
print_word(link->string);
- font_pop();
/* Trailing punctuation. */
while (punct != NULL) {
diff --git a/contrib/mandoc/mdoc_markdown.c b/contrib/mandoc/mdoc_markdown.c
index 06ca839a58b8..eaa22626c99c 100644
--- a/contrib/mandoc/mdoc_markdown.c
+++ b/contrib/mandoc/mdoc_markdown.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_markdown.c,v 1.39 2025/01/20 07:01:17 schwarze Exp $ */
+/* $Id: mdoc_markdown.c,v 1.40 2025/06/26 17:06:34 schwarze Exp $ */
/*
* Copyright (c) 2017, 2018, 2020, 2025 Ingo Schwarze <schwarze@openbsd.org>
*
@@ -292,6 +292,14 @@ markdown_mdoc(void *arg, const struct roff_meta *mdoc)
md_word(mdoc->os);
md_word("-");
md_word(mdoc->date);
+ md_word("-");
+ md_word(mdoc->title);
+ if (mdoc->msec != NULL) {
+ outflags &= ~MD_spc;
+ md_word("(");
+ md_word(mdoc->msec);
+ md_word(")");
+ }
putchar('\n');
}
diff --git a/contrib/mandoc/mdoc_term.c b/contrib/mandoc/mdoc_term.c
index 931bc384a002..b0544de0304e 100644
--- a/contrib/mandoc/mdoc_term.c
+++ b/contrib/mandoc/mdoc_term.c
@@ -1,6 +1,6 @@
-/* $Id: mdoc_term.c,v 1.383 2023/11/13 19:13:01 schwarze Exp $ */
+/* $Id: mdoc_term.c,v 1.387 2025/07/27 15:27:28 schwarze Exp $ */
/*
- * Copyright (c) 2010, 2012-2020, 2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010,2012-2020,2022,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
* Copyright (c) 2013 Franco Fichtner <franco@lastsummer.de>
*
@@ -302,7 +302,7 @@ print_mdoc_node(DECL_ARGS)
{
const struct mdoc_term_act *act;
struct termpair npair;
- size_t offset, rmargin;
+ size_t offset, rmargin; /* In basic units. */
int chld;
/*
@@ -441,70 +441,62 @@ print_mdoc_node(DECL_ARGS)
static void
print_mdoc_foot(struct termp *p, const struct roff_meta *meta)
{
- size_t sz;
+ char *title;
+ size_t datelen, titlen; /* In basic units. */
- term_fontrepl(p, TERMFONT_NONE);
-
- /*
- * Output the footer in new-groff style, that is, three columns
- * with the middle being the manual date and flanking columns
- * being the operating system:
- *
- * SYSTEM DATE SYSTEM
- */
+ assert(meta->title != NULL);
+ datelen = term_strlen(p, meta->date);
+ if (meta->msec == NULL)
+ title = mandoc_strdup(meta->title);
+ else
+ mandoc_asprintf(&title, "%s(%s)", meta->title, meta->msec);
+ titlen = term_strlen(p, title);
+ term_fontrepl(p, TERMFONT_NONE);
term_vspace(p);
+ /* Bottom left corner: operating system. */
+
p->tcol->offset = 0;
- sz = term_strlen(p, meta->date);
- p->tcol->rmargin = p->maxrmargin > sz ?
- (p->maxrmargin + term_len(p, 1) - sz) / 2 : 0;
+ p->tcol->rmargin = p->maxrmargin > datelen ?
+ (p->maxrmargin + term_len(p, 1) - datelen) / 2 : 0;
p->trailspace = 1;
p->flags |= TERMP_NOSPACE | TERMP_NOBREAK;
term_word(p, meta->os);
term_flushln(p);
+ /* At the bottom in the middle: manual date. */
+
p->tcol->offset = p->tcol->rmargin;
- sz = term_strlen(p, meta->os);
- p->tcol->rmargin = p->maxrmargin > sz ? p->maxrmargin - sz : 0;
+ p->tcol->rmargin = p->maxrmargin > titlen ?
+ p->maxrmargin - titlen : 0;
p->flags |= TERMP_NOSPACE;
term_word(p, meta->date);
term_flushln(p);
+ /* Bottom right corner: manual title and section. */
+
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->maxrmargin;
p->trailspace = 0;
p->flags &= ~TERMP_NOBREAK;
p->flags |= TERMP_NOSPACE;
- term_word(p, meta->os);
+ term_word(p, title);
term_flushln(p);
p->tcol->offset = 0;
- p->tcol->rmargin = p->maxrmargin;
p->flags = 0;
+ free(title);
}
static void
print_mdoc_head(struct termp *p, const struct roff_meta *meta)
{
char *volume, *title;
- size_t vollen, titlen;
-
- /*
- * The header is strange. It has three components, which are
- * really two with the first duplicated. It goes like this:
- *
- * IDENTIFIER TITLE IDENTIFIER
- *
- * The IDENTIFIER is NAME(SECTION), which is the command-name
- * (if given, or "unknown" if not) followed by the manual page
- * section. These are given in `Dt'. The TITLE is a free-form
- * string depending on the manual volume. If not specified, it
- * switches on the manual section.
- */
+ size_t vollen, titlen; /* In basic units. */
assert(meta->vol);
if (NULL == meta->arch)
@@ -514,6 +506,8 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta)
meta->vol, meta->arch);
vollen = term_strlen(p, volume);
+ /* Top left corner: manual title and section. */
+
if (NULL == meta->msec)
title = mandoc_strdup(meta->title);
else
@@ -524,13 +518,16 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta)
p->flags |= TERMP_NOBREAK | TERMP_NOSPACE;
p->trailspace = 1;
p->tcol->offset = 0;
- p->tcol->rmargin = 2 * (titlen+1) + vollen < p->maxrmargin ?
+ p->tcol->rmargin =
+ titlen * 2 + term_len(p, 2) + vollen < p->maxrmargin ?
(p->maxrmargin - vollen + term_len(p, 1)) / 2 :
- vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
+ vollen < p->maxrmargin ? p->maxrmargin - vollen : 0;
term_word(p, title);
term_flushln(p);
+ /* At the top in the middle: manual volume. */
+
p->flags |= TERMP_NOSPACE;
p->tcol->offset = p->tcol->rmargin;
p->tcol->rmargin = p->tcol->offset + vollen + titlen <
@@ -539,6 +536,8 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta)
term_word(p, volume);
term_flushln(p);
+ /* Top right corner: title and section, again. */
+
p->flags &= ~TERMP_NOBREAK;
p->trailspace = 0;
if (p->tcol->rmargin + titlen <= p->maxrmargin) {
@@ -556,6 +555,11 @@ print_mdoc_head(struct termp *p, const struct roff_meta *meta)
free(volume);
}
+/*
+ * Interpret the string v as a scaled width or, if the syntax is invalid,
+ * measure how much width it takes up when printed. In both cases,
+ * return the width in basic units.
+ */
static int
a2width(const struct termp *p, const char *v)
{
@@ -564,10 +568,10 @@ a2width(const struct termp *p, const char *v)
end = a2roffsu(v, &su, SCALE_MAX);
if (end == NULL || *end != '\0') {
- su.unit = SCALE_EN;
- su.scale = term_strlen(p, v) / term_strlen(p, "0");
+ su.unit = SCALE_BU;
+ su.scale = term_strlen(p, v);
}
- return term_hen(p, &su);
+ return term_hspan(p, &su);
}
/*
@@ -623,8 +627,11 @@ termp_it_pre(DECL_ARGS)
struct roffsu su;
char buf[24];
const struct roff_node *bl, *nn;
- size_t ncols, dcol;
- int i, offset, width;
+ size_t ncols; /* Number of columns in .Bl -column. */
+ size_t dcol; /* Column spacing in basic units. */
+ int i; /* Zero-based column index. */
+ int offset; /* Start of column in basic units. */
+ int width; /* Column width in basic units. */
enum mdoc_list type;
if (n->type == ROFFT_BLOCK) {
@@ -701,10 +708,9 @@ termp_it_pre(DECL_ARGS)
for (i = 0, nn = n->prev;
nn->prev && i < (int)ncols;
nn = nn->prev, i++) {
- su.unit = SCALE_EN;
- su.scale = term_strlen(p, bl->norm->Bl.cols[i]) /
- term_strlen(p, "0");
- offset += term_hen(p, &su) + dcol;
+ su.unit = SCALE_BU;
+ su.scale = term_strlen(p, bl->norm->Bl.cols[i]);
+ offset += term_hspan(p, &su) + dcol;
}
/*
@@ -720,10 +726,9 @@ termp_it_pre(DECL_ARGS)
* Use the declared column widths, extended as explained
* in the preceding paragraph.
*/
- su.unit = SCALE_EN;
- su.scale = term_strlen(p, bl->norm->Bl.cols[i]) /
- term_strlen(p, "0");
- width = term_hen(p, &su) + dcol;
+ su.unit = SCALE_BU;
+ su.scale = term_strlen(p, bl->norm->Bl.cols[i]);
+ width = term_hspan(p, &su) + dcol;
break;
default:
if (NULL == bl->norm->Bl.width)
@@ -1274,6 +1279,7 @@ termp_sh_pre(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
+ p->fontibi = 1;
return termp_bold_pre(p, pair, meta, n);
case ROFFT_BODY:
p->tcol->offset = term_len(p, p->defindent);
@@ -1294,6 +1300,7 @@ termp_sh_post(DECL_ARGS)
{
switch (n->type) {
case ROFFT_HEAD:
+ p->fontibi = 0;
term_newln(p);
break;
case ROFFT_BODY:
@@ -1421,7 +1428,7 @@ termp_fa_pre(DECL_ARGS)
static int
termp_bd_pre(DECL_ARGS)
{
- int offset;
+ int offset; /* In basic units. */
if (n->type == ROFFT_BLOCK) {
print_bvspace(p, n, n);
@@ -1509,7 +1516,8 @@ termp_ss_pre(DECL_ARGS)
term_vspace(p);
break;
case ROFFT_HEAD:
- p->tcol->offset = term_len(p, (p->defindent+1)/2);
+ p->tcol->offset = term_len(p, p->defindent) / 2 + 1;
+ p->fontibi = 1;
return termp_bold_pre(p, pair, meta, n);
case ROFFT_BODY:
p->tcol->offset = term_len(p, p->defindent);
@@ -1526,8 +1534,16 @@ termp_ss_pre(DECL_ARGS)
static void
termp_ss_post(DECL_ARGS)
{
- if (n->type == ROFFT_HEAD || n->type == ROFFT_BODY)
+ switch (n->type) {
+ case ROFFT_HEAD:
+ p->fontibi = 0;
+ /* FALLTHROUGH */
+ case ROFFT_BODY:
term_newln(p);
+ break;
+ default:
+ break;
+ }
}
static int
@@ -1888,9 +1904,7 @@ termp_lk_pre(DECL_ARGS)
}
/* Link target. */
- term_fontpush(p, TERMFONT_BOLD);
term_word(p, link->string);
- term_fontpop(p);
/* Trailing punctuation. */
while (punct != NULL) {
diff --git a/contrib/mandoc/mdoc_validate.c b/contrib/mandoc/mdoc_validate.c
index 4ca1253e4b70..ac265b88f484 100644
--- a/contrib/mandoc/mdoc_validate.c
+++ b/contrib/mandoc/mdoc_validate.c
@@ -1,4 +1,4 @@
-/* $Id: mdoc_validate.c,v 1.393 2025/06/05 12:38:26 schwarze Exp $ */
+/* $Id: mdoc_validate.c,v 1.396 2025/07/26 12:23:16 schwarze Exp $ */
/*
* Copyright (c) 2010-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
@@ -1714,7 +1714,7 @@ post_xx(POST_ARGS)
os = "OpenBSD";
break;
case MDOC_Ux:
- os = "UNIX";
+ os = "Unix";
break;
default:
abort();
@@ -2777,7 +2777,7 @@ post_dd(POST_ARGS)
mandoc_msg(MANDOCERR_PROLOG_ORDER,
n->line, n->pos, "Dd after Os");
- if (mdoc->quick && n != NULL)
+ if (mdoc->quick)
mdoc->meta.date = mandoc_strdup("");
else
mdoc->meta.date = mandoc_normdate(n->child, n);
@@ -2842,8 +2842,7 @@ post_dt(POST_ARGS)
if (nn == NULL) {
mandoc_msg(MANDOCERR_MSEC_MISSING, n->line, n->pos,
"Dt %s", mdoc->meta.title);
- mdoc->meta.vol = mandoc_strdup("LOCAL");
- return; /* msec and arch remain NULL. */
+ return; /* msec, vol, and arch remain NULL. */
}
mdoc->meta.msec = mandoc_strdup(nn->string);
@@ -2854,7 +2853,6 @@ post_dt(POST_ARGS)
if (cp == NULL) {
mandoc_msg(MANDOCERR_MSEC_BAD,
nn->line, nn->pos, "Dt ... %s", nn->string);
- mdoc->meta.vol = mandoc_strdup(nn->string);
} else {
mdoc->meta.vol = mandoc_strdup(cp);
if (mdoc->filesec != '\0' &&
diff --git a/contrib/mandoc/out.c b/contrib/mandoc/out.c
index f6f5859a1629..21c282b2141b 100644
--- a/contrib/mandoc/out.c
+++ b/contrib/mandoc/out.c
@@ -1,8 +1,8 @@
-/* $Id: out.c,v 1.86 2025/01/05 18:14:39 schwarze Exp $ */
+/* $Id: out.c,v 1.87 2025/07/16 14:33:08 schwarze Exp $ */
/*
- * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2019, 2021
+ * Copyright (c) 2011, 2014, 2015, 2017, 2018, 2019, 2021, 2025
* Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -122,9 +122,23 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
const struct tbl_dat *dp;
struct roffcol *col;
struct tbl_colgroup *first_group, **gp, *g;
- size_t *colwidth;
- size_t ewidth, min1, min2, wanted, width, xwidth;
- int done, icol, maxcol, necol, nxcol, quirkcol;
+
+ /* Widths in basic units. */
+ size_t *colwidth; /* Widths of all columns. */
+ size_t min1; /* Width of the narrowest column. */
+ size_t min2; /* Width of the second narrowest column. */
+ size_t wanted; /* For any of the narrowest columns. */
+ size_t xwidth; /* Total width of columns not to expand. */
+ size_t ewidth; /* Width of widest column to equalize. */
+ size_t width; /* Width of the data in basic units. */
+ size_t enw; /* Width of one EN unit. */
+
+ int icol; /* Column number, starting at zero. */
+ int maxcol; /* Number of last column. */
+ int necol; /* Number of columns to equalize. */
+ int nxcol; /* Number of columns to expand. */
+ int done; /* Boolean: this group is wide enough. */
+ int quirkcol;
/*
* Allocate the master column specifiers. These will hold the
@@ -139,6 +153,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
maxcol = -1;
first_group = NULL;
+ enw = (*tbl->len)(1, tbl->arg);
for (sp = sp_first; sp != NULL; sp = sp->next) {
if (sp->pos != TBL_SPAN_DATA)
continue;
@@ -175,8 +190,8 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
opts, dp,
dp->block == 0 ? 0 :
dp->layout->width ? dp->layout->width :
- rmargin ? (rmargin + sp->opts->cols / 2)
- / (sp->opts->cols + 1) : 0);
+ rmargin ? (rmargin / enw + sp->opts->cols / 2) /
+ (sp->opts->cols + 1) * enw : 0);
if (dp->hspans == 0)
continue;
@@ -211,8 +226,8 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
maxcol = sp_first->opts->cols - 1;
for (icol = 0; icol <= maxcol; icol++) {
col = tbl->cols + icol;
- if (col->width < 1)
- col->width = 1;
+ if (col->width < enw)
+ col->width = enw;
/*
* Column spacings are needed for span width
@@ -234,7 +249,8 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
for (icol = g->startcol; icol <= g->endcol; icol++) {
width = tbl->cols[icol].width;
if (icol < g->endcol)
- width += tbl->cols[icol].spacing;
+ width += (*tbl->len)(tbl->cols[icol].spacing,
+ tbl->arg);
if (g->wanted <= width) {
done = 1;
break;
@@ -372,9 +388,9 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
*/
if (nxcol && rmargin) {
- xwidth += 3*maxcol +
+ xwidth += (*tbl->len)(3 * maxcol +
(opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
- 2 : !!opts->lvert + !!opts->rvert);
+ 2 : !!opts->lvert + !!opts->rvert), tbl->arg);
if (rmargin <= offset + xwidth)
return;
xwidth = rmargin - offset - xwidth;
@@ -387,7 +403,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
*/
if (nxcol == 5) {
- quirkcol = xwidth % nxcol + 2;
+ quirkcol = xwidth / enw % nxcol + 2;
if (quirkcol != 3 && quirkcol != 4)
quirkcol = -1;
} else
@@ -402,7 +418,7 @@ tblcalc(struct rofftbl *tbl, const struct tbl_span *sp_first,
col->width = (double)xwidth * ++necol / nxcol
- ewidth + 0.4995;
if (necol == quirkcol)
- col->width--;
+ col->width -= enw;
ewidth += col->width;
}
}
@@ -444,9 +460,12 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
const char *str; /* Beginning of the first line. */
const char *beg; /* Beginning of the current line. */
char *end; /* End of the current line. */
- size_t lsz; /* Length of the current line. */
- size_t wsz; /* Length of the current word. */
- size_t msz; /* Length of the longest line. */
+
+ /* Widths in basic units. */
+ size_t lsz; /* Of the current line. */
+ size_t wsz; /* Of the current word. */
+ size_t msz; /* Of the longest line. */
+ size_t enw; /* Of one EN unit. */
if (dp->string == NULL || *dp->string == '\0')
return 0;
@@ -460,8 +479,9 @@ tblcalc_literal(struct rofftbl *tbl, struct roffcol *col,
end++;
}
wsz = (*tbl->slen)(beg, tbl->arg);
- if (mw && lsz && lsz + 1 + wsz <= mw)
- lsz += 1 + wsz;
+ enw = (*tbl->len)(1, tbl->arg);
+ if (mw && lsz && lsz + enw + wsz <= mw)
+ lsz += enw + wsz;
else
lsz = wsz;
if (msz < lsz)
@@ -479,7 +499,8 @@ tblcalc_number(struct rofftbl *tbl, struct roffcol *col,
const struct tbl_opts *opts, const struct tbl_dat *dp)
{
const char *cp, *lastdigit, *lastpoint;
- size_t intsz, totsz;
+ size_t totsz; /* Total width of the number in basic units. */
+ size_t intsz; /* Width of the integer part in basic units. */
char buf[2];
if (dp->string == NULL || *dp->string == '\0')
diff --git a/contrib/mandoc/out.h b/contrib/mandoc/out.h
index f746e4486958..a3b49b70460d 100644
--- a/contrib/mandoc/out.h
+++ b/contrib/mandoc/out.h
@@ -1,7 +1,7 @@
-/* $Id: out.h,v 1.35 2022/09/11 09:13:48 schwarze Exp $ */
+/* $Id: out.h,v 1.36 2025/07/16 14:33:08 schwarze Exp $ */
/*
+ * Copyright (c) 2011,2014,2017,2018,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014, 2017, 2018 Ingo Schwarze <schwarze@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@@ -33,11 +33,11 @@ enum roffscale {
};
struct roffcol {
- size_t width; /* width of cell */
- size_t nwidth; /* max. width of number in cell */
- size_t decimal; /* decimal position in cell */
- size_t spacing; /* spacing after the column */
- int flags; /* layout flags, see tbl_cell */
+ size_t width; /* Width of cell [BU]. */
+ size_t nwidth; /* Maximum width of number [BU]. */
+ size_t decimal; /* Decimal position [BU]. */
+ size_t spacing; /* Spacing after the column [EN]. */
+ int flags; /* Layout flags, see tbl_cell. */
};
struct roffsu {
@@ -45,16 +45,14 @@ struct roffsu {
double scale;
};
-typedef size_t (*tbl_sulen)(const struct roffsu *, void *);
typedef size_t (*tbl_strlen)(const char *, void *);
typedef size_t (*tbl_len)(size_t, void *);
struct rofftbl {
- tbl_sulen sulen; /* calculate scaling unit length */
- tbl_strlen slen; /* calculate string length */
- tbl_len len; /* produce width of empty space */
- struct roffcol *cols; /* master column specifiers */
- void *arg; /* passed to sulen, slen, and len */
+ tbl_strlen slen; /* Calculate string length [BU]. */
+ tbl_len len; /* Produce width of empty space [BU]. */
+ struct roffcol *cols; /* Master column specifiers. */
+ void *arg; /* Passed to slen() and len(). */
};
diff --git a/contrib/mandoc/roff.7 b/contrib/mandoc/roff.7
index 27f83853e75b..adb5852e069b 100644
--- a/contrib/mandoc/roff.7
+++ b/contrib/mandoc/roff.7
@@ -1,6 +1,6 @@
-.\" $Id: roff.7,v 1.121 2023/10/23 20:25:02 schwarze Exp $
+.\" $Id: roff.7,v 1.123 2025/08/04 23:12:08 schwarze Exp $
.\"
-.\" Copyright (c) 2010-2019, 2022-2023 Ingo Schwarze <schwarze@openbsd.org>
+.\" Copyright (c) 2010-2019,2022-2023,2025 Ingo Schwarze <schwarze@openbsd.org>
.\" Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
.\"
.\" Permission to use, copy, modify, and distribute this software for any
@@ -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.
.\"
-.Dd $Mdocdate: October 23 2023 $
+.Dd $Mdocdate: August 4 2025 $
.Dt ROFF 7
.Os
.Sh NAME
@@ -79,8 +79,10 @@ They provide free-form text to be printed; the formatting of the text
depends on the respective processing context.
.Sh LANGUAGE SYNTAX
.Nm
-documents may contain only graphable 7-bit ASCII characters, the space
-character, and, in certain circumstances, the tab character.
+documents are text files containing only printable
+.Xr ascii 7
+characters, the space character,
+and, in certain circumstances, the tab character.
The backslash character
.Sq \e
indicates the start of an escape sequence, used for example for
@@ -180,7 +182,7 @@ Italic font.
Return to the previous font.
If a macro caused a font change since the last
.Ic \ef
-eascape sequence or
+escape sequence or
.Ic \&ft
request, this returns to the font before the last font change in
the macro rather than to the font before the last manual font change.
@@ -267,7 +269,7 @@ width of rendered
.Pq en
character
.It u
-default horizontal span for the terminal
+device-dependent basic units
.It M
mini-em (1/100 em)
.El
@@ -289,7 +291,7 @@ for vertical spaces and
for horizontal ones.
.Pp
Examples:
-.Bl -tag -width ".Bl -tag -width 2i" -offset indent -compact
+.Bl -tag -width "xBl -tag -width 2i" -offset indent -compact
.It Li \&.Bl -tag -width 2i
two-inch tagged list indentation in
.Xr mdoc 7
@@ -1319,10 +1321,16 @@ among others because it overrides the
.Xr mandoc 1
.Fl O Cm width
command line option.
-.It Ic \&lnr Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar value Op Ar increment
+.It Ic \&lnr Ar registername Xo
+.Oo Cm + Ns | Ns Cm \- Oc Ns Ar value
+.Op Ar increment
+.Xc
Set local number register.
This is a Heirloom extension and currently unsupported.
-.It Ic \&lnrf Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar value Op Ar increment
+.It Ic \&lnrf Ar registername Xo
+.Oo Cm + Ns | Ns Cm \- Oc Ns Ar value
+.Op Ar increment
+.Xc
Set local floating-point register.
This is a Heirloom extension and currently unsupported.
.It Ic \&lpfx Ar string
@@ -1398,10 +1406,13 @@ skipping the
.Ic \&nop
request and any space characters immediately following it.
This is mostly used to indent text lines inside macro definitions.
-.It Ic \&nr Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar expression Op Ar stepsize
-Define or change a register.
-A register is an arbitrary string value that defines some sort of state,
-which influences parsing and/or formatting.
+.It Ic \&nr Ar registername Xo
+.Oo Cm + Ns | Ns Cm \- Oc Ns Ar expression
+.Op Ar stepsize
+.Xc
+Define or change the number register with the given
+.Ar registername .
+A register can store an integer number.
For the syntax of
.Ar expression ,
see
@@ -1410,6 +1421,9 @@ below.
If it is prefixed by a sign, the register will be
incremented or decremented instead of assigned to.
.Pp
+Once set, the value of a number register can be interpolated using the
+.Ic \en
+escape sequence.
The
.Ar stepsize
is used by the
@@ -1418,29 +1432,13 @@ auto-increment feature.
It remains unchanged when omitted while changing an existing register,
and it defaults to 0 when defining a new register.
.Pp
-The following
-.Ar register
-is handled specially:
-.Bl -tag -width Ds
-.It Cm nS
-If set to a positive integer value, certain
-.Xr mdoc 7
-macros will behave in the same way as in the
-.Em SYNOPSIS
-section.
-If set to 0, these macros will behave in the same way as outside the
-.Em SYNOPSIS
-section, even when called within the
-.Em SYNOPSIS
-section itself.
-Note that starting a new
-.Xr mdoc 7
-section with the
-.Ic \&Sh
-macro will reset this register.
-.El
+Some number registers can be read to inspect parser state,
+and some can be changed to influence formatting.
+For details about individual registers, see the
+.Sx NUMBER REGISTER REFERENCE
+below.
.It Xo
-.Ic \&nrf Ar register Oo Cm + Ns | Ns Cm - Oc Ns Ar expression
+.Ic \&nrf Ar registername Oo Cm + Ns | Ns Cm \- Oc Ns Ar expression
.Op Ar increment
.Xc
Define or change a floating-point register.
@@ -1569,7 +1567,7 @@ requests is not supported, and diversions are not implemented at all.
Rename a number register.
Currently unsupported.
.It Ic \&rr Ar register
-Remove a register.
+Remove a number register.
.It Ic \&rs
End no-space mode.
Currently ignored.
@@ -2152,6 +2150,8 @@ on the current font.
.It Ic \en Ns Oo +|- Oc Ns Ic \&[ Ns Ar name Ns Ic \&]
Interpolate the number register
.Ar name .
+If the register is not yet defined,
+it is automatically initialised to zero before interpolation.
For short names, there are variants
.Ic \en Ns Ar c
and
@@ -2162,6 +2162,9 @@ the register is first incremented or decremented by the
that was specified in the relevant
.Ic \&nr
request, and the changed value is interpolated.
+For the names of predefined registers, see the
+.Sx NUMBER REGISTER REFERENCE
+below.
.It Ic \eO Ns Ar digit , Ic \eO[5 Ns arguments Ns Ic \&]
Suppress output.
This is a groff extension and currently unsupported.
@@ -2250,6 +2253,83 @@ with zero width and height; ignored by
.It Ic \ez
Output the next character without advancing the cursor position.
.El
+.Sh NUMBER REGISTER REFERENCE
+In
+.Xr mdoc 7
+and
+.Xr man 7
+documents, using registers is discouraged.
+For compatibility with legacy documents, the
+.Xr mandoc 1
+.Nm
+parser recognises the following names of read-only registers:
+.Bl -tag -width Ds
+.It Cm .$
+The number of arguments of the innermost user-defined macro
+currently being called, or 0 by default.
+The
+.Ic shift
+request decrements the value of this register.
+.It Cm .A
+Whether ASCII approximation mode is on;
+.Xr mandoc 1
+always returns 0, meaning off.
+.It Cm .g
+Whether the formatter claims groff compatibility;
+.Xr mandoc 1
+always returns 1, meaning yes.
+.It Cm .H
+The minimum horizontal movement in basic units;
+.Xr mandoc 1
+always returns 24, corresponding to one character position.
+.It Cm .j
+The current line adjustment mode;
+.Xr mandoc 1
+always returns 0, meaning flush left.
+.It Cm .l
+The line length in basic units;
+.Xr mandoc 1
+always returns 78 * 24, corresponding to 78 characters per line.
+.It Cm \&.T
+Whether an output device has been selected;
+.Xr mandoc 1
+always returns 1, meaning yes.
+.It Cm .V
+The minimum vertical movement in basic units;
+.Xr mandoc 1
+always returns 40, corresponding to one line height.
+.El
+.Pp
+The
+.Cm nS
+register is handled specially.
+If set to a positive integer value, certain
+.Xr mdoc 7
+macros behave in the same way as in the
+.Em SYNOPSIS
+section.
+If set to 0, these macros behave in the same way as outside the
+.Em SYNOPSIS
+section, even when called within the
+.Em SYNOPSIS
+section itself.
+Starting a new
+.Xr mdoc 7
+section with the
+.Ic \&Sh
+macro resets this register.
+.Pp
+Full
+.Nm
+implementations support large numbers of additional predefined registers.
+While the
+.Ic \&nr
+request supports setting and the
+.Ic \en
+escape sequence supports inspecting arbitrary registers,
+.Xr mandoc 1
+only defines the few registers listed above by default.
+All other registers are undefined by default and yield 0 when interpolated.
.Sh COMPATIBILITY
The
.Xr mandoc 1
@@ -2266,17 +2346,29 @@ never reads or writes external files except via
.Ic \&so
requests with safe relative paths.
.It
-There is no automatic hyphenation, no adjustment to the right margin,
-and very limited support for centering; the output is always set flush-left.
-.It
-Support for setting tabulator and leader characters is missing,
-and support for manually changing indentation is limited.
+There is no automatic hyphenation and no support for the
+.Ic \&ad
+line adjustment request.
+Except when the
+.Ic \&ce
+or
+.Ic \&rj
+requests or the
+.Xr tbl 7
+cell specifications
+.Cm c ,
+.Cm n ,
+or
+.Cm r
+or the table option
+.Cm center
+are used, output is always set flush-left.
.It
-The
-.Sq u
-scaling unit is the default terminal unit.
-In traditional troff systems, this unit changes depending on the
-output media.
+Support for setting tabulator and leader characters is missing, and the
+.Ic \&in
+indentation request is not supported in
+.Xr mdoc 7
+input files.
.It
Width measurements are implemented in a crude way
and often yield wrong results.
@@ -2336,6 +2428,15 @@ implementations.
.%D September 17, 2007
.%U http://heirloom.sourceforge.net/doctools/troff.pdf
.Re
+.Rs
+.%A James Clark
+.%A Werner Lemberg
+.%A G. Branden Robinson
+.%I Free Software Foundation, Inc.
+.%T The GNU Troff Manual
+.%D 1999\(en2023
+.%U https://www.gnu.org/software/groff/manual/
+.Re
.Sh HISTORY
The RUNOFF typesetting system, whose input forms the basis for
.Nm ,
diff --git a/contrib/mandoc/roff_term.c b/contrib/mandoc/roff_term.c
index f696898ebd5a..8f95aa920790 100644
--- a/contrib/mandoc/roff_term.c
+++ b/contrib/mandoc/roff_term.c
@@ -1,6 +1,7 @@
-/* $Id: roff_term.c,v 1.25 2023/04/28 19:11:04 schwarze Exp $ */
+/* $Id: roff_term.c,v 1.26 2025/07/16 14:33:08 schwarze Exp $ */
/*
- * Copyright (c) 2010,2014,2015,2017-2020 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010, 2014, 2015, 2017-2021, 2025
+ * 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
@@ -159,8 +160,13 @@ static void
roff_term_pre_po(ROFF_TERM_ARGS)
{
struct roffsu su;
- static int po, pouse, polast;
- int ponew;
+
+ /* Page offsets in basic units. */
+ static int polast; /* Previously requested. */
+ static int po; /* Currently requested. */
+ static int pouse; /* Currently used. */
+ int pomax; /* Maximum to be used. */
+ int ponew; /* Newly requested. */
/* Revert the currently active page offset. */
p->tcol->offset -= pouse;
@@ -168,7 +174,7 @@ roff_term_pre_po(ROFF_TERM_ARGS)
/* Determine the requested page offset. */
if (n->child != NULL &&
a2roffsu(n->child->string, &su, SCALE_EM) != NULL) {
- ponew = term_hen(p, &su);
+ ponew = term_hspan(p, &su);
if (*n->child->string == '+' ||
*n->child->string == '-')
ponew += po;
@@ -180,8 +186,9 @@ roff_term_pre_po(ROFF_TERM_ARGS)
po = ponew;
/* Truncate to the range [-offset, 60], remember, and apply it. */
- pouse = po >= 60 ? 60 :
- po < -(int)p->tcol->offset ? -(int)p->tcol->offset : po;
+ pomax = term_len(p, 60);
+ pouse = po >= pomax ? pomax :
+ po < -(int)p->tcol->offset ? -p->tcol->offset : po;
p->tcol->offset += pouse;
}
@@ -219,9 +226,10 @@ static void
roff_term_pre_ti(ROFF_TERM_ARGS)
{
struct roffsu su;
- const char *cp;
- const size_t maxoff = 72;
- int len, sign;
+ const char *cp; /* Request argument. */
+ size_t maxoff; /* Maximum indentation in basic units. */
+ int len; /* Request argument in basic units. */
+ int sign;
roff_term_pre_br(p, n);
@@ -239,7 +247,8 @@ roff_term_pre_ti(ROFF_TERM_ARGS)
if (a2roffsu(cp, &su, SCALE_EM) == NULL)
return;
- len = term_hen(p, &su);
+ len = term_hspan(p, &su);
+ maxoff = term_len(p, 72);
switch (sign) {
case 1:
diff --git a/contrib/mandoc/tbl.h b/contrib/mandoc/tbl.h
index 5e98735d6f97..c7566c110f42 100644
--- a/contrib/mandoc/tbl.h
+++ b/contrib/mandoc/tbl.h
@@ -1,7 +1,7 @@
-/* $Id: tbl.h,v 1.3 2025/01/05 18:14:39 schwarze Exp $ */
+/* $Id: tbl.h,v 1.4 2025/07/16 14:33:08 schwarze Exp $ */
/*
+ * Copyright (c) 2014-2018, 2021, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014,2015,2017,2018,2021 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,8 +27,8 @@ struct tbl_opts {
#define TBL_OPT_NOSPACE (1 << 6) /* Option "nospaces". */
#define TBL_OPT_NOWARN (1 << 7) /* Option "nowarn". */
int cols; /* Number of columns. */
- int lvert; /* Width of left vertical line. */
- int rvert; /* Width of right vertical line. */
+ int lvert; /* Width of left vertical line in EN. */
+ int rvert; /* Width of right vertical line in EN. */
char tab; /* Option "tab": cell separator. */
char decimal; /* Option "decimalpoint". */
};
@@ -51,9 +51,9 @@ enum tbl_cellt {
*/
struct tbl_cell {
struct tbl_cell *next; /* Layout cell to the right. */
- size_t width; /* Minimum column width. */
- size_t spacing; /* To the right of the column. */
- int vert; /* Width of subsequent vertical line. */
+ size_t width; /* Minimum column width in basic units. */
+ size_t spacing; /* To the right of the column in EN. */
+ int vert; /* Width of subseq. vertical line in EN. */
int col; /* Column number, starting from 0. */
int flags;
#define TBL_CELL_TALIGN (1 << 2) /* t, T */
@@ -73,7 +73,7 @@ struct tbl_row {
struct tbl_row *next; /* Layout row below. */
struct tbl_cell *first; /* Leftmost layout cell. */
struct tbl_cell *last; /* Rightmost layout cell. */
- int vert; /* Width of left vertical line. */
+ int vert; /* Width of left vertical line in EN. */
};
enum tbl_datt {
diff --git a/contrib/mandoc/tbl_html.c b/contrib/mandoc/tbl_html.c
index 57d90c4c2d67..56ea3c08eef4 100644
--- a/contrib/mandoc/tbl_html.c
+++ b/contrib/mandoc/tbl_html.c
@@ -1,4 +1,4 @@
-/* $Id: tbl_html.c,v 1.41 2022/04/23 14:02:17 schwarze Exp $ */
+/* $Id: tbl_html.c,v 1.42 2025/07/16 14:33:08 schwarze Exp $ */
/*
* Copyright (c) 2014, 2015, 2017, 2018, 2021, 2022
* Ingo Schwarze <schwarze@openbsd.org>
@@ -37,7 +37,6 @@
static void html_tblopen(struct html *, const struct tbl_span *);
static size_t html_tbl_len(size_t, void *);
static size_t html_tbl_strlen(const char *, void *);
-static size_t html_tbl_sulen(const struct roffsu *, void *);
static size_t
@@ -52,36 +51,6 @@ html_tbl_strlen(const char *p, void *arg)
return strlen(p);
}
-static size_t
-html_tbl_sulen(const struct roffsu *su, void *arg)
-{
- if (su->scale < 0.0)
- return 0;
-
- switch (su->unit) {
- case SCALE_FS: /* 2^16 basic units */
- return su->scale * 65536.0 / 24.0;
- case SCALE_IN: /* 10 characters per inch */
- return su->scale * 10.0;
- case SCALE_CM: /* 2.54 cm per inch */
- return su->scale * 10.0 / 2.54;
- case SCALE_PC: /* 6 pica per inch */
- case SCALE_VS:
- return su->scale * 10.0 / 6.0;
- case SCALE_EN:
- case SCALE_EM:
- return su->scale;
- case SCALE_PT: /* 12 points per pica */
- return su->scale * 10.0 / 6.0 / 12.0;
- case SCALE_BU: /* 24 basic units per character */
- return su->scale / 24.0;
- case SCALE_MM: /* 1/1000 inch */
- return su->scale / 100.0;
- default:
- abort();
- }
-}
-
static void
html_tblopen(struct html *h, const struct tbl_span *sp)
{
@@ -89,7 +58,6 @@ html_tblopen(struct html *h, const struct tbl_span *sp)
if (h->tbl.cols == NULL) {
h->tbl.len = html_tbl_len;
h->tbl.slen = html_tbl_strlen;
- h->tbl.sulen = html_tbl_sulen;
tblcalc(&h->tbl, sp, 0, 0);
}
assert(NULL == h->tblt);
diff --git a/contrib/mandoc/tbl_layout.c b/contrib/mandoc/tbl_layout.c
index 3b7e64580fd5..aa054dee9411 100644
--- a/contrib/mandoc/tbl_layout.c
+++ b/contrib/mandoc/tbl_layout.c
@@ -1,4 +1,4 @@
-/* $Id: tbl_layout.c,v 1.51 2025/01/05 18:14:39 schwarze Exp $ */
+/* $Id: tbl_layout.c,v 1.52 2025/07/16 14:33:08 schwarze Exp $ */
/*
* Copyright (c) 2012, 2014, 2015, 2017, 2020, 2021, 2025
* Ingo Schwarze <schwarze@openbsd.org>
@@ -66,8 +66,8 @@ mods(struct tbl_node *tbl, struct tbl_cell *cp,
int ln, const char *p, int *pos)
{
char *endptr;
- unsigned long spacing;
- int isz;
+ unsigned long spacing; /* Column spacing in EN units. */
+ int isz; /* Width in basic units. */
enum mandoc_esc fontesc;
mod:
@@ -145,8 +145,7 @@ mod:
mandoc_msg(MANDOCERR_TBLLAYOUT_WIDTH,
ln, *pos, "%s", p + *pos);
else {
- /* Convert from BU to EN and round. */
- cp->width = (isz + 11) /24;
+ cp->width = isz;
(*pos)++;
}
} else {
@@ -155,6 +154,7 @@ mod:
cp->width *= 10;
cp->width += p[(*pos)++] - '0';
}
+ cp->width *= 24;
if (cp->width == 0)
mandoc_msg(MANDOCERR_TBLLAYOUT_WIDTH,
ln, *pos, "%s", p + *pos);
diff --git a/contrib/mandoc/tbl_term.c b/contrib/mandoc/tbl_term.c
index e92349514d9f..a7f057084266 100644
--- a/contrib/mandoc/tbl_term.c
+++ b/contrib/mandoc/tbl_term.c
@@ -1,6 +1,6 @@
-/* $Id: tbl_term.c,v 1.79 2022/08/28 10:58:31 schwarze Exp $ */
+/* $Id: tbl_term.c,v 1.81 2025/07/24 17:54:48 schwarze Exp $ */
/*
- * Copyright (c) 2011-2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2009, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -39,23 +39,23 @@
static size_t term_tbl_len(size_t, void *);
static size_t term_tbl_strlen(const char *, void *);
-static size_t term_tbl_sulen(const struct roffsu *, void *);
static void tbl_data(struct termp *, const struct tbl_opts *,
const struct tbl_cell *,
const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_direct_border(struct termp *, int, size_t);
-static void tbl_fill_border(struct termp *, int, size_t);
-static void tbl_fill_char(struct termp *, char, size_t);
-static void tbl_fill_string(struct termp *, const char *, size_t);
+static void tbl_fill_border(struct termp *, int, size_t *, size_t);
+static void tbl_fill_char(struct termp *, char, size_t *, size_t);
+static void tbl_fill_string(struct termp *, const char *,
+ size_t *, size_t);
static void tbl_hrule(struct termp *, const struct tbl_span *,
const struct tbl_span *, const struct tbl_span *,
int);
static void tbl_literal(struct termp *, const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_number(struct termp *, const struct tbl_opts *,
const struct tbl_dat *,
- const struct roffcol *);
+ const struct roffcol *, size_t *);
static void tbl_word(struct termp *, const struct tbl_dat *);
@@ -140,15 +140,6 @@ static const int *borders_locale;
static size_t
-term_tbl_sulen(const struct roffsu *su, void *arg)
-{
- int i;
-
- i = term_hen((const struct termp *)arg, su);
- return i > 0 ? i : 0;
-}
-
-static size_t
term_tbl_strlen(const char *p, void *arg)
{
return term_strlen((const struct termp *)arg, p);
@@ -166,16 +157,29 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
{
const struct tbl_cell *cp, *cpn, *cpp, *cps;
const struct tbl_dat *dp;
- static size_t offset;
- size_t save_offset;
- size_t coloff, tsz;
- int hspans, ic, more;
- int dvert, fc, horiz, lhori, rhori, uvert;
+
+ /* Positions and widths in basic units. */
+ static size_t offset; /* Of the table as a whole. */
+ size_t save_offset; /* Of the surrounding text. */
+ size_t coloff; /* Of this cell. */
+ size_t tsz; /* Total width of the table. */
+ size_t enw; /* Width of one EN unit. */
+
+ int ic; /* Column number. */
+ int hspans; /* Number of spans following this cell. */
+ int horiz; /* Boolean: this row only contains a line. */
+ int lhori; /* Number of horizontal lines pointing left. */
+ int rhori; /* Number of horizontal lines pointing right. */
+ int dvert; /* Number of vertical lines pointing down. */
+ int uvert; /* Number of vertical lines pointing up. */
+ int fc; /* Frame character index in borders_locale[]. */
+ int more; /* Boolean: there are more columns to print. */
/* Inhibit printing of spaces: we do padding ourselves. */
tp->flags |= TERMP_NOSPACE | TERMP_NONOSPACE;
save_offset = tp->tcol->offset;
+ enw = term_len(tp, 1);
/*
* The first time we're invoked for a given table block,
@@ -188,7 +192,6 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tbl.len = term_tbl_len;
tp->tbl.slen = term_tbl_strlen;
- tp->tbl.sulen = term_tbl_sulen;
tp->tbl.arg = tp;
tblcalc(&tp->tbl, sp, tp->tcol->offset, tp->tcol->rmargin);
@@ -197,17 +200,36 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
offset = tp->tcol->offset;
if (sp->opts->opts & TBL_OPT_CENTRE) {
- tsz = sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX)
- ? 2 : !!sp->opts->lvert + !!sp->opts->rvert;
+
+ /*
+ * Vertical lines on the edges of the table make the
+ * table wider; take that into account for centering.
+ * The following assignment essentially says that a
+ * line on the right side occupies two columns (which
+ * matches reality) and a line on the left side three
+ * columns (which does not match reality; in fact,
+ * it only occupies two columns). But this is how
+ * groff does centering, so for compatibility, use
+ * the same numbers as groff.
+ */
+
+ tsz = term_len(tp,
+ sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ?
+ 5 : 3 * !!sp->opts->lvert + 2 * !!sp->opts->rvert);
+
+ /* Column widths and column spacing. */
+
for (ic = 0; ic + 1 < sp->opts->cols; ic++)
tsz += tp->tbl.cols[ic].width +
- tp->tbl.cols[ic].spacing;
+ term_len(tp, tp->tbl.cols[ic].spacing);
if (sp->opts->cols)
tsz += tp->tbl.cols[sp->opts->cols - 1].width;
+
if (offset + tsz > tp->tcol->rmargin)
- tsz -= 1;
+ tsz -= enw;
offset = offset + tp->tcol->rmargin > tsz ?
- (offset + tp->tcol->rmargin - tsz) / 2 : 0;
+ ((offset + tp->tcol->rmargin - tsz) / enw / 2) *
+ enw : 0;
tp->tcol->offset = offset;
}
@@ -239,7 +261,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (sp->opts->opts & (TBL_OPT_BOX | TBL_OPT_DBOX) ||
sp->opts->lvert)
- coloff++;
+ coloff += enw * 2;
tp->tcol->rmargin = coloff;
/* Set up the data columns. */
@@ -254,7 +276,8 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
coloff += tp->tbl.cols[ic].width;
tp->tcol->rmargin = coloff;
if (ic + 1 < sp->opts->cols)
- coloff += tp->tbl.cols[ic].spacing;
+ coloff += term_len(tp,
+ tp->tbl.cols[ic].spacing);
if (hspans) {
hspans--;
continue;
@@ -269,7 +292,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
/* Set up a column for a right vertical frame. */
tp->tcol++;
- tp->tcol->offset = coloff + 1;
+ tp->tcol->offset = coloff + enw;
tp->tcol->rmargin = tp->maxrmargin;
/* Spans may have reduced the number of columns. */
@@ -279,6 +302,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
/* Fill the buffers for all data columns. */
tp->tcol = tp->tcols;
+ coloff = tp->tcols[1].offset;
cp = cpn = sp->layout->first;
dp = sp->first;
hspans = 0;
@@ -294,7 +318,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol++;
tp->col = 0;
tp->flags &= ~(TERMP_BACKAFTER | TERMP_BACKBEFORE);
- tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic);
+ tbl_data(tp, sp->opts, cp, dp, tp->tbl.cols + ic,
+ &coloff);
+ coloff += term_len(tp, tp->tbl.cols[ic].spacing);
if (dp != NULL &&
(ic || sp->layout->first->pos != TBL_CELL_SPAN)) {
hspans = dp->hspans;
@@ -328,8 +354,8 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
fc = BUP * uvert + BDOWN * dvert + BRIGHT * rhori;
if (uvert > 0 || dvert > 0 || (horiz && sp->opts->lvert)) {
(*tp->advance)(tp, tp->tcols->offset);
- tp->viscol = tp->tcol->offset;
- tbl_direct_border(tp, fc, 1);
+ tbl_direct_border(tp, fc, enw);
+ tbl_direct_border(tp, BHORIZ * rhori, enw);
}
/* Print the data cells. */
@@ -348,6 +374,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
for (ic = 0; ic < sp->opts->cols; ic++) {
/*
+ * Handle horizontal alignment.
* Figure out whether to print a
* vertical line after this cell
* and advance to next layout cell.
@@ -362,6 +389,16 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (sp->pos == TBL_SPAN_DATA)
uvert = dvert = cps->vert;
switch (cp->pos) {
+ case TBL_CELL_CENTRE:
+ tp->flags |= TERMP_CENTER;
+ break;
+ case TBL_CELL_RIGHT:
+ tp->flags |= TERMP_RIGHT;
+ break;
+ case TBL_CELL_LONG:
+ if (hspans == 0)
+ tp->tcol->offset += enw;
+ break;
case TBL_CELL_HORIZ:
fc = BHORIZ;
break;
@@ -432,6 +469,9 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tp->tcol++;
if (tp->tcol->col < tp->tcol->lastcol)
term_flushln(tp);
+ tp->flags &= ~(TERMP_CENTER | TERMP_RIGHT);
+ if (cp != NULL && cp->pos == TBL_CELL_LONG)
+ tp->tcol->offset -= enw;
if (tp->tcol->col < tp->tcol->lastcol)
more = 1;
@@ -451,15 +491,13 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
continue;
}
- if (tp->viscol < tp->tcol->rmargin) {
- (*tp->advance)(tp, tp->tcol->rmargin
- - tp->viscol);
- tp->viscol = tp->tcol->rmargin;
- }
+ if (tp->viscol < tp->tcol->rmargin)
+ (*tp->advance)(tp,
+ tp->tcol->rmargin - tp->viscol);
while (tp->viscol < tp->tcol->rmargin +
- tp->tbl.cols[ic].spacing / 2)
+ term_len(tp, tp->tbl.cols[ic].spacing / 2))
tbl_direct_border(tp,
- BHORIZ * lhori, 1);
+ BHORIZ * lhori, enw);
if (tp->tcol + 1 == tp->tcols + tp->lasttcol)
continue;
@@ -479,7 +517,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (tp->tbl.cols[ic].spacing)
tbl_direct_border(tp,
BLEFT * lhori + BRIGHT * rhori +
- BUP * uvert + BDOWN * dvert, 1);
+ BUP * uvert + BDOWN * dvert, enw);
if (tp->enc == TERMENC_UTF8)
uvert = dvert = 0;
@@ -489,7 +527,7 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
tbl_direct_border(tp,
BHORIZ * rhori +
BUP * (uvert > 1) +
- BDOWN * (dvert > 1), 1);
+ BDOWN * (dvert > 1), enw);
}
}
@@ -528,15 +566,14 @@ term_tbl(struct termp *tp, const struct tbl_span *sp)
if (horiz == 0 && (IS_HORIZ(sp->layout->last) == 0 ||
sp->layout->last->col + 1 < sp->opts->cols)) {
tp->tcol++;
- do {
+ if (tp->tcol->offset > tp->viscol)
tbl_direct_border(tp,
- BHORIZ * lhori, 1);
- } while (tp->viscol < tp->tcol->offset);
+ BHORIZ * lhori,
+ tp->tcol->offset - tp->viscol);
}
- tbl_direct_border(tp, fc, 1);
+ tbl_direct_border(tp, fc, enw);
}
(*tp->endline)(tp);
- tp->viscol = 0;
} while (more);
/*
@@ -575,6 +612,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
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. */
+ size_t enw; /* Width of one EN unit. */
int opts; /* For the table as a whole. */
int bw; /* Box line width. */
int hw; /* Horizontal line width. */
@@ -599,16 +637,19 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
/* Print the left end of the line. */
- if (tp->viscol == 0) {
+ enw = term_len(tp, 1);
+ if (tp->viscol == 0)
(*tp->advance)(tp, tp->tcols->offset);
- tp->viscol = tp->tcols->offset;
- }
- if (flags != 0)
+ if (flags != 0) {
tbl_direct_border(tp,
(spp == NULL ? 0 : BUP * bw) +
(spn == NULL ? 0 : BDOWN * bw) +
(spp == NULL || cpn == NULL ||
- cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), 1);
+ cpn->pos != TBL_CELL_DOWN ? BRIGHT * hw : 0), enw);
+ tbl_direct_border(tp,
+ (spp == NULL || cpn == NULL ||
+ cpn->pos != TBL_CELL_DOWN ? BHORIZ * hw : 0), enw);
+ }
col = tp->tbl.cols;
for (;;) {
@@ -625,7 +666,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
strcmp(dpn->string, "\\^") != 0))
? hw : 0;
tbl_direct_border(tp, BHORIZ * lw,
- col->width + col->spacing / 2);
+ col->width + term_len(tp, col->spacing / 2));
/*
* Figure out whether a vertical line is crossing
@@ -678,7 +719,7 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
if (col->spacing)
tbl_direct_border(tp, BLEFT * lw +
- BRIGHT * rw + BUP * uw + BDOWN * dw, 1);
+ BRIGHT * rw + BUP * uw + BDOWN * dw, enw);
/*
* In ASCII output, a crossing may print two characters.
@@ -688,13 +729,14 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
uw = dw = 0;
if (col->spacing > 2)
tbl_direct_border(tp,
- BHORIZ * rw + BUP * uw + BDOWN * dw, 1);
+ BHORIZ * rw + BUP * uw + BDOWN * dw, enw);
/* Padding before the start of the next column. */
if (col->spacing > 4)
tbl_direct_border(tp,
- BHORIZ * rw, (col->spacing - 3) / 2);
+ BHORIZ * rw,
+ term_len(tp, (col->spacing - 3) / 2));
}
/* Print the right end of the line. */
@@ -705,23 +747,22 @@ tbl_hrule(struct termp *tp, const struct tbl_span *spp,
(spn == NULL ? 0 : BDOWN * bw) +
(spp == NULL || spn == NULL ||
spn->layout->last->pos != TBL_CELL_DOWN ?
- BLEFT * hw : 0), 1);
+ BLEFT * hw : 0), enw);
(*tp->endline)(tp);
- tp->viscol = 0;
}
}
static void
tbl_data(struct termp *tp, const struct tbl_opts *opts,
const struct tbl_cell *cp, const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
switch (cp->pos) {
case TBL_CELL_HORIZ:
- tbl_fill_border(tp, BHORIZ, col->width);
+ tbl_fill_border(tp, BHORIZ, coloff, col->width);
return;
case TBL_CELL_DHORIZ:
- tbl_fill_border(tp, BHORIZ * 2, col->width);
+ tbl_fill_border(tp, BHORIZ * 2, coloff, col->width);
return;
default:
break;
@@ -735,11 +776,11 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
return;
case TBL_DATA_HORIZ:
case TBL_DATA_NHORIZ:
- tbl_fill_border(tp, BHORIZ, col->width);
+ tbl_fill_border(tp, BHORIZ, coloff, col->width);
return;
case TBL_DATA_NDHORIZ:
case TBL_DATA_DHORIZ:
- tbl_fill_border(tp, BHORIZ * 2, col->width);
+ tbl_fill_border(tp, BHORIZ * 2, coloff, col->width);
return;
default:
break;
@@ -750,10 +791,10 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
case TBL_CELL_CENTRE:
case TBL_CELL_LEFT:
case TBL_CELL_RIGHT:
- tbl_literal(tp, dp, col);
+ tbl_literal(tp, dp, col, coloff);
break;
case TBL_CELL_NUMBER:
- tbl_number(tp, opts, dp, col);
+ tbl_number(tp, opts, dp, col, coloff);
break;
case TBL_CELL_DOWN:
case TBL_CELL_SPAN:
@@ -763,46 +804,72 @@ tbl_data(struct termp *tp, const struct tbl_opts *opts,
}
}
+/*
+ * Print multiple copies of the string cp to advance to
+ * len basic units from the left edge of the current column.
+ */
static void
-tbl_fill_string(struct termp *tp, const char *cp, size_t len)
+tbl_fill_string(struct termp *tp, const char *cp, size_t *coloff, size_t len)
{
- size_t i, sz;
+ size_t sz; /* Width of the string cp in basic units. */
+ size_t target; /* Distance from the left margin in basic units. */
+ if (len == 0)
+ return;
sz = term_strlen(tp, cp);
- for (i = 0; i < len; i += sz)
+ target = tp->tcol->offset + len;
+ while (*coloff < target) {
term_word(tp, cp);
+ *coloff += sz;
+ }
}
+/*
+ * Print multiple copies of the ASCII character c to advance to
+ * len basic units from the left edge of the current column.
+ */
static void
-tbl_fill_char(struct termp *tp, char c, size_t len)
+tbl_fill_char(struct termp *tp, char c, size_t *coloff, size_t len)
{
char cp[2];
cp[0] = c;
cp[1] = '\0';
- tbl_fill_string(tp, cp, len);
+ tbl_fill_string(tp, cp, coloff, len);
}
+/*
+ * Print multiple copies of the border c to fill len basic units.
+ * Used for horizontal lines inside table cells.
+ */
static void
-tbl_fill_border(struct termp *tp, int c, size_t len)
+tbl_fill_border(struct termp *tp, int c, size_t *coloff, size_t len)
{
char buf[12];
if ((c = borders_locale[c]) > 127) {
(void)snprintf(buf, sizeof(buf), "\\[u%04x]", c);
- tbl_fill_string(tp, buf, len);
+ tbl_fill_string(tp, buf, coloff, len);
} else
- tbl_fill_char(tp, c, len);
+ tbl_fill_char(tp, c, coloff, len);
}
+/*
+ * The same, but bypassing term_flushln().
+ * Used for horizontal and vertical lines at the edges of table cells.
+ */
static void
tbl_direct_border(struct termp *tp, int c, size_t len)
{
- size_t i, sz;
+ size_t sz; /* Width of the character in basic units. */
+ size_t enw2; /* Width of half an EN in basic units. */
+ size_t target; /* Distance from the left margin in basic units. */
c = borders_locale[c];
- sz = (*tp->width)(tp, c);
- for (i = 0; i < len; i += sz) {
+ sz = (*tp->getwidth)(tp, c);
+ enw2 = (*tp->getwidth)(tp, ' ') / 2;
+ target = tp->viscol + len;
+ while (tp->viscol + enw2 < target) {
(*tp->letter)(tp, c);
tp->viscol += sz;
}
@@ -810,56 +877,36 @@ tbl_direct_border(struct termp *tp, int c, size_t len)
static void
tbl_literal(struct termp *tp, const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
- size_t len, padl, padr, width;
- int ic, hspans;
+ size_t width; /* Of the cell including following spans [BU]. */
+ int ic; /* Column number of the cell. */
+ int hspans; /* Number of horizontal spans that follow. */
- assert(dp->string);
- len = term_strlen(tp, dp->string);
width = col->width;
ic = dp->layout->col;
hspans = dp->hspans;
while (hspans--) {
- width += tp->tbl.cols[ic].spacing;
+ width += term_len(tp, tp->tbl.cols[ic].spacing);
ic++;
width += tp->tbl.cols[ic].width;
}
-
- padr = width > len ? width - len : 0;
- padl = 0;
-
- switch (dp->layout->pos) {
- case TBL_CELL_LONG:
- padl = term_len(tp, 1);
- padr = padr > padl ? padr - padl : 0;
- break;
- case TBL_CELL_CENTRE:
- if (2 > padr)
- break;
- padl = padr / 2;
- padr -= padl;
- break;
- case TBL_CELL_RIGHT:
- padl = padr;
- padr = 0;
- break;
- default:
- break;
- }
-
- tbl_fill_char(tp, ASCII_NBRSP, padl);
tbl_word(tp, dp);
- tbl_fill_char(tp, ASCII_NBRSP, padr);
+ *coloff += width;
}
static void
tbl_number(struct termp *tp, const struct tbl_opts *opts,
const struct tbl_dat *dp,
- const struct roffcol *col)
+ const struct roffcol *col, size_t *coloff)
{
const char *cp, *lastdigit, *lastpoint;
- size_t intsz, padl, totsz;
+
+ /* Widths in basic units. */
+ size_t pad; /* Padding before the number. */
+ size_t totsz; /* Of the number to be printed. */
+ size_t intsz; /* Of the integer part. */
+
char buf[2];
/*
@@ -883,7 +930,7 @@ tbl_number(struct termp *tp, const struct tbl_opts *opts,
/* Then measure both widths. */
- padl = 0;
+ pad = 0;
totsz = term_strlen(tp, dp->string);
if (lastdigit != NULL) {
if (lastpoint == NULL)
@@ -901,23 +948,19 @@ tbl_number(struct termp *tp, const struct tbl_opts *opts,
*/
if (col->decimal > intsz && col->width > totsz) {
- padl = col->decimal - intsz;
- if (padl + totsz > col->width)
- padl = col->width - totsz;
+ pad = col->decimal - intsz;
+ if (pad + totsz > col->width)
+ pad = col->width - totsz;
}
/* If it is not a number, simply center the string. */
} else if (col->width > totsz)
- padl = (col->width - totsz) / 2;
+ pad = (col->width - totsz) / 2;
- tbl_fill_char(tp, ASCII_NBRSP, padl);
+ tbl_fill_char(tp, ASCII_NBRSP, coloff, pad);
tbl_word(tp, dp);
-
- /* Pad right to fill the column. */
-
- if (col->width > padl + totsz)
- tbl_fill_char(tp, ASCII_NBRSP, col->width - padl - totsz);
+ *coloff += col->width;
}
static void
diff --git a/contrib/mandoc/term.c b/contrib/mandoc/term.c
index 58d9d9bf9240..4dde60d1e45c 100644
--- a/contrib/mandoc/term.c
+++ b/contrib/mandoc/term.c
@@ -1,6 +1,6 @@
-/* $Id: term.c,v 1.291 2023/04/28 19:11:04 schwarze Exp $ */
+/* $Id: term.c,v 1.294 2025/08/01 14:59:39 schwarze Exp $ */
/*
- * Copyright (c) 2010-2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2010-2022, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -94,12 +94,15 @@ term_end(struct termp *p)
void
term_flushln(struct termp *p)
{
- size_t vbl; /* Number of blanks to prepend to the output. */
+ /* Widths in basic units. */
+ size_t vbl; /* Whitespace to prepend to the output. */
size_t vbr; /* Actual visual position of the end of field. */
size_t vfield; /* Desired visual field width. */
size_t vtarget; /* Desired visual position of the right margin. */
- size_t ic; /* Character position in the input buffer. */
- size_t nbr; /* Number of characters to print in this field. */
+
+ /* Bytes. */
+ size_t ic; /* Byte index in the input buffer. */
+ size_t nbr; /* Number of bytes to print in this field. */
/*
* Normally, start writing at the left margin, but with the
@@ -108,8 +111,8 @@ term_flushln(struct termp *p)
vbl = (p->flags & TERMP_NOPAD) || p->tcol->offset < p->viscol ?
0 : p->tcol->offset - p->viscol;
- if (p->minbl && vbl < p->minbl)
- vbl = p->minbl;
+ if (p->minbl > 0 && vbl < term_len(p, p->minbl))
+ vbl = term_len(p, p->minbl);
if ((p->flags & TERMP_MULTICOL) == 0)
p->tcol->col = 0;
@@ -137,7 +140,7 @@ term_flushln(struct termp *p)
*/
term_fill(p, &nbr, &vbr,
- p->flags & TERMP_BRNEVER ? SIZE_MAX : vtarget);
+ p->flags & TERMP_BRNEVER ? SIZE_MAX / 2 : vtarget);
if (nbr == 0)
break;
@@ -161,7 +164,7 @@ term_flushln(struct termp *p)
p->tcol->taboff += vbr;
else
p->tcol->taboff += vtarget;
- p->tcol->taboff += (*p->width)(p, ' ');
+ p->tcol->taboff += term_len(p, 1);
/*
* If there is no text left in the field, exit the loop.
@@ -178,7 +181,7 @@ term_flushln(struct termp *p)
continue;
case ' ':
if (p->flags & TERMP_BRTRSP)
- vbr += (*p->width)(p, ' ');
+ vbr += term_len(p, 1);
continue;
case '\n':
case ASCII_NBRZW:
@@ -245,13 +248,13 @@ term_flushln(struct termp *p)
if ((p->flags & TERMP_HANG) == 0 &&
((p->flags & TERMP_NOBREAK) == 0 ||
- vbr + term_len(p, p->trailspace) > vfield))
+ vbr + term_len(p, p->trailspace) > vfield + term_len(p, 1) / 2))
endline(p);
}
/*
- * Store the number of input characters to print in this field in *nbr
- * and their total visual width to print in *vbr.
+ * Store the number of input bytes to print in this field in *nbr
+ * and their total visual width in basic units in *vbr.
* If there is only whitespace in the field, both remain zero.
* The desired visual width of the field is provided by vtarget.
* If the first word is longer, the field will be overrun.
@@ -259,28 +262,33 @@ term_flushln(struct termp *p)
static void
term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
{
- size_t ic; /* Character position in the input buffer. */
+ /* Widths in basic units. */
size_t vis; /* Visual position of the current character. */
size_t vn; /* Visual position of the next character. */
+ size_t enw; /* Width of an EN unit. */
+ int taboff; /* Temporary offset for literal tabs. */
+
+ size_t ic; /* Byte index in the input buffer. */
int breakline; /* Break at the end of this word. */
int graph; /* Last character was non-blank. */
- int taboff; /* Temporary offset for literal tabs. */
*nbr = *vbr = vis = 0;
breakline = graph = 0;
taboff = p->tcol->taboff;
+ enw = (*p->getwidth)(p, ' ');
+ vtarget += enw / 2;
for (ic = p->tcol->col; ic < p->tcol->lastcol; ic++) {
switch (p->tcol->buf[ic]) {
case '\b': /* Escape \o (overstrike) or backspace markup. */
assert(ic > 0);
- vis -= (*p->width)(p, p->tcol->buf[ic - 1]);
+ vis -= (*p->getwidth)(p, p->tcol->buf[ic - 1]);
continue;
case ' ':
case ASCII_BREAK: /* Escape \: (breakpoint). */
vn = vis;
if (p->tcol->buf[ic] == ' ')
- vn += (*p->width)(p, ' ');
+ vn += enw;
/* Can break at the end of a word. */
if (breakline || vn > vtarget)
break;
@@ -305,7 +313,7 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
* hyphen such that we get the correct width.
*/
p->tcol->buf[ic] = '-';
- vis += (*p->width)(p, '-');
+ vis += (*p->getwidth)(p, '-');
if (vis > vtarget) {
ic++;
break;
@@ -315,7 +323,7 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
continue;
case ASCII_TABREF:
- taboff = -vis - (*p->width)(p, ' ');
+ taboff = -vis - enw;
continue;
default:
@@ -334,7 +342,7 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
p->tcol->buf[ic] = ' ';
/* FALLTHROUGH */
default: /* Printable character. */
- vis += (*p->width)(p, p->tcol->buf[ic]);
+ vis += (*p->getwidth)(p, p->tcol->buf[ic]);
break;
}
graph = 1;
@@ -359,18 +367,20 @@ term_fill(struct termp *p, size_t *nbr, size_t *vbr, size_t vtarget)
/*
* Print the contents of one field
- * with an indentation of vbl visual columns,
- * and an input string length of nbr characters.
+ * with an indentation of vbl basic units
+ * and an input string length of nbr bytes.
*/
static void
term_field(struct termp *p, size_t vbl, size_t nbr)
{
- size_t ic; /* Character position in the input buffer. */
+ /* Widths in basic units. */
size_t vis; /* Visual position of the current character. */
size_t vt; /* Visual position including tab offset. */
size_t dv; /* Visual width of the current character. */
int taboff; /* Temporary offset for literal tabs. */
+ size_t ic; /* Byte position in the input buffer. */
+
vis = 0;
taboff = p->tcol->taboff;
for (ic = p->tcol->col; ic < nbr; ic++) {
@@ -386,7 +396,7 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
case ASCII_NBRZW:
continue;
case ASCII_TABREF:
- taboff = -vis - (*p->width)(p, ' ');
+ taboff = -vis - (*p->getwidth)(p, ' ');
continue;
case '\t':
case ' ':
@@ -398,7 +408,7 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
vt = vis + taboff;
dv = term_tab_next(vt) - vt;
} else
- dv = (*p->width)(p, ' ');
+ dv = (*p->getwidth)(p, ' ');
vbl += dv;
vis += dv;
continue;
@@ -413,7 +423,6 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
if (vbl > 0) {
(*p->advance)(p, vbl);
- p->viscol += vbl;
vbl = 0;
}
@@ -421,11 +430,11 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
(*p->letter)(p, p->tcol->buf[ic]);
if (p->tcol->buf[ic] == '\b') {
- dv = (*p->width)(p, p->tcol->buf[ic - 1]);
+ dv = (*p->getwidth)(p, p->tcol->buf[ic - 1]);
p->viscol -= dv;
vis -= dv;
} else {
- dv = (*p->width)(p, p->tcol->buf[ic]);
+ dv = (*p->getwidth)(p, p->tcol->buf[ic]);
p->viscol += dv;
vis += dv;
}
@@ -433,6 +442,10 @@ term_field(struct termp *p, size_t vbl, size_t nbr)
p->tcol->col = nbr;
}
+/*
+ * Print the margin character, if one is configured,
+ * and end the output line.
+ */
static void
endline(struct termp *p)
{
@@ -441,14 +454,13 @@ endline(struct termp *p)
p->flags &= ~TERMP_ENDMC;
}
if (p->mc != NULL) {
- if (p->viscol && p->maxrmargin >= p->viscol)
- (*p->advance)(p, p->maxrmargin - p->viscol + 1);
+ if (p->viscol > 0 && p->viscol <= p->maxrmargin)
+ (*p->advance)(p,
+ p->maxrmargin - p->viscol + term_len(p, 1));
p->flags |= TERMP_NOBUF | TERMP_NOSPACE;
term_word(p, p->mc);
p->flags &= ~(TERMP_NOBUF | TERMP_NEWMC);
}
- p->viscol = 0;
- p->minbl = 0;
(*p->endline)(p);
}
@@ -477,8 +489,6 @@ term_vspace(struct termp *p)
{
term_newln(p);
- p->viscol = 0;
- p->minbl = 0;
if (0 < p->skipvsp)
p->skipvsp--;
else
@@ -496,34 +506,36 @@ term_fontlast(struct termp *p)
p->fontq[p->fonti] = f;
}
-/* Set font, save current, discard previous; for \f, .ft, .B etc. */
+/* Set font, save current, discard previous; for \f, .ft, and man(7). */
void
term_fontrepl(struct termp *p, enum termfont f)
{
-
p->fontl = p->fontq[p->fonti];
+ if (p->fontibi && f == TERMFONT_UNDER)
+ f = TERMFONT_BI;
p->fontq[p->fonti] = f;
}
-/* Set font, save previous. */
+/* Set font, save previous; for mdoc(7), eqn(7), and tbl(7). */
void
term_fontpush(struct termp *p, enum termfont f)
{
+ enum termfont fl;
- p->fontl = p->fontq[p->fonti];
+ fl = p->fontq[p->fonti];
if (++p->fonti == p->fontsz) {
p->fontsz += 8;
p->fontq = mandoc_reallocarray(p->fontq,
p->fontsz, sizeof(*p->fontq));
}
- p->fontq[p->fonti] = f;
+ p->fontq[p->fonti] = fl;
+ term_fontrepl(p, f);
}
/* Flush to make the saved pointer current again. */
void
term_fontpopq(struct termp *p, int i)
{
-
assert(i >= 0);
if (p->fonti > i)
p->fonti = i;
@@ -533,8 +545,7 @@ term_fontpopq(struct termp *p, int i)
void
term_fontpop(struct termp *p)
{
-
- assert(p->fonti);
+ assert(p->fonti > 0);
p->fonti--;
}
@@ -548,9 +559,14 @@ term_word(struct termp *p, const char *word)
{
struct roffsu su;
const char nbrsp[2] = { ASCII_NBRSP, 0 };
- const char *seq, *cp;
- int sz, uc;
- size_t csz, lsz, ssz;
+ const char *seq; /* Escape sequence argument. */
+ const char *cp; /* String to be printed. */
+ size_t csz; /* String length in basic units. */
+ size_t lsz; /* Line width in basic units. */
+ size_t ssz; /* Substring length in bytes. */
+ int sz; /* Argument length in bytes. */
+ int uc; /* Unicode codepoint number. */
+ int bu; /* Width in basic units. */
enum mandoc_esc esc;
if ((p->flags & TERMP_NOBUF) == 0) {
@@ -663,15 +679,15 @@ term_word(struct termp *p, const char *word)
}
if (*seq == '|') {
seq++;
- uc = -p->col;
+ bu = -term_len(p, p->col);
} else
- uc = 0;
+ bu = 0;
if (a2roffsu(seq, &su, SCALE_EM) == NULL)
continue;
- uc += term_hen(p, &su);
- if (uc >= 0) {
- while (uc > 0) {
- uc -= term_len(p, 1);
+ bu += term_hspan(p, &su);
+ if (bu >= 0) {
+ while (bu > 0) {
+ bu -= term_len(p, 1);
if (p->flags & TERMP_BACKBEFORE)
p->flags &= ~TERMP_BACKBEFORE;
else
@@ -681,17 +697,17 @@ term_word(struct termp *p, const char *word)
}
if (p->flags & TERMP_BACKBEFORE) {
p->flags &= ~TERMP_BACKBEFORE;
- assert(p->col > 0);
+ assert(p->col > 1);
p->col--;
}
- if (p->col >= (size_t)(-uc)) {
- p->col += uc;
+ if (term_len(p, p->col) >= (size_t)(-bu)) {
+ p->col -= -bu / term_len(p, 1);
} else {
- uc += p->col;
+ bu += term_len(p, p->col);
p->col = 0;
- if (p->tcol->offset > (size_t)(-uc)) {
- p->ti += uc;
- p->tcol->offset += uc;
+ if (p->tcol->offset > (size_t)(-bu)) {
+ p->ti += bu;
+ p->tcol->offset += bu;
} else {
p->ti -= p->tcol->offset;
p->tcol->offset = 0;
@@ -701,13 +717,13 @@ term_word(struct termp *p, const char *word)
case ESCAPE_HLINE:
if ((cp = a2roffsu(seq, &su, SCALE_EM)) == NULL)
continue;
- uc = term_hen(p, &su);
- if (uc <= 0) {
+ bu = term_hspan(p, &su);
+ if (bu <= 0) {
if (p->tcol->rmargin <= p->tcol->offset)
continue;
lsz = p->tcol->rmargin - p->tcol->offset;
} else
- lsz = uc;
+ lsz = bu;
if (*cp == seq[-1])
uc = -1;
else if (*cp == '\\') {
@@ -739,13 +755,16 @@ term_word(struct termp *p, const char *word)
csz = term_strlen(p, cp);
ssz = strlen(cp);
} else
- csz = (*p->width)(p, uc);
- while (lsz >= csz) {
+ csz = (*p->getwidth)(p, uc);
+ while (lsz > 0) {
if (p->enc == TERMENC_ASCII)
encode(p, cp, ssz);
else
encode1(p, uc);
- lsz -= csz;
+ if (lsz > csz)
+ lsz -= csz;
+ else
+ lsz = 0;
}
continue;
case ESCAPE_SKIPCHAR:
@@ -954,28 +973,39 @@ term_setwidth(struct termp *p, const char *wstr)
size_t
term_len(const struct termp *p, size_t sz)
{
-
- return (*p->width)(p, ' ') * sz;
+ return (*p->getwidth)(p, ' ') * sz;
}
static size_t
cond_width(const struct termp *p, int c, int *skip)
{
-
if (*skip) {
(*skip) = 0;
return 0;
} else
- return (*p->width)(p, c);
+ return (*p->getwidth)(p, c);
}
size_t
term_strlen(const struct termp *p, const char *cp)
{
- size_t sz, rsz, i;
- int ssz, skip, uc;
- const char *seq, *rhs;
+ const char *seq; /* Escape sequence argument. */
+ const char *rhs; /* String to be printed. */
+
+ /* Widths in basic units. */
+ size_t sz; /* Return value. */
+ size_t this_sz; /* Individual char for overstrike. */
+ size_t max_sz; /* Result of overstrike. */
+
+ /* Numbers of bytes. */
+ size_t rsz; /* Substring length in bytes. */
+ size_t i; /* Byte index in substring. */
+ int ssz; /* Argument length in bytes. */
+ int skip; /* Number of bytes to skip. */
+
+ int uc; /* Unicode codepoint number. */
enum mandoc_esc esc;
+
static const char rej[] = { '\\', ASCII_NBRSP, ASCII_NBRZW,
ASCII_BREAK, ASCII_HYPH, ASCII_TABREF, '\0' };
@@ -1039,18 +1069,18 @@ term_strlen(const struct termp *p, const char *cp)
skip = 1;
continue;
case ESCAPE_OVERSTRIKE:
- rsz = 0;
+ max_sz = 0;
rhs = seq + ssz;
while (seq < rhs) {
if (*seq == '\\') {
mandoc_escape(&seq, NULL, NULL);
continue;
}
- i = (*p->width)(p, *seq++);
- if (rsz < i)
- rsz = i;
+ this_sz = (*p->getwidth)(p, *seq++);
+ if (max_sz < this_sz)
+ max_sz = this_sz;
}
- sz += rsz;
+ sz += max_sz;
continue;
default:
continue;
@@ -1085,7 +1115,7 @@ term_strlen(const struct termp *p, const char *cp)
*/
for (i = 0; i < rsz; i++)
- sz += (*p->width)(p, *rhs++);
+ sz += (*p->getwidth)(p, *rhs++);
break;
case ASCII_NBRSP:
sz += cond_width(p, ' ', &skip);
@@ -1146,25 +1176,10 @@ term_vspan(const struct termp *p, const struct roffsu *su)
}
/*
- * Convert a scaling width to basic units, rounding towards 0.
+ * Convert a scaling width to basic units.
*/
int
term_hspan(const struct termp *p, const struct roffsu *su)
{
-
return (*p->hspan)(p, su);
}
-
-/*
- * Convert a scaling width to basic units, rounding to closest.
- */
-int
-term_hen(const struct termp *p, const struct roffsu *su)
-{
- int bu;
-
- if ((bu = (*p->hspan)(p, su)) >= 0)
- return (bu + 11) / 24;
- else
- return -((-bu + 11) / 24);
-}
diff --git a/contrib/mandoc/term.h b/contrib/mandoc/term.h
index 3b3a79527eeb..1e4659734fc5 100644
--- a/contrib/mandoc/term.h
+++ b/contrib/mandoc/term.h
@@ -1,6 +1,7 @@
-/* $Id: term.h,v 1.134 2022/08/16 17:45:55 schwarze Exp $ */
+/* $Id: term.h,v 1.138 2025/07/27 15:27:28 schwarze Exp $ */
/*
- * Copyright (c) 2011-2015,2017,2019,2022 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2011-2015, 2017, 2019, 2021, 2022, 2025
+ * Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -44,19 +45,14 @@ struct termp;
typedef void (*term_margin)(struct termp *, const struct roff_meta *);
-struct termp_tbl {
- int width; /* width in fixed chars */
- int decimal; /* decimal point position */
-};
-
struct termp_col {
int *buf; /* Output buffer. */
size_t maxcols; /* Allocated bytes in buf. */
size_t lastcol; /* Last byte in buf. */
size_t col; /* Byte in buf to be written. */
- size_t rmargin; /* Current right margin. */
- size_t offset; /* Current left margin. */
- size_t taboff; /* Offset for literal tabs. */
+ size_t rmargin; /* Current right margin [BU]. */
+ size_t offset; /* Current left margin [BU]. */
+ size_t taboff; /* Offset for literal tabs [BU]. */
};
struct termp {
@@ -66,17 +62,16 @@ struct termp {
size_t maxtcol; /* Allocated table columns. */
size_t lasttcol; /* Last column currently used. */
size_t line; /* Current output line number. */
- size_t defindent; /* Default indent for text. */
- size_t defrmargin; /* Right margin of the device. */
- size_t lastrmargin; /* Right margin before the last ll. */
- size_t maxrmargin; /* Max right margin. */
+ size_t defindent; /* Default indent for text [EN]. */
+ size_t defrmargin; /* Right margin of the device [BU]. */
+ size_t lastrmargin; /* Right margin before last ll [BU]. */
+ size_t maxrmargin; /* Maximum right margin [BU]. */
size_t col; /* Byte position in buf. */
- size_t viscol; /* Chars on current line. */
- size_t trailspace; /* See term_flushln(). */
- size_t minbl; /* Minimum blanks before next field. */
+ size_t viscol; /* Width of the current line [BU]. */
+ size_t trailspace; /* Whitespace after field [EN]. */
+ size_t minbl; /* Whitespace before field [EN]. */
int synopsisonly; /* Print the synopsis only. */
- int mdocstyle; /* Imitate mdoc(7) output. */
- int ti; /* Temporary indent for one line. */
+ int ti; /* Temporary indent for line [BU]. */
int skipvsp; /* Vertical space to skip. */
int flags;
#define TERMP_SENTENCE (1 << 0) /* Space before a sentence. */
@@ -108,6 +103,7 @@ struct termp {
enum termfont *fontq; /* Symmetric fonts. */
int fontsz; /* Allocated size of font stack */
int fonti; /* Index of font stack. */
+ int fontibi; /* Map font I to BI. */
term_margin headf; /* invoked to print head */
term_margin footf; /* invoked to print foot */
void (*letter)(struct termp *, int);
@@ -116,7 +112,7 @@ struct termp {
void (*endline)(struct termp *);
void (*advance)(struct termp *, size_t);
void (*setwidth)(struct termp *, int, int);
- size_t (*width)(const struct termp *, int);
+ size_t (*getwidth)(const struct termp *, int);
int (*hspan)(const struct termp *,
const struct roffsu *);
const void *argf; /* arg for headf/footf */
@@ -143,13 +139,11 @@ void term_end(struct termp *);
void term_setwidth(struct termp *, const char *);
int term_hspan(const struct termp *, const struct roffsu *);
-int term_hen(const struct termp *, const struct roffsu *);
int term_vspan(const struct termp *, const struct roffsu *);
size_t term_strlen(const struct termp *, const char *);
size_t term_len(const struct termp *, size_t);
void term_tab_set(const struct termp *, const char *);
-void term_tab_iset(size_t);
void term_tab_ref(struct termp *);
size_t term_tab_next(size_t);
void term_tab_free(void);
diff --git a/contrib/mandoc/term_ascii.c b/contrib/mandoc/term_ascii.c
index 3942dc757953..990833c8a021 100644
--- a/contrib/mandoc/term_ascii.c
+++ b/contrib/mandoc/term_ascii.c
@@ -1,7 +1,7 @@
-/* $Id: term_ascii.c,v 1.69 2023/11/13 19:13:01 schwarze Exp $ */
+/* $Id: term_ascii.c,v 1.71 2025/07/16 14:33:08 schwarze Exp $ */
/*
+ * Copyright (c) 2014,2015,2017-2020,2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * 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
@@ -43,7 +43,7 @@
static struct termp *ascii_init(enum termenc, const struct manoutput *);
static int ascii_hspan(const struct termp *,
const struct roffsu *);
-static size_t ascii_width(const struct termp *, int);
+static size_t ascii_getwidth(const struct termp *, int);
static void ascii_advance(struct termp *, size_t);
static void ascii_begin(struct termp *);
static void ascii_end(struct termp *);
@@ -55,7 +55,7 @@ static void ascii_setwidth(struct termp *, int, int);
static void locale_advance(struct termp *, size_t);
static void locale_endline(struct termp *);
static void locale_letter(struct termp *, int);
-static size_t locale_width(const struct termp *, int);
+static size_t locale_getwidth(const struct termp *, int);
#endif
@@ -73,7 +73,6 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
p->line = 1;
p->defindent = 5;
- p->defrmargin = p->lastrmargin = 78;
p->fontq = mandoc_reallocarray(NULL,
(p->fontsz = 8), sizeof(*p->fontq));
p->fontq[0] = p->fontl = TERMFONT_NONE;
@@ -82,13 +81,12 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
p->end = ascii_end;
p->hspan = ascii_hspan;
p->type = TERMTYPE_CHAR;
-
p->enc = TERMENC_ASCII;
p->advance = ascii_advance;
p->endline = ascii_endline;
p->letter = ascii_letter;
p->setwidth = ascii_setwidth;
- p->width = ascii_width;
+ p->getwidth = ascii_getwidth;
#if HAVE_WCHAR
if (enc != TERMENC_ASCII) {
@@ -118,17 +116,15 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
p->advance = locale_advance;
p->endline = locale_endline;
p->letter = locale_letter;
- p->width = locale_width;
+ p->getwidth = locale_getwidth;
}
}
#endif
+ p->defrmargin = term_len(p, outopts->width ? outopts->width : 78);
+ p->lastrmargin = p->defrmargin;
- if (outopts->mdoc)
- p->mdocstyle = 1;
if (outopts->indent)
p->defindent = outopts->indent;
- if (outopts->width)
- p->defrmargin = outopts->width;
if (outopts->synopsisonly)
p->synopsisonly = 1;
@@ -140,29 +136,24 @@ ascii_init(enum termenc enc, const struct manoutput *outopts)
void *
ascii_alloc(const struct manoutput *outopts)
{
-
return ascii_init(TERMENC_ASCII, outopts);
}
void *
utf8_alloc(const struct manoutput *outopts)
{
-
return ascii_init(TERMENC_UTF8, outopts);
}
void *
locale_alloc(const struct manoutput *outopts)
{
-
return ascii_init(TERMENC_LOCALE, outopts);
}
static void
ascii_setwidth(struct termp *p, int iop, int width)
{
-
- width /= 24;
p->tcol->rmargin = p->defrmargin;
if (iop > 0)
p->defrmargin += width;
@@ -172,8 +163,8 @@ ascii_setwidth(struct termp *p, int iop, int width)
p->defrmargin -= width;
else
p->defrmargin = 0;
- if (p->defrmargin > 1000)
- p->defrmargin = 1000;
+ if (p->defrmargin > term_len(p, 1000))
+ p->defrmargin = term_len(p, 1000);
p->lastrmargin = p->tcol->rmargin;
p->tcol->rmargin = p->maxrmargin = p->defrmargin;
}
@@ -182,67 +173,76 @@ void
terminal_sepline(void *arg)
{
struct termp *p;
- size_t i;
+ size_t i; /* Printed width in basic units. */
+ size_t sz; /* Width of a dash in basic units. */
p = (struct termp *)arg;
(*p->endline)(p);
- for (i = 0; i < p->defrmargin; i++)
+ sz = (*p->getwidth)(p, '-');
+ for (i = 0; i < p->defrmargin; i += sz)
(*p->letter)(p, '-');
(*p->endline)(p);
(*p->endline)(p);
}
static size_t
-ascii_width(const struct termp *p, int c)
+ascii_getwidth(const struct termp *p, int c)
{
- return c != ASCII_BREAK && c != ASCII_NBRZW && c != ASCII_TABREF;
+ switch (c) {
+ case ASCII_BREAK:
+ case ASCII_NBRZW:
+ case ASCII_TABREF:
+ return 0;
+ default:
+ return 24;
+ }
}
void
ascii_free(void *arg)
{
-
term_free((struct termp *)arg);
}
static void
ascii_letter(struct termp *p, int c)
{
-
putchar(c);
}
static void
ascii_begin(struct termp *p)
{
-
(*p->headf)(p, p->argf);
}
static void
ascii_end(struct termp *p)
{
-
(*p->footf)(p, p->argf);
}
static void
ascii_endline(struct termp *p)
{
-
p->line++;
if ((int)p->tcol->offset > p->ti)
p->tcol->offset -= p->ti;
else
p->tcol->offset = 0;
p->ti = 0;
+ p->minbl = 0;
+ p->viscol = 0;
putchar('\n');
}
static void
ascii_advance(struct termp *p, size_t len)
{
- size_t i;
+ size_t dst; /* Destination column in basic units. */
+ size_t sz; /* Width of a space in basic units. */
+
+ sz = (*p->getwidth)(p, ' ');
/*
* XXX We used to have "assert(len < UINT16_MAX)" here.
@@ -250,10 +250,14 @@ ascii_advance(struct termp *p, size_t len)
* can trigger that by merely providing large input.
* For now, simply truncate.
*/
- if (len > 256)
- len = 256;
- for (i = 0; i < len; i++)
+ if (len > 256 * sz)
+ len = 256 * sz;
+
+ dst = p->viscol + len;
+ while (p->viscol + sz / 2 < dst) {
putchar(' ');
+ p->viscol += sz;
+ }
}
static int
@@ -372,7 +376,7 @@ ascii_uc2str(int uc)
#if HAVE_WCHAR
static size_t
-locale_width(const struct termp *p, int c)
+locale_getwidth(const struct termp *p, int c)
{
int rc;
@@ -381,13 +385,16 @@ locale_width(const struct termp *p, int c)
rc = wcwidth(c);
if (rc < 0)
rc = 0;
- return rc;
+ return rc * 24;
}
static void
locale_advance(struct termp *p, size_t len)
{
- size_t i;
+ size_t dst; /* Destination column in basic units. */
+ size_t sz; /* Width of a space in basic units. */
+
+ sz = (*p->getwidth)(p, ' ');
/*
* XXX We used to have "assert(len < UINT16_MAX)" here.
@@ -395,29 +402,33 @@ locale_advance(struct termp *p, size_t len)
* can trigger that by merely providing large input.
* For now, simply truncate.
*/
- if (len > 256)
- len = 256;
- for (i = 0; i < len; i++)
+ if (len > 256 * sz)
+ len = 256 * sz;
+
+ dst = p->viscol + len;
+ while (p->viscol + sz / 2 < dst) {
putwchar(L' ');
+ p->viscol += sz;
+ }
}
static void
locale_endline(struct termp *p)
{
-
p->line++;
if ((int)p->tcol->offset > p->ti)
p->tcol->offset -= p->ti;
else
p->tcol->offset = 0;
p->ti = 0;
+ p->minbl = 0;
+ p->viscol = 0;
putwchar(L'\n');
}
static void
locale_letter(struct termp *p, int c)
{
-
putwchar(c);
}
#endif
diff --git a/contrib/mandoc/term_ps.c b/contrib/mandoc/term_ps.c
index 374d3d9a6abd..4c6368ca1d1f 100644
--- a/contrib/mandoc/term_ps.c
+++ b/contrib/mandoc/term_ps.c
@@ -1,7 +1,7 @@
-/* $Id: term_ps.c,v 1.92 2020/09/06 14:45:22 schwarze Exp $ */
+/* $Id: term_ps.c,v 1.94 2025/07/18 15:47:18 schwarze Exp $ */
/*
+ * Copyright (c) 2014-2017, 2020, 2025 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
- * Copyright (c) 2014,2015,2016,2017,2020 Ingo Schwarze <schwarze@openbsd.org>
* Copyright (c) 2017 Marc Espie <espie@openbsd.org>
*
* Permission to use, copy, modify, and distribute this software for any
@@ -95,7 +95,7 @@ struct termp_ps {
static int ps_hspan(const struct termp *,
const struct roffsu *);
-static size_t ps_width(const struct termp *, int);
+static size_t ps_getwidth(const struct termp *, int);
static void ps_advance(struct termp *, size_t);
static void ps_begin(struct termp *);
static void ps_closepage(struct termp *);
@@ -549,7 +549,7 @@ pspdf_alloc(const struct manoutput *outopts, enum termtype type)
p->hspan = ps_hspan;
p->letter = ps_letter;
p->setwidth = ps_setwidth;
- p->width = ps_width;
+ p->getwidth = ps_getwidth;
/* Default to US letter (millimetres). */
@@ -1211,6 +1211,7 @@ ps_advance(struct termp *p, size_t len)
ps_plast(p);
ps_pclose(p);
p->ps->pscol += len;
+ p->viscol += len;
}
static void
@@ -1234,6 +1235,8 @@ ps_endline(struct termp *p)
/* Left-justify. */
p->ps->pscol = p->ps->left;
+ p->viscol = 0;
+ p->minbl = 0;
/* If we haven't printed anything, return. */
@@ -1282,7 +1285,7 @@ ps_setfont(struct termp *p, enum termfont f)
}
static size_t
-ps_width(const struct termp *p, int c)
+ps_getwidth(const struct termp *p, int c)
{
if (c <= 32 || c - 32 >= MAXCHAR)
@@ -1311,7 +1314,7 @@ ps_hspan(const struct termp *p, const struct roffsu *su)
* scaling unit so that output is the same regardless
* the media.
*/
- r = PNT2AFM(p, su->scale * 72.0 / 240.0);
+ r = PNT2AFM(p, su->scale * 72.0 / 10.0);
break;
case SCALE_CM:
r = PNT2AFM(p, su->scale * 72.0 / 2.54);
@@ -1344,8 +1347,7 @@ ps_hspan(const struct termp *p, const struct roffsu *su)
r = su->scale;
break;
}
-
- return r * 24.0;
+ return r;
}
static void
diff --git a/contrib/mandoc/term_tab.c b/contrib/mandoc/term_tab.c
index a2d1074159b9..dd1b6bcdc696 100644
--- a/contrib/mandoc/term_tab.c
+++ b/contrib/mandoc/term_tab.c
@@ -1,6 +1,6 @@
-/* $Id: term_tab.c,v 1.7 2021/10/04 18:56:31 schwarze Exp $ */
+/* $Id: term_tab.c,v 1.9 2025/07/16 14:33:08 schwarze Exp $ */
/*
- * Copyright (c) 2017, 2021 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2017, 2021, 2025 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,7 +27,7 @@
#include "term.h"
struct tablist {
- size_t *t; /* Allocated array of tab positions. */
+ size_t *t; /* Allocated array of tab positions [BU]. */
size_t s; /* Allocated number of positions. */
size_t n; /* Currently used number of positions. */
};
@@ -36,7 +36,7 @@ static struct {
struct tablist a; /* All tab positions for lookup. */
struct tablist p; /* Periodic tab positions to add. */
struct tablist *r; /* Tablist currently being recorded. */
- size_t d; /* Default tab width in units of n. */
+ size_t d; /* Default tab width in basic units. */
} tabs;
@@ -55,7 +55,7 @@ term_tab_set(const struct termp *p, const char *arg)
tabs.r = &tabs.a;
if (tabs.d == 0) {
a2roffsu(".8i", &su, SCALE_IN);
- tabs.d = term_hen(p, &su);
+ tabs.d = term_hspan(p, &su);
}
return;
}
@@ -84,28 +84,13 @@ term_tab_set(const struct termp *p, const char *arg)
/* Append the new position. */
- pos = term_hen(p, &su);
+ pos = term_hspan(p, &su);
tl->t[tl->n] = pos;
if (add && tl->n)
tl->t[tl->n] += tl->t[tl->n - 1];
tl->n++;
}
-/*
- * Simplified version without a parser,
- * never incremental, never periodic, for use by tbl(7).
- */
-void
-term_tab_iset(size_t inc)
-{
- if (tabs.a.n >= tabs.a.s) {
- tabs.a.s += 8;
- tabs.a.t = mandoc_reallocarray(tabs.a.t, tabs.a.s,
- sizeof(*tabs.a.t));
- }
- tabs.a.t[tabs.a.n++] = inc;
-}
-
size_t
term_tab_next(size_t prev)
{