gbranden pushed a commit to branch master
in repository groff.

commit 2548c4659cf7ceaa43bde7d9b5857a3e1412f553
Author: G. Branden Robinson <[email protected]>
AuthorDate: Sat Sep 21 20:32:04 2024 -0500

    [troff]: Interpret spec'l chars in `device` reqs.
    
    * src/roff/troff/input.cpp (device_request): Add extensive new logic to
      subvert copy mode so as to interpret special character escape
      sequences.  We don't yet support _composite_ special character escape
      sequences.
    
    * src/roff/groff/tests/device-control-special-character-handling.sh:
      Update and expand test cases.  Comment out one new case that doesn't
      yet work.
    
    This change is in further service of the grueling march toward
    resolution of <https://savannah.gnu.org/bugs/?63074>.
    
    What's it called when you put _yourself_ on a death march?
---
 ChangeLog                                          | 13 ++++
 .../device-control-special-character-handling.sh   | 87 ++++++++++++++++++----
 src/roff/troff/input.cpp                           | 50 ++++++++++++-
 3 files changed, 131 insertions(+), 19 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 059f74b95..130f94c67 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2024-09-21  G. Branden Robinson <[email protected]>
+
+       * src/roff/troff/input.cpp (device_request): Add extensive new
+       logic to subvert copy mode so as to interpret special character
+       escape sequences.  We don't yet support _composite_ special
+       character escape sequences.
+       * src/roff/groff/tests/
+       device-control-special-character-handling.sh: Update and expand
+       test cases.  Comment out one new case that doesn't yet work.
+
+       This change is in further service of the grueling march toward
+       resolution of <https://savannah.gnu.org/bugs/?63074>.
+
 2024-09-21  G. Branden Robinson <[email protected]>
 
        * src/roff/groff/groff.cpp (main): Clarify verbose output.
diff --git a/src/roff/groff/tests/device-control-special-character-handling.sh 
b/src/roff/groff/tests/device-control-special-character-handling.sh
index 62a8c25b3..320fb71ef 100755
--- a/src/roff/groff/tests/device-control-special-character-handling.sh
+++ b/src/roff/groff/tests/device-control-special-character-handling.sh
@@ -100,42 +100,97 @@ echo "$output" | grep -Fqx 'x X bogus5: {||}~' || wail
 # A more practical case, suggested by Deri James.
 
 input='.
-.ds h Caf\[e aa] Hyphen-Minus and \[rs]\[u2010]
-\X"ps:exec 1:\\X [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT pdfmark"
-\!x X ps:exec 2:\! [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT pdfmark
+.ds h Caf\['"'"'e] Hyphen-Minus and \[rs]\[u2010]
+\X"ps:exec 1:\\X     [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT pdfmark"
+.fl
+\!x X ps:exec 2:\!     [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT pdfmark
 .device ps:exec 3:device [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT pdfmark
+.fl
 .output x X ps:exec 4:output [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT 
pdfmark
 .'
 
 output=$(printf '%s\n' "$input" | "$groff" -T pdf -Z 2> /dev/null \
   | grep '^x X')
-echo "$output"
+error=$(printf '%s\n' "$input" | "$groff" -ww -T pdf -z)
+echo "$error"
+printf "%s\n" "$output"
 
 # Expected:
 #
-# x X ps:exec 2:\! [/Dest /pdf:bm1 /Title (Caf\[e aa] Hyphen-Minus and 
\[rs]\[u2010]) /Level 1 /OUT pdfmark
-# x X ps:exec 4:output [/Dest /pdf:bm1 /Title (Caf\[e aa] Hyphen-Minus and 
\[rs]\[u2010]) /Level 1 /OUT pdfmark
-# x X ps:exec 1:\X [/Dest /pdf:bm1 /Title (Caf\[u00E9] Hyphen-Minus and 
\[u2010]) /Level 1 /OUT pdfmark
-# x X ps:exec 3:device [/Dest /pdf:bm1 /Title (Caf\[u00E9] Hyphen-Minus and 
\[rs]\[u2010]) /Level 1 /OUT pdfmark
+# x X ps:exec 2:\!     [/Dest /pdf:bm1 /Title (Caf\['e] Hyphen-Minus and 
\[rs]\[u2010]) /Level 1 /OUT pdfmark
+# x X ps:exec 4:output [/Dest /pdf:bm1 /Title (Caf\['e] Hyphen-Minus and 
\[rs]\[u2010]) /Level 1 /OUT pdfmark
+# x X ps:exec 1:\X     [/Dest /pdf:bm1 /Title (Caf\[u00E9] Hyphen-Minus and 
\\[u2010]) /Level 1 /OUT pdfmark
+# x X ps:exec 3:device [/Dest /pdf:bm1 /Title (Caf\[u00E9] Hyphen-Minus and 
\\[u2010]) /Level 1 /OUT pdfmark
 
 echo "checking practical bookmarking with \X escape sequence" >&2
