Follow-up Comment #30, bug #67571 (group groff):

At 2025-12-04T15:07:44-0500, Dave wrote:
> Follow-up Comment #29, bug #67571 (group groff):
>
> [comment #22 comment #22:]
>> I do want the formatter to diagnose when the input attempts
>> to interpolate a character class in any other context
>
> I'm in favor of that: it's making groff complain when the user attempts
> something that's always been explicitly documented as unsupported.

I just happen to have a patch in my Git stash to do exactly this.  :)


diff --git a/src/roff/troff/env.cpp b/src/roff/troff/env.cpp
index 1f4e3c054..c625abab7 100644
--- a/src/roff/troff/env.cpp
+++ b/src/roff/troff/env.cpp
@@ -1683,6 +1683,8 @@ void margin_character()
       curenv->margin_character_node = 0 /* nullptr */;
     }
   }
+  else if (ci->is_class())
+    error("cannot use %1 as a margin character", tok.description());
   else {
     // Call tok.next() only after making the node so that
     // .mc \s+9\(br\s0 works.
@@ -3856,6 +3858,12 @@ static void add_hyphenation_exceptions()
        skip_line();
        return;
       }
+      if (ci->is_class()) {
+       error("cannot use %1 in a hyphenation exception word",
+            tok.description());
+       skip_line();
+       return;
+      }
       tok.next();
       if (ci->get_ascii_code() == '-') {
        if (i > 0 && (npos == 0 || pos[npos - 1] != i))
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index b4333a2b6..bf090f05a 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -1673,8 +1673,15 @@ node *do_overstrike() // \o
        delete osnode;
        return 0 /* nullptr */;
       }
+      else if (ci->is_class()) {
+       error("cannot use %1 in overstrike escape sequence",
+             tok.description());
+       delete osnode;
+       return 0 /* nullptr */;
+      }
       else {
        node *n = curenv->make_char_node(ci);
+       // XXX add assertion here
        if (n != 0 /* nullptr */)
          osnode->overstrike(n);
       }
@@ -1732,8 +1739,15 @@ static node *do_bracket() // \b
       delete bracketnode;
       return 0 /* nullptr */;
     }
+    else if (ci->is_class()) {
+      error("cannot use %1 in bracket-building escape sequence",
+           tok.description());
+      delete bracketnode;
+      return 0 /* nullptr */;
+    }
     else {
        node *n = curenv->make_char_node(ci);
+       // XXX add assertion here
        if (n != 0 /* nullptr */)
        bracketnode->bracket(n);
     }
@@ -2633,11 +2647,17 @@ void token::next()
              break;
            }
            node *gn = curenv->make_char_node(ci);
+           // XXX add assertion here
            if (0 /* nullptr */ == gn) {
              assert("make_char_node failed to create a character"
                     " node");
              break;
            }
+           if (ci->is_class()) {
+             error("cannot use %1 as argument to zero-width escape"
+                   " sequence", tok.description());
+             break;
+           }
            nd = new zero_width_node(gn);
            type = TOKEN_NODE;
          }
@@ -5933,6 +5953,12 @@ static bool get_line_arg(units *n, unsigned char si,
charinfo **cip)
     if (!(start_token == tok
          && input_stack::get_level() == start_level)) {
       *cip = tok.get_charinfo(true /* required */);
+      // XXX add assertion here
+      if ((*cip != 0 /* nullptr */) && (*cip)->is_class()) {
+       error("cannot use %1 in line-drawing escape sequence",
+            tok.description());
+       return false;
+      }
       tok.next();
     }
     if (!(start_token == tok
@@ -6276,6 +6302,7 @@ void read_title_parts(node **part, hunits *part_width)
   token start(tok);
   int start_level = input_stack::get_level();
   tok.next();
+  // A title has three parts: "'left'center'right'".
   for (int i = 0; i < 3; i++) {
     while (!tok.is_newline() && !tok.is_eof()) {
       if ((tok == start)
@@ -6473,14 +6500,19 @@ static void
map_special_character_for_device_output(macro *mac,
 static void encode_special_character_for_device_output(macro *mac)
 {
   const char *sc;
-  if (font::use_charnames_in_special) {
-    charinfo *ci = tok.get_charinfo(true /* required */);
-    if (ci != 0 /* nullptr */)
-       sc = ci->get_symbol()->contents();
-    else
-      assert(0 == "attempted to encode token without charinfo for"
-                 " device extension command output");
+  charinfo *ci = tok.get_charinfo(true /* required */);
+  if (0 /* nullptr */ == ci) {
+    assert(0 == "attempted to encode token without charinfo for"
+               " device extension command output");
+    return;
+  }
+  if (ci->is_class()) {
+    error("cannot encode %1 for device extension command output",
+         tok.description());
+    return;
   }
+  if (font::use_charnames_in_special)
+    sc = ci->get_symbol()->contents();
   else
     sc = tok.get_charinfo(true /* required */)->get_symbol()
       ->contents();
@@ -8865,6 +8897,18 @@ void token::process()
     curenv->space();
     break;
   case TOKEN_SPECIAL_CHAR:
+    {
+      charinfo *ci = get_charinfo();
+      if (0 /* nullptr */ == ci) {
+       assert(0 == "attempted to interpolate special character token"
+             " without charinfo");
+       break;
+      }
+      if (ci->is_class())
+       error("cannot interpolate %1", description());
+      else
+       curenv->add_char(ci);
+    }
     // Optimize `curenv->add_char(get_charinfo())` for token type.
     curenv->add_char(lookup_charinfo(nm));
     break;




    _______________________________________________________

Reply to this item at:

  <https://savannah.gnu.org/bugs/?67571>

_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/

Attachment: signature.asc
Description: PGP signature

Reply via email to