--- /dev/null
+#define _GNU_SOURCE
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <assert.h>
+#include "config.h"
+
+#include "asn1_buffer.h"
+
+#if !defined(HAVE_DECL_VASPRINTF) || (HAVE_DECL_VASPRINTF == 0)
+int vasprintf(char **ret, const char *fmt, va_list args);
+#endif
+
+/*
+ * Create and destroy the buffer.
+ */
+abuf *
+abuf_new() {
+ abuf *ab = calloc(1, sizeof(abuf));
+ assert(ab);
+ ab->length = 0;
+ ab->size = 32;
+ ab->buffer = calloc(1, ab->size);
+ assert(ab->buffer);
+ return ab;
+}
+
+void abuf_free(abuf *ab) {
+ if(ab) {
+ union {
+ const char *c_buf;
+ char *nc_buf;
+ } const_cast;
+ const_cast.c_buf = ab->buffer;
+ free(const_cast.nc_buf);
+ free(ab);
+ }
+}
+
+/*
+ * Erase contents of the buffer (without destroying it).
+ */
+void
+abuf_clear(abuf *ab) {
+ union {
+ const char *c_buf;
+ char *nc_buf;
+ } const_cast;
+ if(!ab->buffer) {
+ ab->size = 32;
+ ab->buffer = calloc(1, ab->size);
+ assert(ab->buffer);
+ }
+ const_cast.c_buf = ab->buffer;
+ ab->length = 0;
+ const_cast.nc_buf[0] = '\0';
+}
+
+static void
+abuf_resize_by(abuf *ab, size_t size) {
+ union {
+ const char *c_buf;
+ char *nc_buf;
+ } const_cast;
+ const_cast.c_buf = ab->buffer;
+
+ assert(!ab->buffer || ab->buffer[ab->length] == '\0');
+
+ size_t new_size = ab->length + size;
+ char *p = realloc(const_cast.nc_buf, new_size);
+ assert(p);
+ if(!ab->buffer) {
+ assert(ab->length == 0);
+ *p = '\0';
+ }
+ ab->buffer = p;
+ assert(ab->buffer[ab->length] == '\0');
+ ab->size = new_size;
+}
+
+void abuf_add_bytes(abuf *ab, const char *str, size_t size) {
+ abuf_resize_by(ab, size + 1);
+ union {
+ const char *c_buf;
+ char *nc_buf;
+ } const_cast;
+ const_cast.c_buf = ab->buffer;
+ memcpy(&const_cast.nc_buf[ab->length], str, size);
+ ab->length += size;
+ const_cast.nc_buf[ab->length] = '\0';
+}
+
+void abuf_str(abuf *ab, const char *str) {
+ abuf_add_bytes(ab, str, strlen(str));
+}
+
+void abuf_buf(abuf *ab, const abuf *buf) {
+ abuf_add_bytes(ab, buf->buffer, buf->length);
+}
+
+int abuf_printf(abuf *ab, const char *fmt, ...) {
+ va_list ap;
+
+ for(;;) {
+ union {
+ const char *c_buf;
+ char *nc_buf;
+ } const_cast;
+ const_cast.c_buf = ab->buffer;
+ va_start(ap, fmt);
+ int ret = vsnprintf(&const_cast.nc_buf[ab->length],
+ ab->size - ab->length, fmt, ap);
+ va_end(ap);
+ assert(ret >= 0);
+ if((size_t)ret < ab->size - ab->length) {
+ ab->length += ret;
+ assert(ab->buffer[ab->length] == '\0');
+ return ret;
+ }
+ const_cast.nc_buf[ab->length] = '\0'; /* Restore order */
+ abuf_resize_by(ab, ret + 1);
+ }
+}
+
+int abuf_vprintf(abuf *ab, const char *fmt, va_list ap) {
+ int ret;
+ char *str = 0;
+
+ ret = vasprintf(&str, fmt, ap);
+ assert(ret >= 0);
+ assert(str != NULL);
+
+ abuf_add_bytes(ab, str, ret);
+
+ free(str);
+
+ return ret;
+}
+
+#if !defined(HAVE_DECL_VASPRINTF) || (HAVE_DECL_VASPRINTF == 0)
+/* Solaris doesn't have vasprintf(3). */
+int
+vasprintf(char **ret, const char *fmt, va_list args) {
+ int actual_length = -1;
+ va_list copy;
+ va_copy(copy, args);
+
+ int suggested = vsnprintf(NULL, 0, fmt, args);
+ if(suggested >= 0) {
+ *ret = malloc(suggested + 1);
+ if(*ret) {
+ int actual_length = vsnprintf(*ret, suggested + 1, fmt, copy);
+ if(actual_length >= 0) {
+ assert(actual_length == suggested);
+ assert((*ret)[actual_length] == '\0');
+ } else {
+ free(*ret);
+ *ret = 0;
+ }
+ }
+ } else {
+ *ret = NULL;
+ assert(suggested >= 0); /* Can't function like this */
+ }
+ va_end(args);
+
+ return actual_length;
+}
+#endif