-echo "$output" \
-  | grep -q '1:\\X.*(Caf\\\[u00E9\] Hyphen-Minus and \\\[u2010\])' \
+# 5 spaces in the pattern below
+printf "%s\n" "$output" \
+  | grep -Fqx 'x X ps:exec 1:\X     [/Dest /pdf:bm1 /Title (Caf\[u00E9] 
Hyphen-Minus and \\[u2010]) /Level 1 /OUT pdfmark' \
   || wail
 
 echo "checking practical bookmarking with \! escape sequence" >&2
-echo "$output" \
-  | grep -q '2:\\!.*(Caf\\\[e aa\] Hyphen-Minus and \\\[rs\]\\\[u2010\])' \
+# 5 spaces in the pattern below
+printf "%s\n" "$output" \
+  | grep -Fqx 'x X ps:exec 2:\!     [/Dest /pdf:bm1 /Title (Caf\['"'"'e] 
Hyphen-Minus and \[rs]\[u2010]) /Level 1 /OUT pdfmark' \
+  || wail
+
+echo "checking practical bookmarking with device request" >&2
+printf "%s\n" "$output" \
+  | grep -Fqx 'x X ps:exec 3:device [/Dest /pdf:bm1 /Title (Caf\[u00E9] 
Hyphen-Minus and \\[u2010]) /Level 1 /OUT pdfmark' \
+  || wail
+
+echo "checking practical bookmarking with output request" >&2
+printf "%s\n" "$output" \
+  | grep -Fqx 'x X ps:exec 4:output [/Dest /pdf:bm1 /Title (Caf\['"'"'e] 
Hyphen-Minus and \[rs]\[u2010]) /Level 1 /OUT pdfmark' \
+  || wail
+
+#test -z "$fail"
+#exit
+
+# Test the same thing, but with a composite special character escape
+# sequence.
+
+input='.
+.ds h Caf\[e aa] Hyphen-Minus and \[rs]\[u2010]
+\X"ps:exec 5:\\X     [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT pdfmark"
+.fl
+\!x X ps:exec 6:\!     [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT pdfmark
+.device ps:exec 7:device [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT pdfmark
+.fl
+.output x X ps:exec 8:output [/Dest /pdf:bm1 /Title (\*[h]) /Level 1 /OUT 
pdfmark
+.'
+
+output=$(printf '%s\n' "$input" | "$groff" -T pdf -Z 2> /dev/null \
+  | grep '^x X')
+error=$(printf '%s\n' "$input" | "$groff" -ww -T pdf -z)
+echo "$error"
+printf "%s\n" "$output"
+
+# Expected:
+#
+# x X ps:exec 6:\! [/Dest /pdf:bm1 /Title (Caf\['e] Hyphen-Minus and 
\[rs]\[u2010]) /Level 1 /OUT pdfmark
+# x X ps:exec 8:output [/Dest /pdf:bm1 /Title (Caf\['e] Hyphen-Minus and 
\[rs]\[u2010]) /Level 1 /OUT pdfmark
+# x X ps:exec 5:\X [/Dest /pdf:bm1 /Title (Caf\[u00E9] Hyphen-Minus and 
\[u2010]) /Level 1 /OUT pdfmark
+# x X ps:exec 7:device [/Dest /pdf:bm1 /Title (Caf\[u00E9] Hyphen-Minus and 
\[rs]\[u2010]) /Level 1 /OUT pdfmark
+
+echo "checking practical bookmarking with \X escape sequence" >&2
+printf "%s\n" "$output" \
+  | grep -Fqx 'x X ps:exec 5:\X     [/Dest /pdf:bm1 /Title (Caf\[u00E9] 
Hyphen-Minus and \\[u2010]) /Level 1 /OUT pdfmark' \
+  || wail
+
+echo "checking practical bookmarking with \! escape sequence" >&2
+printf "%s\n" "$output" \
+  | grep -Fqx 'x X ps:exec 6:\!     [/Dest /pdf:bm1 /Title (Caf\[e aa] 
Hyphen-Minus and \[rs]\[u2010]) /Level 1 /OUT pdfmark' \
   || wail
 
 #echo "checking practical bookmarking with device request" >&2
