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

Reply via email to