]> granicus.if.org Git - graphviz/commitdiff
use a dynamic buffer when expanding text replacements
authorMatthew Fernandez <matthew.fernandez@gmail.com>
Sat, 15 Aug 2020 21:13:34 +0000 (14:13 -0700)
committerMatthew Fernandez <matthew.fernandez@gmail.com>
Thu, 20 Aug 2020 14:40:31 +0000 (07:40 -0700)
This change is intended to guard against bugs like that fixed in
fbefeb31989130c48b965cc9a2f0ad0cac07015c. The buffer used to construct the
result string now dynamically expands so we do not need to manually calculate
its extent in advance. Related to #1676.

lib/common/labels.c

index e46c3955c669b9c3b65b9e5676a2cce6b85aee91..9be55d809ae3fc7ff8661aae1a94e6e7f4b1e584 100644 (file)
@@ -11,7 +11,7 @@
  * Contributors: See CVS logs. Details at http://www.graphviz.org/
  *************************************************************************/
 
-
+#include "agxbuf.h"
 #include "render.h"
 #include "htmltable.h"
 #include <limits.h>
@@ -288,172 +288,113 @@ void emit_label(GVJ_t * job, emit_state_t emit_state, textlabel_t * lp)
  */
 static char *strdup_and_subst_obj0 (char *str, void *obj, int escBackslash)
 {
-    char c, *s, *p, *t, *newstr;
+    char c, *s, *newstr;
     char *tp_str = "", *hp_str = "";
     char *g_str = "\\G", *n_str = "\\N", *e_str = "\\E",
        *h_str = "\\H", *t_str = "\\T", *l_str = "\\L";
-    size_t g_len = 2, n_len = 2, e_len = 2,
-       h_len = 2, t_len = 2, l_len = 2,
-       tp_len = 0, hp_len = 0;
-    size_t newlen = 0;
+    boolean has_hp = FALSE;
+    boolean has_tp = FALSE;
     int isEdge = 0;
     textlabel_t *tl;
     port pt;
+    agxbuf buf;
 
     /* prepare substitution strings */
     switch (agobjkind(obj)) {
        case AGRAPH:
            g_str = agnameof((graph_t *)obj);
-           g_len = strlen(g_str);
            tl = GD_label((graph_t *)obj);
            if (tl) {
                l_str = tl->text;
-               if (str) l_len = strlen(l_str);
            }
            break;
        case AGNODE:
            g_str = agnameof(agraphof((node_t *)obj));
-           g_len = strlen(g_str);
            n_str = agnameof((node_t *)obj);
-           n_len = strlen(n_str);
            tl = ND_label((node_t *)obj);
            if (tl) {
                l_str = tl->text;
-               if (str) l_len = strlen(l_str);
            }
            break;
        case AGEDGE:
            isEdge = 1;
            g_str = agnameof(agroot(agraphof(agtail(((edge_t *)obj)))));
-           g_len = strlen(g_str);
            t_str = agnameof(agtail(((edge_t *)obj)));
-           t_len = strlen(t_str);
            pt = ED_tail_port((edge_t *)obj);
            if ((tp_str = pt.name))
-               tp_len = strlen(tp_str);
+               has_tp = TRUE;
            h_str = agnameof(aghead(((edge_t *)obj)));
-           h_len = strlen(h_str);
            pt = ED_head_port((edge_t *)obj);
            if ((hp_str = pt.name))
-               hp_len = strlen(hp_str);
-           h_len = strlen(h_str);
+               has_hp = TRUE;
            tl = ED_label((edge_t *)obj);
            if (tl) {
                l_str = tl->text;
-               if (str) l_len = strlen(l_str);
            }
            if (agisdirected(agroot(agraphof(agtail(((edge_t*)obj))))))
                e_str = "->";
            else
                e_str = "--";
-           e_len = t_len + (tp_len?tp_len+1:0) + 2 + h_len + (hp_len?hp_len+1:0);
            break;
     }
 
-    /* two passes over str.
-     *
-     * first pass prepares substitution strings and computes 
-     * total length for newstring required from malloc.
-     */
-    for (s = str; (c = *s++);) {
-       if (c == '\\' && *s != '\0') {
-           switch (c = *s++) {
-           case 'G':
-               newlen += g_len;
-               break;
-           case 'N':
-               newlen += n_len;
-               break;
-           case 'E':
-               if (isEdge) {
-                   newlen += t_len;
-                   if (tp_len) {
-                       newlen++;
-                       newlen += tp_len;
-                   }
-                   newlen += e_len;
-                   newlen += h_len;;
-                   if (hp_len) {
-                       newlen++;
-                       newlen += hp_len;
-                   }
-               }
-               break;
-           case 'H':
-               newlen += h_len;
-               break;
-           case 'T':
-               newlen += t_len;
-               break; 
-           case 'L':
-               newlen += l_len;
-               break; 
-           case '\\':
-               if (escBackslash) {
-                   newlen += 1;
-                   break; 
-               }
-               /* Fall through */
-           default:  /* leave other escape sequences unmodified, e.g. \n \l \r */
-               newlen += 2;
-           }
-       } else {
-           newlen++;
-       }
-    }
-    /* allocate new string */
-    newstr = gmalloc(newlen + 1);
+    /* allocate a dynamic buffer that we will use to construct the result */
+    agxbinit(&buf, 0, NULL);
 
-    /* second pass over str assembles new string */
-    for (s = str, p = newstr; (c = *s++);) {
+    /* assemble new string */
+    for (s = str; (c = *s++);) {
        if (c == '\\' && *s != '\0') {
            switch (c = *s++) {
            case 'G':
-               for (t = g_str; (*p = *t++); p++);
+               agxbput(&buf, g_str);
                break;
            case 'N':
-               for (t = n_str; (*p = *t++); p++);
+               agxbput(&buf, n_str);
                break;
            case 'E':
                if (isEdge) {
-                   for (t = t_str; (*p = *t++); p++);
-                   if (tp_len) {
-                       *p++ = ':';
-                       for (t = tp_str; (*p = *t++); p++);
+                   agxbput(&buf, t_str);
+                   if (has_tp) {
+                       agxbputc(&buf, ':');
+                       agxbput(&buf, tp_str);
                    }
-                   for (t = e_str; (*p = *t++); p++);
-                   for (t = h_str; (*p = *t++); p++);
-                   if (hp_len) {
-                       *p++ = ':';
-                       for (t = hp_str; (*p = *t++); p++);
+                   agxbput(&buf, e_str);
+                   agxbput(&buf, h_str);
+                   if (has_hp) {
+                       agxbputc(&buf, ':');
+                       agxbput(&buf, hp_str);
                    }
                }
                break;
            case 'T':
-               for (t = t_str; (*p = *t++); p++);
+               agxbput(&buf, t_str);
                break;
            case 'H':
-               for (t = h_str; (*p = *t++); p++);
+               agxbput(&buf, h_str);
                break;
            case 'L':
-               for (t = l_str; (*p = *t++); p++);
+               agxbput(&buf, l_str);
                break;
            case '\\':
                if (escBackslash) {
-                   *p++ = '\\';
+                   agxbputc(&buf, '\\');
                    break; 
                }
                /* Fall through */
            default:  /* leave other escape sequences unmodified, e.g. \n \l \r */
-               *p++ = '\\';
-               *p++ = c;
+               agxbputc(&buf, '\\');
+               agxbputc(&buf, c);
                break;
            }
        } else {
-           *p++ = c;
+           agxbputc(&buf, c);
        }
     }
-    *p++ = '\0';
+
+    /* extract the final string with replacements applied */
+    newstr = agxbdisown(&buf);
+    agxbfree(&buf);
+
     return newstr;
 }