-#echo "$output" \
-#  | grep -q '3:device.*(Caf\\\[u00E9\] Hyphen-Minus and \\\[rs\]\\\[u2010\])' 
\
+#printf "%s\n" "$output" \
+#  | grep -Fqx 'x X ps:exec 7:device [/Dest /pdf:bm1 /Title (Caf\[u00E9] 
Hyphen-Minus and \\[u2010]) /Level 1 /OUT pdfmark' \
 #  || wail
 
 echo "checking practical bookmarking with output request" >&2
-echo "$output" \
-  | grep -q '4:output.*(Caf\\\[e aa\] Hyphen-Minus and \\\[rs\]\\\[u2010\])' \
+printf "%s\n" "$output" \
+  | grep -Fqx 'x X ps:exec 8:output [/Dest /pdf:bm1 /Title (Caf\[e aa] 
Hyphen-Minus and \[rs]\[u2010]) /Level 1 /OUT pdfmark' \
   || wail
 
 test -z "$fail"
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index 7e80bc0c6..670fe2eea 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -5987,9 +5987,53 @@ static void device_request()
   // Null characters can correspond to node types like vmotion_node that
   // are unrepresentable in a device extension command, and got scrubbed
   // by `asciify`.
-  for (; c != '\0' && c != '\n' && c != EOF;
-       c = get_copy(0 /* nullptr */))
-    mac.append(c);
+  for (int prevc = c; (c != '\0') && (c != '\n') && (c != EOF);
+       c = get_copy(0 /* nullptr */)) {
+    if (!('\\' == c) && (prevc != '\\'))
+      mac.append(c);
+    else {
+      int c1 = get_copy(0 /* nullptr */);
+      if (c1 != '[')
+       mac.append(c1);
+      else {
+       // Does the input resemble a valid (bracket-form) special
+       // character escape sequence?
+       bool is_valid = false;
+       string sc = "";
+       string scdup; // for composite character ugliness below
+       int c2 = get_copy(0 /* nullptr */);
+       for (; (c2 != '\0') && (c2 != '\n') && (c2 != EOF);
+            c2 = get_copy(0 /* nullptr */)) {
+         // XXX: `map_special_character_for_device_output()` will need
+         // the closing bracket in the iterator we construct, but a
+         // composite character mapping mustn't see it.
+         sc += c2;
+         if (']' == c2) {
+           is_valid = true;
+           break;
+         }
+       }
+       sc += '\0';
+       if (sc.search(' ') > 0) {
+         // XXX: TODO
+         error("composite special character escape sequences not yet"
+               " supported in device extension command arguments");
+         is_valid = false;
+       }
+       if (is_valid) {
+         input_stack::push(make_temp_iterator(sc.contents()));
+         symbol s = read_long_escape_parameters(WITH_ARGS);
+         map_special_character_for_device_output(&mac, s.contents());
+       }
+       else {
+         // We couldn't make sense of it.  Write it out as-is.
+         mac.append(c);
+         mac.append(c1);
+         mac.append_str(sc.contents());
+       }
+      }
+    }
+  }
   curenv->add_node(new device_extension_node(mac));
   tok.next();
 }

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

Reply via email to