gbranden pushed a commit to branch master
in repository groff.

commit c9c316578963a07995bb6cf6a8e00d7aa7a2efed
Author: G. Branden Robinson <g.branden.robin...@gmail.com>
AuthorDate: Tue Mar 4 03:27:09 2025 -0600

    [libgroff]: Support object dumping in JSON (2/3).
    
    Support JSON dumping of `string`s.
    
    Add `json_length()`, `json_extract()`, and `json_dump()` public member
    functions to `string` class.  The enhanced node printing feature will
    require them.
    
    * src/include/stringclass.h (class string): Declare them.
    
    * src/libs/libgroff/string.cpp (string::json_length):
      (string::json_extract, string::json_dump): Implement them.
---
 ChangeLog                    | 10 ++++++
 src/include/stringclass.h    |  5 ++-
 src/libs/libgroff/string.cpp | 86 +++++++++++++++++++++++++++++++++++++++++---
 3 files changed, 95 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 3225d05ea..5480dee2a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,13 @@
+2025-03-04  G. Branden Robinson <g.branden.robin...@gmail.com>
+
+       [libgroff]: Add `json_length()`, `json_extract()`, and
+       `json_dump()` public member functions to `string` class.  The
+       enhanced node printing feature will require them.
+
+       * src/include/stringclass.h (class string): Declare them.
+       * src/libs/libgroff/string.cpp (string::json_length):
+       (string::json_extract, string::json_dump): Implement them.
+
 2025-03-15  G. Branden Robinson <g.branden.robin...@gmail.com>
 
        [libgroff]: Support dumping of some object types in JSON format.
diff --git a/src/include/stringclass.h b/src/include/stringclass.h
index 445b44152..11cc56d7b 100644
--- a/src/include/stringclass.h
+++ b/src/include/stringclass.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 1989-2024 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2025 Free Software Foundation, Inc.
      Written by James Clark (j...@jclark.com)
 
 This file is part of groff.
@@ -69,6 +69,9 @@ public:
   int search(const char) const;
   int find(const char *) const;
   char *extract() const;
+  size_t json_length() const;
+  const char *json_extract() const;
+  void json_dump() const;
   void remove_spaces();
   void clear();
   void move(string &);
diff --git a/src/libs/libgroff/string.cpp b/src/libs/libgroff/string.cpp
index c8d759528..739fdfe05 100644
--- a/src/libs/libgroff/string.cpp
+++ b/src/libs/libgroff/string.cpp
@@ -1,4 +1,4 @@
-/* Copyright (C) 1989-2024 Free Software Foundation, Inc.
+/* Copyright (C) 1989-2025 Free Software Foundation, Inc.
      Written by James Clark (j...@jclark.com)
 
 This file is part of groff.
@@ -21,11 +21,13 @@ along with this program.  If not, see 
<http://www.gnu.org/licenses/>. */
 #endif
 
 #include <stdio.h> // FILE, putc(), sprintf()
-#include <stdlib.h> // malloc()
+#include <stdlib.h> // calloc()
 #include <string.h> // memchr(), memcmp(), memcpy(), memmem(), memset(),
                    // strlen(), size_t
 
+#include "cset.h" // csprint()
 #include "lib.h"
+#include "json-encode.h" // json_encode_char()
 
 #include "stringclass.h"
 
@@ -299,8 +301,9 @@ int string::find(const char *c) const
   return (p != 0 /* nullptr */) ? (p - ptr) : -1;
 }
 
-// we silently strip nuls
-
+// Return pointer to null-terminated C string; any nulls internal to the
+// string are omitted.  The caller is responsible for `free()`ing the
+// returned storage.
 char *string::extract() const
 {
   char *p = ptr;
@@ -310,7 +313,7 @@ char *string::extract() const
   for (i = 0; i < n; i++)
     if (p[i] == '\0')
       nnuls++;
-  char *q = static_cast<char *>(malloc(n + 1 - nnuls));
+  char *q = static_cast<char *>(calloc(n + 1 - nnuls, sizeof(char)));
   if (q != 0 /* nullptr */) {
     char *r = q;
     for (i = 0; i < n; i++)
@@ -321,6 +324,79 @@ char *string::extract() const
   return q;
 }
 
+// Compute length of JSON representation of object.
+size_t string::json_length() const
+{
+  size_t n = len;
+  const char *p = ptr;
+  char ch;
+  int nextrachars = 2; // leading and trailing double quotes
+  for (size_t i = 0; i < n; i++) {
+    ch = p[i];
+    // Handle the most common cases first.
+    if (ch < 128) {
+      if (csprint(ch))
+       ;
+      else
+       switch (ch) {
+       case '"':
+       case '\\':
+       case '/':
+       case '\b':
+       case '\f':
+       case '\n':
+       case '\r':
+       case '\t':
+         nextrachars++;
+         break;
+       default:
+         nextrachars += 5;
+      }
+    }
+    else
+      nextrachars += 5;
+  }
+  return (n + nextrachars);
+}
+
+// Like `extract()`, but double-quote the string and escape characters
+// per JSON and emit nulls.  This string is not null-terminated!  Caller
+// MUST use .json_length().
+const char *string::json_extract() const
+{
+  const char *p = ptr;
+  size_t n = len;
+  size_t i;
+  char *q = static_cast<char *>(calloc(this->json_length(),
+                                      sizeof (char)));
+  if (q != 0 /* nullptr */) {
+    char *r = q;
+    *r++ = '"';
+    json_char ch;
+    for (i = 0; i < n; i++, p++) {
+      ch = json_encode_char(*p);
+      for (size_t j = 0; j < ch.len; j++)
+        *r++ = ch.buf[j];
+    }
+    *r++ = '"';
+  }
+  else
+    return strdup("\"\"");
+  return q;
+}
+
+// Dump string in JSON representation to standard error stream.
+void string::json_dump() const
+{
+  const char *repr = this->json_extract();
+  size_t jsonlen = this->json_length();
+  // Write it out by character to keep libc string functions from
+  // interpreting escape sequences.
+  for (size_t i = 0; i < jsonlen; i++)
+    fputc(repr[i], stderr);
+  free(const_cast<char *>(repr));
+}
+
 void string::remove_spaces()
 {
   int l = len - 1;

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

Reply via email to