]> granicus.if.org Git - graphviz/commitdiff
gvc auto_output_filename: avoid 'strdup' when constructing file name
authorMatthew Fernandez <matthew.fernandez@gmail.com>
Wed, 14 Sep 2022 00:47:29 +0000 (17:47 -0700)
committerMatthew Fernandez <matthew.fernandez@gmail.com>
Wed, 14 Sep 2022 14:47:40 +0000 (07:47 -0700)
This is a reattempt of 4291cc769a3eeef8b1c171e5479194733a4da6cd. The first
attempt introduced a bug where the separating `.` was not accounted for and
accidentally truncated. It was reverted in
8d662734b6a34709d9475b120e7ce3de872339e2. The present commit takes much the same
approach but ensures to account for the separating `.`.

Quoting 4291cc769a3eeef8b1c171e5479194733a4da6cd:

  This block of code is scanning a string of `:`-separated entries and writing
  them into `buf` in reverse order `.`-separated. We can rewrite it to avoid
  dynamic allocation, thus avoiding certain failure cases and locking overhead.

  Unfortunately there seems to be no variant of `strrchr` that takes a length.
  So we need to write our own loop for locating the last `:` with a limit.

lib/gvc/gvdevice.c

index dfe68871d2c4d85dfecf0b4250147546071e2c7e..0ab0e45b069acf079b3815b2fc616b89725317f5 100644 (file)
@@ -17,6 +17,7 @@
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdbool.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <inttypes.h>
@@ -86,7 +87,7 @@ static void auto_output_filename(GVJ_t *job)
     static char *buf;
     static size_t bufsz;
     char gidx[100];  /* large enough for '.' plus any integer */
-    char *fn, *p, *q;
+    char *fn;
     size_t len;
 
     if (job->graph_index)
@@ -107,14 +108,22 @@ static void auto_output_filename(GVJ_t *job)
     strcpy(buf, fn);
     strcat(buf, gidx);
     strcat(buf, ".");
-    p = strdup(job->output_langname);
-    while ((q = strrchr(p, ':'))) {
-        strcat(buf, q+1);
-        strcat(buf, ".");
-       *q = '\0';
+
+    {
+        char *dst = buf + strlen(buf);
+        const char *src = job->output_langname;
+        const char *src_end = src + strlen(src);
+        for (const char *q = src_end; ; --q) {
+            if (*q == ':') {
+                dst += sprintf(dst, "%.*s.", (int)(src_end - q - 1), q + 1);
+                src_end = q;
+            }
+            if (q == src) {
+                sprintf(dst, "%.*s", (int)(src_end - src), src);
+                break;
+            }
+        }
     }
-    strcat(buf, p);
-    free(p);
 
     job->output_filename = buf;
 }