From 31bab037c9bfde3bd18e06b5ab878c09de265ccf Mon Sep 17 00:00:00 2001 From: Matthew Fernandez Date: Sat, 15 Aug 2020 14:13:34 -0700 Subject: [PATCH] use a dynamic buffer when expanding text replacements 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 | 127 ++++++++++++-------------------------------- 1 file changed, 34 insertions(+), 93 deletions(-) diff --git a/lib/common/labels.c b/lib/common/labels.c index e46c3955c..9be55d809 100644 --- a/lib/common/labels.c +++ b/lib/common/labels.c @@ -11,7 +11,7 @@ * Contributors: See CVS logs. Details at http://www.graphviz.org/ *************************************************************************/ - +#include "agxbuf.h" #include "render.h" #include "htmltable.h" #include @@ -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; } -- 2.50.1