]> granicus.if.org Git - graphviz/commitdiff
fix: unescaped backslashes in node labels in xdot output
authorMatthew Fernandez <matthew.fernandez@gmail.com>
Tue, 21 Jul 2020 00:18:40 +0000 (17:18 -0700)
committerMatthew Fernandez <matthew.fernandez@gmail.com>
Tue, 21 Jul 2020 00:55:16 +0000 (17:55 -0700)
Fixes #165.

CHANGELOG.md
plugin/core/gvrender_core_dot.c
rtest/165.dot [new file with mode: 0644]
rtest/test_regression.py

index f293b7ed10a2126c338d3cbf2d5a3949caa4593c..98a4f65547c2550c4fadedbf8abce6f12ab176ee 100644 (file)
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 ### Fixed
 - gvpr: line numbers in gvpr errors/warnings are incorrect #1594
+- Escaped backslashes are not correctly handled when producing xdot with dot #165
 
 ## [2.44.1] - 2020-06-29
 
index f086278f2097a90ffe495fcb2c0a9b81c77e76b1..f6b29ea02c92cba253ac88b966d2e690d963aa79 100644 (file)
@@ -245,13 +245,48 @@ static void xdot_style (GVJ_t *job)
 
 }
 
+/** set the value of a symbol within a node, escaping backslashes
+ *
+ * The back ends that output certain text-based formats, e.g. xdot, assume that
+ * all characters that have special meaning within a string have already been
+ * escaped, with the exception of double quote ("). Hence, any string being
+ * constructed from user-provided input needs to be escaped for other
+ * problematic characters (namely \) beforehand. This is a little utility
+ * function to do such.
+ *
+ * @param n Node to operate on
+ * @param sym Symbol to set
+ * @param value Unescaped string
+ */
+static void put_escaping_backslashes(Agnode_t* n, Agsym_t *sym, const char *value)
+{
+    agxbuf buf;
+
+    /* create a temporary buffer */
+    agxbinit(&buf, 0, NULL);
+
+    /* print the given string to the buffer, escaping as we go */
+    for (; *value != '\0'; ++value) {
+        if (*value == '\\') {
+            agxbputc(&buf, '\\');
+        }
+        agxbputc(&buf, *value);
+    }
+
+    /* update the node's symbol to the escaped text */
+    agxset(n, sym, agxbuse(&buf));
+
+    /* discard the buffer */
+    agxbfree(&buf);
+}
+
 static void xdot_end_node(GVJ_t* job)
 {
     Agnode_t* n = job->obj->u.n; 
     if (agxblen(xbufs[EMIT_NDRAW]))
        agxset(n, xd->n_draw, agxbuse(xbufs[EMIT_NDRAW]));
     if (agxblen(xbufs[EMIT_NLABEL]))
-       agxset(n, xd->n_l_draw, agxbuse(xbufs[EMIT_NLABEL]));
+       put_escaping_backslashes(n, xd->n_l_draw, agxbuse(xbufs[EMIT_NLABEL]));
     penwidth[EMIT_NDRAW] = 1;
     penwidth[EMIT_NLABEL] = 1;
     textflags[EMIT_NDRAW] = 0;
diff --git a/rtest/165.dot b/rtest/165.dot
new file mode 100644 (file)
index 0000000..bdc0c4b
--- /dev/null
@@ -0,0 +1,4 @@
+// see test_regression.py:test_165()
+digraph {
+  a [label="hello \\\" world"];
+}
index 04d8f6a156d2678a30a593013e3a813c8dfaace5..7444354549d84998cd367345e3e8a288b628166b 100644 (file)
@@ -1,5 +1,6 @@
 import subprocess
 import os
+import re
 
 # The terminology used in rtest.sh is a little inconsistent. At the
 # end it reports the total number of tests, the number of "failures"
@@ -32,6 +33,28 @@ def test_regression_failure():
 # FIXME: re-enable when all tests pass on all platforms
 #    assert result.returncode == 0
 
+def test_165():
+    '''
+    dot should be able to produce properly escaped xdot output
+    https://gitlab.com/graphviz/graphviz/-/issues/165
+    '''
+
+    # locate our associated test case in this directory
+    input = os.path.join(os.path.dirname(__file__), '165.dot')
+    assert os.path.exists(input), 'unexpectedly missing test case'
+
+    # ask Graphviz to translate it to xdot
+    output = subprocess.check_output(['dot', '-Txdot', input],
+      universal_newlines=True)
+
+    # find the line containing the _ldraw_ attribute
+    ldraw = re.search(r'^\s*_ldraw_\s*=(?P<value>.*?)$', output, re.MULTILINE)
+    assert ldraw is not None, 'no _ldraw_ attribute in graph'
+
+    # this should contain the label correctly escaped
+    assert r'hello \\\" world' in ldraw.group('value'), \
+      'unexpected ldraw contents'
+
 def test_1436():
     '''
     test a segfault from https://gitlab.com/graphviz/graphviz/-/issues/1436 has