Cope with platforms whose vsnprintf violates both POSIX and C99
authorSimon McVittie <simon.mcvittie@collabora.co.uk>
Tue, 21 Jun 2011 13:09:32 +0000 (14:09 +0100)
committerSimon McVittie <simon.mcvittie@collabora.co.uk>
Wed, 21 Sep 2011 10:25:48 +0000 (11:25 +0100)
Bug: https://bugs.freedesktop.org/show_bug.cgi?id=11668
Reviewed-by: Will Thompson <will.thompson@collabora.co.uk>
dbus/dbus-sysdeps-unix.c

index 171977d..f51f6fc 100644 (file)
@@ -3021,14 +3021,60 @@ _dbus_full_duplex_pipe (int        *fd1,
  *
  * @param format a printf-style format string
  * @param args arguments for the format string
- * @returns length of the given format string and args
+ * @returns length of the given format string and args, or -1 if no memory
  */
 int
 _dbus_printf_string_upper_bound (const char *format,
                                  va_list     args)
 {
-  char c;
-  return vsnprintf (&c, 1, format, args);
+  char static_buf[1024];
+  int bufsize = sizeof (static_buf);
+  int len;
+
+  len = vsnprintf (static_buf, bufsize, format, args);
+
+  /* If vsnprintf() returned non-negative, then either the string fits in
+   * static_buf, or this OS has the POSIX and C99 behaviour where vsnprintf
+   * returns the number of characters that were needed, or this OS returns the
+   * truncated length.
+   *
+   * We ignore the possibility that snprintf might just ignore the length and
+   * overrun the buffer (64-bit Solaris 7), because that's pathological.
+   * If your libc is really that bad, come back when you have a better one. */
+  if (len == bufsize)
+    {
+      /* This could be the truncated length (Tru64 and IRIX have this bug),
+       * or the real length could be coincidentally the same. Which is it?
+       * If vsnprintf returns the truncated length, we'll go to the slow
+       * path. */
+      if (vsnprintf (static_buf, 1, format, args) == 1)
+        len = -1;
+    }
+
+  /* If vsnprintf() returned negative, we have to do more work.
+   * HP-UX returns negative. */
+  while (len < 0)
+    {
+      char *buf;
+
+      bufsize *= 2;
+
+      buf = dbus_malloc (bufsize);
+
+      if (buf == NULL)
+        return -1;
+
+      len = vsnprintf (buf, bufsize, format, args);
+      dbus_free (buf);
+
+      /* If the reported length is exactly the buffer size, round up to the
+       * next size, in case vsnprintf has been returning the truncated
+       * length */
+      if (len == bufsize)
+        len = -1;
+    }
+
+  return len;
 }
 
 /**