gbranden pushed a commit to branch master
in repository groff.

commit 52f93e69dddb39bbfbbf7c43e355538e35b8edab
Author: G. Branden Robinson <g.branden.robin...@gmail.com>
AuthorDate: Thu Mar 20 00:24:05 2025 -0500

    [troff]: Implement macro/string/diversion dumper.
    
    ...made possible by the 60+ themed commits earlier this month.
    
    * src/roff/troff/input.cpp: Do it.  Add inelegant `extern` declaration
      roping in `dump_node_list()` function from "node.cpp".
    
      (class macro_header): Replace `json_dump()` member function with
      `json_dump_macro()` and `json_dump_diversion()`, since how we dump
      depends on what component objects we contain.
    
      (macro::json_dump): Call appropriate aforementioned function;
      `json_dump_macro` on macros and strings, `json_dump_diversion` on
      diversions.
    
      (macro_header::json_dump_diversion): Call `dump_node_list()` on the
      `head` member of this object's `node_list` `struct`.
    
      (macro::json_dump): Rename this...
      (macro::json_dump_macro): ...to this.  Also use `errprint()` instead
      of `fputc()`.  Also explicitly compare return values of pointer type
      to null pointer literal instead of letting them pun down to Booleans.
    
      (print_macros): Check for arguments.  If given any, interpret them as
      macro/string/diversion names and dump the contents of each,
      JSON-encoded.  If not, proceed with previously unconditional behavior.
    
    * doc/groff.texi.in (Debugging) <pm>:
    * man/groff.7.man (Request short reference) <pm>:
    * man/groff_diff.7.man (Other differences): Document it.
    
    Illustrations:
    
    Here's a string...
    
    $ printf '.ds str \\%%ambi\\%%dextrous\n.pm str\n' \
      | build/test-groff -z 2>&1
    {"name": "str", "contents": "\u001Eambi\u001Edextrous"}
    $ printf '.ds str \\%%ambi\\%%dextrous\n.pm str\n' \
      | build/test-groff -z 2>&1 | jq
    {
      "name": "str",
      "contents": "\u001eambi\u001edextrous"
    }
    
    ...a macro...
    
    $ printf '.de foo\nhello to \\\\$@ from\n.ie t troff\n.el nroff\nand H.\\& 
