+/*
+ * Do an sprintf(), but into a custom-allocated buffer.
+ *
+ * Irritatingly, we don't seem to be able to do this portably using
+ * vsnprintf(), because there appear to be issues with re-using the
+ * same va_list for two calls, and the excellent C99 va_copy is not
+ * yet widespread. Bah. Instead I'm going to do a horrid, horrid
+ * hack, in which I trawl the format string myself, work out the
+ * maximum length of each format component, and resize the buffer
+ * before printing it.
+ */
+char *dupprintf(const char *fmt, ...)
+{
+ char *ret;
+ va_list ap;
+ va_start(ap, fmt);
+ ret = dupvprintf(fmt, ap);
+ va_end(ap);
+ return ret;
+}
+char *dupvprintf(const char *fmt, va_list ap)
+{
+ char *buf;
+ int len, size;
+
+ buf = smalloc(512);
+ size = 512;
+
+ while (1) {
+#ifdef _WINDOWS
+#define vsnprintf _vsnprintf
+#endif
+ len = vsnprintf(buf, size, fmt, ap);
+ if (len >= 0 && len < size) {
+ /* This is the C99-specified criterion for snprintf to have
+ * been completely successful. */
+ return buf;
+ } else if (len > 0) {
+ /* This is the C99 error condition: the returned length is
+ * the required buffer size not counting the NUL. */
+ size = len + 1;
+ } else {
+ /* This is the pre-C99 glibc error condition: <0 means the
+ * buffer wasn't big enough, so we enlarge it a bit and hope. */
+ size += 512;
+ }
+ buf = srealloc(buf, size);
+ }
+}
+