gbranden pushed a commit to branch master
in repository groff.

commit 589a724ecae6811a795b661484ef60843565dc8c
Author: G. Branden Robinson <[email protected]>
AuthorDate: Sun Nov 2 13:38:03 2025 -0600

    [troff]: Warn on groffish esc seqs in compat mode.
    
    * src/roff/troff/input.cpp (token::next): Throw warnings in category
      "syntax" when a document uses GNU troff extension escape sequences
      while in compatibility mode.
    
    * doc/groff.texi.in (Warnings):
    * src/roff/troff/troff.1.man (Warnings): Document this.
---
 ChangeLog                  |  8 +++++
 doc/groff.texi.in          |  7 +++--
 src/roff/troff/input.cpp   | 76 ++++++++++++++++++++++++++++++++++------------
 src/roff/troff/troff.1.man |  5 +--
 4 files changed, 72 insertions(+), 24 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 522a08a27..2594278a5 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2025-11-02  G. Branden Robinson <[email protected]>
+
+       * src/roff/troff/input.cpp (token::next): Throw warnings in
+       category "syntax" when a document uses GNU troff extension
+       escape sequences while in compatibility mode.
+       * doc/groff.texi.in (Warnings):
+       * src/roff/troff/troff.1.man (Warnings): Document this.
+
 2025-11-02  G. Branden Robinson <[email protected]>
 
        [troff]: Fix Savannah #67408.  As I've observed before in the
diff --git a/doc/groff.texi.in b/doc/groff.texi.in
index 1641b44ac..9ecdd0a4f 100644
--- a/doc/groff.texi.in
+++ b/doc/groff.texi.in
@@ -465,7 +465,7 @@ Documentation License''.
 @title groff
 @subtitle The GNU implementation of @code{troff}
 @subtitle version @VERSION@
-@subtitle October 2025
+@subtitle November 2025
 @author Trent@tie{}A.@: Fisher
 @author Werner Lemberg
 @author G.@tie{}Branden Robinson
@@ -19311,8 +19311,9 @@ empty,
 or nonsensical character class;
 or a
 @code{groff}
-extension conditional expression operator was used
-while in compatibility mode.
+extension escape sequence
+or conditional expression operator
+was used while in compatibility mode.
 
 @item di
 @itemx 256
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index bafc62d1b..a1367a30b 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -2280,29 +2280,41 @@ void token::next()
       case '^':
        goto ESCAPE_CIRCUMFLEX;
       case '/':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        type = TOKEN_ITALIC_CORRECTION;
        return;
       case ',':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        type = TOKEN_NODE;
        nd = new left_italic_corrected_node;
        return;
       case '&':
        goto ESCAPE_AMPERSAND;
       case ')':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        goto ESCAPE_RIGHT_PARENTHESIS;
       case '!':
        goto ESCAPE_BANG;
       case '?':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        goto ESCAPE_QUESTION;
       case '~':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        goto ESCAPE_TILDE;
       case ':':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        goto ESCAPE_COLON;
       case '"':
        while ((cc = input_stack::get(0 /* nullptr */)) != '\n'
@@ -2314,7 +2326,9 @@ void token::next()
          type = TOKEN_EOF;
        return;
       case '#':                        // Like \" but newline is ignored.
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        while ((cc = input_stack::get(0 /* nullptr */)) != '\n')
          if (cc == EOF) {
            type = TOKEN_EOF;
@@ -2346,7 +2360,9 @@ void token::next()
        type = TOKEN_NODE;
        return;
       case 'A':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        {
          const char *res = do_name_test();
          if (0 /* nullptr */ == res)
@@ -2362,7 +2378,9 @@ void token::next()
        type = TOKEN_NODE;
        return;
       case 'B':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        {
          const char *res = do_expr_test();
          if (0 /* nullptr */ == res)
@@ -2393,7 +2411,9 @@ void token::next()
       case 'e':
        goto ESCAPE_e;
       case 'E':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        goto handle_escape_char;
       case 'f':
        {
@@ -2403,7 +2423,9 @@ void token::next()
          break;
        }
       case 'F':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        {
          curenv->set_family(read_escape_parameter(ALLOW_EMPTY));
          have_formattable_input = true;
@@ -2460,13 +2482,17 @@ void token::next()
          return;
        }
       case 'm':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        do_stroke_color(read_escape_parameter(ALLOW_EMPTY));
        if (!want_att_compat)
          have_formattable_input = true;
        break;
       case 'M':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        do_fill_color(read_escape_parameter(ALLOW_EMPTY));
        if (!want_att_compat)
          have_formattable_input = true;
@@ -2491,7 +2517,9 @@ void token::next()
        type = TOKEN_NODE;
        return;
       case 'O':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        nd = do_suppress(read_escape_parameter());
        if (0 /* nullptr */ == nd)
          break;
@@ -2505,7 +2533,9 @@ void token::next()
        nd = new vmotion_node(-curenv->get_size(), curenv->get_fill_color());
        return;
       case 'R':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        do_register();
        if (!want_att_compat)
          have_formattable_input = true;
@@ -2538,7 +2568,9 @@ void token::next()
        nd = new vmotion_node(x, curenv->get_fill_color());
        return;
       case 'V':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        {
          symbol s = read_escape_parameter();
          if (!(s.is_null() || s.is_empty()))
@@ -2561,7 +2593,9 @@ void token::next()
        type = TOKEN_NODE;
        return;
       case 'Y':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        {
          symbol s = read_escape_parameter();
          if (s.is_null() || s.is_empty())
@@ -2596,7 +2630,9 @@ void token::next()
          return;
        }
       case 'Z':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        nd = do_zero_width_output();
        if (0 /* nullptr */ == nd)
          break;
@@ -2609,7 +2645,9 @@ void token::next()
       case '\n':
        break;
       case '[':
-       // TODO: Reject this GNU troff extension in compatibility mode?
+       if (want_att_compat)
+         warning(WARN_SYNTAX, "an escaped '%1' is not portable to"
+                 " AT&T troff", char(cc));
        if (!want_att_compat) {
          symbol s = read_long_escape_parameters(WITH_ARGS);
          if (s.is_null() || s.is_empty())
diff --git a/src/roff/troff/troff.1.man b/src/roff/troff/troff.1.man
index 4e3566e54..e3648507c 100644
--- a/src/roff/troff/troff.1.man
+++ b/src/roff/troff/troff.1.man
@@ -809,8 +809,9 @@ empty,
 or nonsensical character class;
 or a
 .I groff
-extension conditional expression operator was used
-while in compatibility mode.
+extension escape sequence
+or conditional expression operator
+was used while in compatibility mode.
 .
 .
 .TP

_______________________________________________
groff-commit mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/groff-commit

Reply via email to