+ * Do an sprintf(), but into a custom-allocated buffer.
+ *
+ * Currently I'm doing this via vsnprintf. This has worked so far,
+ * but it's not good, because:
+ *
+ * - vsnprintf is not available on all platforms. There's an ifdef
+ * to use `_vsnprintf', which seems to be the local name for it
+ * on Windows. Other platforms may lack it completely, in which
+ * case it'll be time to rewrite this function in a totally
+ * different way.
+ *
+ * - technically you can't reuse a va_list like this: it is left
+ * unspecified whether advancing a va_list pointer modifies its
+ * value or something it points to, so on some platforms calling
+ * vsnprintf twice on the same va_list might fail hideously. It
+ * would be better to use the `va_copy' macro mandated by C99,
+ * but that too is not yet ubiquitous.
+ *
+ * The only `properly' portable solution I can think of is to
+ * implement my own format string scanner, which figures out an
+ * upper bound for the length of each formatting directive,
+ * allocates the buffer as it goes along, and calls sprintf() to
+ * actually process each directive. If I ever need to actually do
+ * this, some caveats:
+ *
+ * - It's very hard to find a reliable upper bound for
+ * floating-point values. %f, in particular, when supplied with
+ * a number near to the upper or lower limit of representable
+ * numbers, could easily take several hundred characters. It's
+ * probably feasible to predict this statically using the
+ * constants in <float.h>, or even to predict it dynamically by
+ * looking at the exponent of the specific float provided, but
+ * it won't be fun.
+ *
+ * - Don't forget to _check_, after calling sprintf, that it's
+ * used at most the amount of space we had available.
+ *
+ * - Fault any formatting directive we don't fully understand. The
+ * aim here is to _guarantee_ that we never overflow the buffer,
+ * because this is a security-critical function. If we see a
+ * directive we don't know about, we should panic and die rather
+ * than run any risk.
+ */
+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 = snewn(512, char);
+ 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 = sresize(buf, size, char);
+ }
+}
+
+/*
+ * Read an entire line of text from a file. Return a buffer
+ * malloced to be as big as necessary (caller must free).
+ */
+char *fgetline(FILE *fp)
+{
+ char *ret = snewn(512, char);
+ int size = 512, len = 0;
+ while (fgets(ret + len, size - len, fp)) {
+ len += strlen(ret + len);
+ if (ret[len-1] == '\n')
+ break; /* got a newline, we're done */
+ size = len + 512;
+ ret = sresize(ret, size, char);
+ }
+ if (len == 0) { /* first fgets returned NULL */
+ sfree(ret);
+ return NULL;
+ }
+ ret[len] = '\0';
+ return ret;
+}
+
+/* ----------------------------------------------------------------------
+ * Base64 encoding routine. This is required in public-key writing
+ * but also in HTTP proxy handling, so it's centralised here.
+ */
+
+void base64_encode_atom(unsigned char *data, int n, char *out)
+{
+ static const char base64_chars[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ unsigned word;
+
+ word = data[0] << 16;
+ if (n > 1)
+ word |= data[1] << 8;
+ if (n > 2)
+ word |= data[2];
+ out[0] = base64_chars[(word >> 18) & 0x3F];
+ out[1] = base64_chars[(word >> 12) & 0x3F];
+ if (n > 1)
+ out[2] = base64_chars[(word >> 6) & 0x3F];
+ else
+ out[2] = '=';
+ if (n > 2)
+ out[3] = base64_chars[word & 0x3F];
+ else
+ out[3] = '=';
+}
+
+/* ----------------------------------------------------------------------