diff options
Diffstat (limited to 'contrib/mandoc')
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) { |