+ va_start(ap, s1);
+ while (1) {
+ sn = va_arg(ap, char *);
+ if (!sn)
+ break;
+ strcpy(q, sn);
+ q += strlen(q);
+ }
+ va_end(ap);
+
+ return p;
+}
+
+void burnstr(char *string) /* sfree(str), only clear it first */
+{
+ if (string) {
+ smemclr(string, strlen(string));
+ sfree(string);
+ }
+}
+
+int toint(unsigned u)
+{
+ /*
+ * Convert an unsigned to an int, without running into the
+ * undefined behaviour which happens by the strict C standard if
+ * the value overflows. You'd hope that sensible compilers would
+ * do the sensible thing in response to a cast, but actually I
+ * don't trust modern compilers not to do silly things like
+ * assuming that _obviously_ you wouldn't have caused an overflow
+ * and so they can elide an 'if (i < 0)' test immediately after
+ * the cast.
+ *
+ * Sensible compilers ought of course to optimise this entire
+ * function into 'just return the input value'!
+ */
+ if (u <= (unsigned)INT_MAX)
+ return (int)u;
+ else if (u >= (unsigned)INT_MIN) /* wrap in cast _to_ unsigned is OK */
+ return INT_MIN + (int)(u - (unsigned)INT_MIN);
+ else
+ return INT_MIN; /* fallback; should never occur on binary machines */
+}
+
+/*
+ * 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.
+ *
+ * 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
+#ifdef va_copy
+ /* Use the `va_copy' macro mandated by C99, if present.
+ * XXX some environments may have this as __va_copy() */
+ va_list aq;
+ va_copy(aq, ap);
+ len = vsnprintf(buf, size, fmt, aq);
+ va_end(aq);
+#else
+ /* Ugh. No va_copy macro, so do something nasty.
+ * 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
+ * (indeed, it has been observed to).
+ * XXX the autoconf manual suggests that using memcpy() will give
+ * "maximum portability". */
+ len = vsnprintf(buf, size, fmt, ap);
+#endif
+ 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;
+}
+
+/*
+ * Perl-style 'chomp', for a line we just read with fgetline. Unlike
+ * Perl chomp, however, we're deliberately forgiving of strange
+ * line-ending conventions. Also we forgive NULL on input, so you can
+ * just write 'line = chomp(fgetline(fp));' and not bother checking
+ * for NULL until afterwards.
+ */
+char *chomp(char *str)
+{
+ if (str) {
+ int len = strlen(str);
+ while (len > 0 && (str[len-1] == '\r' || str[len-1] == '\n'))
+ len--;
+ str[len] = '\0';
+ }
+ return str;
+}
+
+/* ----------------------------------------------------------------------
+ * Core base64 encoding and decoding routines.
+ */
+
+void base64_encode_atom(const 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] = '=';
+}
+
+int base64_decode_atom(const char *atom, unsigned char *out)
+{
+ int vals[4];
+ int i, v, len;
+ unsigned word;
+ char c;
+
+ for (i = 0; i < 4; i++) {
+ c = atom[i];
+ if (c >= 'A' && c <= 'Z')
+ v = c - 'A';
+ else if (c >= 'a' && c <= 'z')
+ v = c - 'a' + 26;
+ else if (c >= '0' && c <= '9')
+ v = c - '0' + 52;
+ else if (c == '+')
+ v = 62;
+ else if (c == '/')
+ v = 63;
+ else if (c == '=')
+ v = -1;
+ else
+ return 0; /* invalid atom */
+ vals[i] = v;
+ }
+
+ if (vals[0] == -1 || vals[1] == -1)
+ return 0;
+ if (vals[2] == -1 && vals[3] != -1)
+ return 0;
+
+ if (vals[3] != -1)
+ len = 3;
+ else if (vals[2] != -1)
+ len = 2;
+ else
+ len = 1;
+
+ word = ((vals[0] << 18) |
+ (vals[1] << 12) | ((vals[2] & 0x3F) << 6) | (vals[3] & 0x3F));
+ out[0] = (word >> 16) & 0xFF;
+ if (len > 1)
+ out[1] = (word >> 8) & 0xFF;
+ if (len > 2)
+ out[2] = word & 0xFF;
+ return len;
+}
+
+/* ----------------------------------------------------------------------
+ * Generic routines to deal with send buffers: a linked list of
+ * smallish blocks, with the operations
+ *
+ * - add an arbitrary amount of data to the end of the list
+ * - remove the first N bytes from the list
+ * - return a (pointer,length) pair giving some initial data in
+ * the list, suitable for passing to a send or write system
+ * call
+ * - retrieve a larger amount of initial data from the list
+ * - return the current size of the buffer chain in bytes
+ */
+
+#define BUFFER_MIN_GRANULE 512
+
+struct bufchain_granule {
+ struct bufchain_granule *next;
+ char *bufpos, *bufend, *bufmax;
+};
+
+void bufchain_init(bufchain *ch)
+{
+ ch->head = ch->tail = NULL;
+ ch->buffersize = 0;
+}
+
+void bufchain_clear(bufchain *ch)
+{
+ struct bufchain_granule *b;
+ while (ch->head) {
+ b = ch->head;
+ ch->head = ch->head->next;
+ sfree(b);
+ }
+ ch->tail = NULL;
+ ch->buffersize = 0;
+}
+
+int bufchain_size(bufchain *ch)
+{
+ return ch->buffersize;
+}
+
+void bufchain_add(bufchain *ch, const void *data, int len)
+{
+ const char *buf = (const char *)data;
+
+ if (len == 0) return;
+
+ ch->buffersize += len;
+
+ while (len > 0) {
+ if (ch->tail && ch->tail->bufend < ch->tail->bufmax) {
+ int copylen = min(len, ch->tail->bufmax - ch->tail->bufend);
+ memcpy(ch->tail->bufend, buf, copylen);
+ buf += copylen;
+ len -= copylen;
+ ch->tail->bufend += copylen;
+ }
+ if (len > 0) {
+ int grainlen =
+ max(sizeof(struct bufchain_granule) + len, BUFFER_MIN_GRANULE);
+ struct bufchain_granule *newbuf;
+ newbuf = smalloc(grainlen);
+ newbuf->bufpos = newbuf->bufend =
+ (char *)newbuf + sizeof(struct bufchain_granule);
+ newbuf->bufmax = (char *)newbuf + grainlen;
+ newbuf->next = NULL;
+ if (ch->tail)
+ ch->tail->next = newbuf;
+ else
+ ch->head = newbuf;
+ ch->tail = newbuf;
+ }
+ }
+}
+
+void bufchain_consume(bufchain *ch, int len)
+{
+ struct bufchain_granule *tmp;
+
+ assert(ch->buffersize >= len);
+ while (len > 0) {
+ int remlen = len;
+ assert(ch->head != NULL);
+ if (remlen >= ch->head->bufend - ch->head->bufpos) {
+ remlen = ch->head->bufend - ch->head->bufpos;
+ tmp = ch->head;
+ ch->head = tmp->next;
+ if (!ch->head)
+ ch->tail = NULL;
+ sfree(tmp);
+ } else
+ ch->head->bufpos += remlen;
+ ch->buffersize -= remlen;
+ len -= remlen;
+ }
+}
+
+void bufchain_prefix(bufchain *ch, void **data, int *len)
+{
+ *len = ch->head->bufend - ch->head->bufpos;
+ *data = ch->head->bufpos;
+}
+
+void bufchain_fetch(bufchain *ch, void *data, int len)
+{
+ struct bufchain_granule *tmp;
+ char *data_c = (char *)data;
+
+ tmp = ch->head;
+
+ assert(ch->buffersize >= len);
+ while (len > 0) {
+ int remlen = len;
+
+ assert(tmp != NULL);
+ if (remlen >= tmp->bufend - tmp->bufpos)
+ remlen = tmp->bufend - tmp->bufpos;
+ memcpy(data_c, tmp->bufpos, remlen);
+
+ tmp = tmp->next;
+ len -= remlen;
+ data_c += remlen;
+ }
+}
+
+/* ----------------------------------------------------------------------
+ * My own versions of malloc, realloc and free. Because I want
+ * malloc and realloc to bomb out and exit the program if they run
+ * out of memory, realloc to reliably call malloc if passed a NULL
+ * pointer, and free to reliably do nothing if passed a NULL
+ * pointer. We can also put trace printouts in, if we need to; and
+ * we can also replace the allocator with an ElectricFence-like
+ * one.
+ */
+
+#ifdef MINEFIELD
+void *minefield_c_malloc(size_t size);
+void minefield_c_free(void *p);
+void *minefield_c_realloc(void *p, size_t size);
+#endif