mirror of https://gerrit.osmocom.org/asn1c
172 lines
3.9 KiB
C
172 lines
3.9 KiB
C
#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
|