#define _GNU_SOURCE #include #include #include #include #include #include #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