H.\\& Asquith.\n..\n.pm foo\n' | build/test-groff -z 2>&1
    {"name": "foo", "contents": "hello to \\$@ from\n.ie t troff\n.el 
nroff\nand H.\u0012 H.\u0012 Asquith.\n"}
    $ printf '.de foo\nhello to \\\\$@ from\n.ie t troff\n.el nroff\nand H.\\& 
H.\\& Asquith.\n..\n.pm foo\n' | build/test-groff -z 2>&1 | jq
    {
      "name": "foo",
      "contents": "hello to \\$@ from\n.ie t troff\n.el nroff\nand H.\u0012 
H.\u0012 Asquith.\n"
    }
    
    ...and (strap yourself in) a diversion.
    
    $ printf '.di foo\nABC.\n.sp\nDEF\n.br\n.di\n.pm foo\n' \
      | build/test-groff -z 2>&1
    {"name": "foo", "contents": [{"type": "line_start_node", "diversion level": 
0, "is_special_node": false}, {"type": "glyph_node", "diversion level": 0, 
"is_special_node": false, "character": "A"}, {"type": "glyph_node", "diversion 
level": 0, "is_special_node": false, "character": "B"}, {"type": "glyph_node", 
"diversion level": 0, "is_special_node": false, "character": "C"}, {"type": 
"glyph_node", "diversion level": 0, "is_special_node": false, "character": 
"."}, {"type": "vertical_size [...]
    $ printf '.di foo\nABC.\n.sp\nDEF\n.br\n.di\n.pm foo\n' \
      | build/test-groff -z 2>&1 | jq
    {
      "name": "foo",
      "contents": [
        {
          "type": "line_start_node",
          "diversion level": 0,
          "is_special_node": false
        },
        {
          "type": "glyph_node",
          "diversion level": 0,
          "is_special_node": false,
          "character": "A"
        },
        {
          "type": "glyph_node",
          "diversion level": 0,
          "is_special_node": false,
          "character": "B"
        },
        {
          "type": "glyph_node",
          "diversion level": 0,
          "is_special_node": false,
          "character": "C"
        },
        {
          "type": "glyph_node",
          "diversion level": 0,
          "is_special_node": false,
          "character": "."
        },
        {
          "type": "vertical_size_node",
          "diversion level": 0,
          "is_special_node": false,
          "vunits": -12000
        },
        {
          "type": "vertical_size_node",
          "diversion level": 0,
          "is_special_node": false,
          "vunits": 0
        },
        {
          "type": "diverted_space_node",
          "diversion level": 0,
          "is_special_node": false,
          "vunits": 12000
        },
        {
          "type": "line_start_node",
          "diversion level": 0,
          "is_special_node": false
        },
        {
          "type": "glyph_node",
          "diversion level": 0,
          "is_special_node": false,
          "character": "D"
        },
        {
          "type": "glyph_node",
          "diversion level": 0,
          "is_special_node": false,
          "character": "E"
        },
        {
          "type": "glyph_node",
          "diversion level": 0,
          "is_special_node": false,
          "character": "F"
        },
        {
          "type": "vertical_size_node",
          "diversion level": 0,
          "is_special_node": false,
          "vunits": -12000
        },
        {
          "type": "vertical_size_node",
          "diversion level": 0,
          "is_special_node": false,
          "vunits": 0
        }
      ]
    }
---
 doc/groff.texi.in        | 12 +++++++++-
 man/groff.7.man          |  9 ++++++++
 man/groff_diff.7.man     |  3 ++-
 src/roff/troff/input.cpp | 57 +++++++++++++++++++++++++++++++++++++-----------
 4 files changed, 66 insertions(+), 15 deletions(-)

diff --git a/doc/groff.texi.in b/doc/groff.texi.in
index f9b795dd9..4ff7c88d5 100644
--- a/doc/groff.texi.in
+++ b/doc/groff.texi.in
@@ -17760,11 +17760,21 @@ a pair of empty brackets
 represents an empty list.
 @endDefreq
 
-@Defreq {pm, }
+@Defreq {pm, [@Var{name} @r{@dots{}}]}}
+@cindex dumping macros, strings, or diversions (@code{pm})
 @cindex dumping symbol table (@code{pm})
 @cindex symbol table, dumping (@code{pm})
+@cindex macro, dumping (@code{pm})
+@cindex string, dumping (@code{pm})
+@cindex diversion, dumping (@code{pm})
 Report,
 to the standard error stream,
+the name and @acronym{JSON}-encoded contents of each macro,
+string,
+or diversion
+@var{name},
+or,
+without arguments,
 the names of all defined macros,
 strings,
 and diversions and their sizes in bytes.
diff --git a/man/groff.7.man b/man/groff.7.man
index a11102f4b..89bd4cf43 100644
--- a/man/groff.7.man
+++ b/man/groff.7.man
@@ -4221,6 +4221,15 @@ strings,
 and diversions and their sizes in bytes.
 .
 .TPx
+.REQ .pm "name \fR\&.\|.\|.\&\fP"
+Report,
+to the standard error stream,
+the name and JSON-encoded contents of each macro,
+string,
+or diversion
+.IR name .
+.
+.TPx
 .REQ .pn \[+-]N
 Set next page number.
 .
diff --git a/man/groff_diff.7.man b/man/groff_diff.7.man
index 78ecab57e..4a1f57fee 100644
--- a/man/groff_diff.7.man
+++ b/man/groff_diff.7.man
@@ -6282,7 +6282,8 @@ units.
 .
 GNU
 .I troff \" GNU
-ignores any arguments and reports the sizes in bytes.
+reports the sizes in bytes if given no arguments,
+and otherwise dumps the contents of each named argument.
 .
 .
 .P
diff --git a/src/roff/troff/input.cpp b/src/roff/troff/input.cpp
index 0bf7533c5..1b18403ce 100644
--- a/src/roff/troff/input.cpp
+++ b/src/roff/troff/input.cpp
@@ -3552,7 +3552,8 @@ public:
   node_list nl;
   macro_header() { count = 1; }
   macro_header *copy(int);
-  void json_dump();
+  void json_dump_macro();
+  void json_dump_diversion();
 };
 
 macro::~macro()
@@ -3719,7 +3720,10 @@ void macro::print_size()
 void macro::json_dump()
 {
   if (p != 0 /* nullptr */)
-    p->json_dump();
+    if (is_a_diversion)
+      p->json_dump_diversion();
+    else
+      p->json_dump_macro();
   else
     fputs("\"\"", stderr);
 }
@@ -3747,9 +3751,17 @@ macro_header *macro_header::copy(int n)
   return p;
 }
 
-void macro_header::json_dump()
+extern void dump_node_list(node *);
+
+void macro_header::json_dump_diversion()
+{
+  dump_node_list(nl.head);
+  fflush(stderr);
+}
+
+void macro_header::json_dump_macro()
 {
-  fputc('\"', stderr);
+  errprint("\"contents\": \"");
   int macro_len = cl.length();
   for (int i = 0; i < macro_len; i++) {
     json_char jc = json_encode_char(cl.get(i));
@@ -3758,22 +3770,41 @@ void macro_header::json_dump()
     for (size_t j = 0; j < jc.len; j++)
       fputc(jc.buf[j], stderr);
   }
-  fputc('\"', stderr);
+  errprint("\"");
   fflush(stderr);
 }
 
 void print_macros()
 {
-  object_dictionary_iterator iter(request_dictionary);
   request_or_macro *rm;
+  macro *m = 0 /* nullptr */;
   symbol s;
-  while (iter.get(&s, (object **)&rm)) {
-    assert(!s.is_null());
-    macro *m = rm->to_macro();
-    if (m) {
-      errprint("%1\t", s.contents());
-      m->print_size();
-      errprint("\n");
+  if (has_arg()) {
+    do {
+      s = get_name();
+      rm = static_cast<request_or_macro *>(request_dictionary.lookup(s));
+      if (rm != 0 /* nullptr */)
+       m = rm->to_macro();
+      if (m != 0 /* nullptr */) {
+       errprint("{\"name\": ");
+       s.json_dump();
+       errprint(", ");
+       m->json_dump();
+       errprint("}\n");
+       fflush(stderr);
+      }
+    } while (has_arg());
+  }
+  else {
+    object_dictionary_iterator iter(request_dictionary);
+    while (iter.get(&s, (object **)&rm)) {
+      assert(!s.is_null());
+      m = rm->to_macro();
+      if (m != 0 /* nullptr */) {
+       errprint("%1\t", s.contents());
+       m->print_size();
+       errprint("\n");
+      }
     }
   }
   fflush(stderr);

_______________________________________________
groff-commit mailing list
groff-commit@gnu.org
https://lists.gnu.org/mailman/listinfo/groff-commit

Reply via email to