gbranden pushed a commit to branch master
in repository groff.
commit 601c5bd05585a2a4acc51f499ab289687e80b158
Author: G. Branden Robinson <[email protected]>
AuthorDate: Sat Mar 14 15:21:17 2026 -0500
[troff]: Warn of unportable syntax in compat mode.
* src/roff/troff/input.cpp (is_conditional_expression_true): Keep track
of how many leading `!` complementation operators appear in a
conditional expression. If multiple occur and the formatter is in
AT&T compatibility mode, warn in category "syntax" that the
construction is not portable to AT&T troffs.
* doc/groff.texi.in (Other Differences):
* man/groff_diff.7.man (Other differences): Document this syntactical
difference.
Illustration:
$ printf '.nr t 0\n.if !\\nt .tm A\n.if !!\\nt .tm B\n.if !!!\\nt .tm C\n'
| groff
A
C
$ printf '.nr t 0\n.if !\\nt .tm A\n.if !!\\nt .tm B\n.if !!!\\nt .tm C\n'
| dwb troff >/dev/null
A
$ printf '.nr t 0\n.if !\\nt .tm A\n.if !!\\nt .tm B\n.if !!!\\nt .tm C\n'
| 9 troff >/dev/null
A
$ printf '.nr t 0\n.if !\\nt .tm A\n.if !!\\nt .tm B\n.if !!!\\nt .tm C\n'
| heirloom troff >/dev/null
A
$ printf '.nr t 0\n.if !\\nt .tm A\n.if !!\\nt .tm B\n.if !!!\\nt .tm C\n'
| groff -C
A
C
$ printf '.nr t 0\n.if !\\nt .tm A\n.if !!\\nt .tm B\n.if !!!\\nt .tm C\n'
| ./build/test-groff -C
A
troff:<standard input>:3: warning: use of multiple complementation
operators '!' in a conditional expression is not portable to AT&T troff
troff:<standard input>:4: warning: use of multiple complementation
operators '!' in a conditional expression is not portable to AT&T troff
C
A less contrived example would involve stuffing a conditional
expression into a string and then interpolating, possibly with a leading
`!`. GNU troff supports this with reasonable flexibility; AT&T troff
does not.
$ printf '.ds ! !\n.nr t 0\n.if \\*!\\nt .tm A\n.if \\*!\\*!\\nt .tm B\n.if
\\*!\\*!\\*!\\nt .tm C\n' | ./build/test-groff -C
A
troff:<standard input>:4: warning: use of multiple complementation
operators '!' in a conditional expression is not portable to AT&T troff
troff:<standard input>:5: warning: use of multiple complementation
operators '!' in a conditional expression is not portable to AT&T troff
C
$ printf '.ds ! !\n.nr t 0\n.if \\*!\\nt .tm A\n.if \\*!\\*!\\nt .tm B\n.if
\\*!\\*!\\*!\\nt .tm C\n' | dwb troff >/dev/null
A
$ printf '.ds ! !\n.nr t 0\n.if \\*!\\nt .tm A\n.if \\*!\\*!\\nt .tm B\n.if
\\*!\\*!\\*!\\nt .tm C\n' | 9 troff >/dev/null
A
$ printf '.ds ! !\n.nr t 0\n.if \\*!\\nt .tm A\n.if \\*!\\*!\\nt .tm B\n.if
\\*!\\*!\\*!\\nt .tm C\n' | heirloom troff >/dev/null
A
---
ChangeLog | 12 ++++++++++++
doc/groff.texi.in | 25 +++++++++++++++++++++++++
man/groff_diff.7.man | 20 ++++++++++++++++++++
src/roff/troff/input.cpp | 9 +++++++++
4 files changed, 66 insertions(+)
diff --git a/ChangeLog b/ChangeLog
index dc6de6164..2c7fd6959 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2026-03-14 G. Branden Robinson <[email protected]>
+
+ * src/roff/troff/input.cpp (is_conditional_expression_true):
+ Keep track of how many leading `!` complementation operators
+ appear in a conditional expression. If multiple occur and the
+ formatter is in AT&T compatibility mode, warn in category
+ "syntax" that the construction is not portable to AT&T troffs.
+
+ * doc/groff.texi.in (Other Differences):
+ * man/groff_diff.7.man (Other differences): Document this
+ syntactical difference.
+
2026-03-14 G. Branden Robinson <[email protected]>
* src/preproc/html/pushback.cpp (pushBackBuffer::readInt):
diff --git a/doc/groff.texi.in b/doc/groff.texi.in
index e5bb29c5a..245aac436 100644
--- a/doc/groff.texi.in
+++ b/doc/groff.texi.in
@@ -22430,6 +22430,31 @@ and doubling of an initial neutral double quote
@samp{"}
if the file name has one.
+@cindex multiple logical complementation operators @code{!} in conditional
expressions, incompatibility with @acronym{AT&T} @code{troff}
+@cindex complementation operators @code{!}, multiple logical, in conditional
expressions, incompatibility with @acronym{AT&T} @code{troff}
+@cindex @code{!} complementation operator, multiple logical, in conditional
expressions, incompatibility with @acronym{AT&T} @code{troff}
+@cindex operator, complementation (@code{!}), multiple logical, in conditional
expressions, incompatibility with @acronym{AT&T} @code{troff}
+GNU
+@command{troff} @c GNU
+accepts multiple leading complementation operators
+@code{!}@:
+in a conditional expression,
+inverting its truth value with each occurrence.
+
+@Example
+.ie !!0 .tm true
+.el .tm false
+ @error{} false
+@endExample
+
+AT&T
+@command{troff} @c AT&T
+regards the foregoing conditional expression as malformed
+(but issues no diagnostic message)
+and takes
+@emph{neither}
+branch.
+
@cindex output device name string (@code{.T}), in other implementations
The existence of the
@code{.T}
diff --git a/man/groff_diff.7.man b/man/groff_diff.7.man
index d31ec8bc1..a9c46a72d 100644
--- a/man/groff_diff.7.man
+++ b/man/groff_diff.7.man
@@ -6760,6 +6760,26 @@ if the file name has one.
.
.
.P
+GNU
+.I troff \" GNU
+accepts multiple leading complementation operators
+.RB \[lq] ! \[rq]\&
+in a conditional expression,
+inverting its truth value with each occurrence.
+.
+AT&T
+.I troff \" AT&T
+regards the conditional expression in
+.RB \[lq] ".ie !!0 .tm true" \[rq]
+as malformed
+(but issues no diagnostic message)
+and takes neither the true branch
+nor any alternative one subsequently established by an
+.B el
+request.
+.
+.
+.P
The existence of the
.B .T
string is a common feature of device-independent
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index 68a691423..c79ad5035 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -7191,9 +7191,18 @@ static bool is_conditional_expression_true()
bool perform_output_comparison = false;
bool want_test_sense_inverted = false;
tok.skip_spaces();
+ int nbangs = 0;
+ bool warned_about_bangs = false;
while (tok.ch() == int('!')) { // TODO: grochar
+ nbangs++;
tok.next();
want_test_sense_inverted = !want_test_sense_inverted;
+ if (want_att_compat && (nbangs > 1) && !warned_about_bangs) {
+ warning(WARN_SYNTAX, "use of multiple complementation operators"
+ " '!' in a conditional expression is not portable to AT&T"
+ " troff");
+ warned_about_bangs = true;
+ }
}
bool result;
int c = tok.ch(); // safely compares to char literals; TODO: grochar
_______________________________________________
groff-commit mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/groff-commit