diff options
Diffstat (limited to '')
-rw-r--r-- | lib/buffer.c | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/lib/buffer.c b/lib/buffer.c new file mode 100644 index 0000000..1dba7dd --- /dev/null +++ b/lib/buffer.c @@ -0,0 +1,289 @@ +/* + * No copyright is claimed. This code is in the public domain; do with + * it what you wish. + * + * Written by Karel Zak <kzak@redhat.com> + */ +#include "buffer.h" +#include "mbsalign.h" + +void ul_buffer_reset_data(struct ul_buffer *buf) +{ + if (buf->begin) + buf->begin[0] = '\0'; + buf->end = buf->begin; + + if (buf->ptrs && buf->nptrs) + memset(buf->ptrs, 0, buf->nptrs * sizeof(char *)); +} + +void ul_buffer_free_data(struct ul_buffer *buf) +{ + assert(buf); + + free(buf->begin); + buf->begin = NULL; + buf->end = NULL; + buf->sz = 0; + + free(buf->ptrs); + buf->ptrs = NULL; + buf->nptrs = 0; + + free(buf->encoded); + buf->encoded = NULL; + buf->encoded_sz = 0; +} + +void ul_buffer_set_chunksize(struct ul_buffer *buf, size_t sz) +{ + buf->chunksize = sz; +} + +int ul_buffer_is_empty(struct ul_buffer *buf) +{ + return buf->begin == buf->end; +} + +int ul_buffer_save_pointer(struct ul_buffer *buf, unsigned short ptr_idx) +{ + if (ptr_idx >= buf->nptrs) { + char **tmp = realloc(buf->ptrs, (ptr_idx + 1) * sizeof(char *)); + + if (!tmp) + return -EINVAL; + buf->ptrs = tmp; + buf->nptrs = ptr_idx + 1; + } + + buf->ptrs[ptr_idx] = buf->end; + return 0; +} + + +char *ul_buffer_get_pointer(struct ul_buffer *buf, unsigned short ptr_idx) +{ + if (ptr_idx < buf->nptrs) + return buf->ptrs[ptr_idx]; + return NULL; +} + +/* returns length from begin to the pointer */ +size_t ul_buffer_get_pointer_length(struct ul_buffer *buf, unsigned short ptr_idx) +{ + char *ptr = ul_buffer_get_pointer(buf, ptr_idx); + + if (ptr && ptr > buf->begin) + return ptr - buf->begin; + return 0; +} + +/* returns width of data in safe encoding (from the begin to the pointer) */ +size_t ul_buffer_get_safe_pointer_width(struct ul_buffer *buf, unsigned short ptr_idx) +{ + size_t len = ul_buffer_get_pointer_length(buf, ptr_idx); + + if (!len) + return 0; + + return mbs_safe_nwidth(buf->begin, len, NULL); +} + +void ul_buffer_refer_string(struct ul_buffer *buf, char *str) +{ + if (buf->sz) + ul_buffer_free_data(buf); + buf->begin = str; + buf->sz = str ? strlen(str) : 0; + buf->end = buf->begin ? buf->begin + buf->sz : buf->begin; +} + +int ul_buffer_alloc_data(struct ul_buffer *buf, size_t sz) +{ + char *tmp; + size_t len = 0; + + assert(buf); + + if (sz <= buf->sz) + return 0; + + if (buf->end && buf->begin) + len = buf->end - buf->begin; + + if (buf->chunksize) + sz = ((sz + buf->chunksize) / buf->chunksize) * buf->chunksize + 1; + + tmp = realloc(buf->begin, sz); + if (!tmp) + return -ENOMEM; + + buf->begin = tmp; + buf->end = buf->begin + len; + buf->sz = sz; + + memset(buf->end, '\0', sz - len); + + return 0; +} + +int ul_buffer_append_data(struct ul_buffer *buf, const char *data, size_t sz) +{ + size_t maxsz = 0; + + if (!buf) + return -EINVAL; + if (!data || !*data) + return 0; + + if (buf->begin && buf->end) + maxsz = buf->sz - (buf->end - buf->begin); + + if (maxsz <= sz + 1) { + int rc = ul_buffer_alloc_data(buf, buf->sz + sz + 1); + if (rc) + return rc; + } + if (!buf->end) + return -EINVAL; /* make static analyzers happy */ + + memcpy(buf->end, data, sz); + buf->end += sz; + *buf->end = '\0'; /* make sure it's terminated */ + return 0; +} + +int ul_buffer_append_string(struct ul_buffer *buf, const char *str) +{ + if (!str) + return 0; + + return ul_buffer_append_data(buf, str, strlen(str)); +} + +int ul_buffer_append_ntimes(struct ul_buffer *buf, size_t n, const char *str) +{ + size_t i; + size_t len = strlen(str); + + for (i = 0; len && i < n; i++) { + int rc = ul_buffer_append_data(buf, str, len); + if (rc) + return rc; + } + return 0; +} + +int ul_buffer_set_data(struct ul_buffer *buf, const char *data, size_t sz) +{ + ul_buffer_reset_data(buf); + return ul_buffer_append_data(buf, data, sz); +} + +char *ul_buffer_get_data(struct ul_buffer *buf, size_t *sz, size_t *width) +{ + if (sz) + *sz = buf->end - buf->begin; + if (width) + *width = buf->begin && *buf->begin ? mbs_width(buf->begin) : 0; + return buf->begin; +} + +/* size of allocated area (!= size of stored data */ +size_t ul_buffer_get_bufsiz(struct ul_buffer *buf) +{ + return buf->sz; +} + +/* encode data by mbs_safe_encode() to avoid control and non-printable chars */ +char *ul_buffer_get_safe_data(struct ul_buffer *buf, size_t *sz, size_t *width, const char *safechars) +{ + char *data = ul_buffer_get_data(buf, NULL, NULL); + size_t encsz, wsz = 0; + char *res = NULL; + + if (!data) + goto nothing; + + encsz = mbs_safe_encode_size(buf->sz) + 1; + if (encsz > buf->encoded_sz) { + char *tmp = realloc(buf->encoded, encsz); + if (!tmp) + goto nothing; + buf->encoded = tmp; + buf->encoded_sz = encsz; + } + + res = mbs_safe_encode_to_buffer(data, &wsz, buf->encoded, safechars); + if (!res || !wsz || wsz == (size_t) -1) + goto nothing; + + if (width) + *width = wsz; + if (sz) + *sz = strlen(res); + return res; +nothing: + if (width) + *width = 0; + if (sz) + *sz = 0; + return NULL; +} + + +#ifdef TEST_PROGRAM_BUFFER + +enum { + PTR_AAA = 0, + PTR_BBB, +}; + +int main(void) +{ + struct ul_buffer buf = UL_INIT_BUFFER; + char *str; + size_t sz = 0; + + ul_buffer_set_chunksize(&buf, 16); + + ul_buffer_append_string(&buf, "AAA"); + ul_buffer_append_data(&buf, "=", 1); + ul_buffer_append_string(&buf, "aaa"); + ul_buffer_save_pointer(&buf, PTR_AAA); + + ul_buffer_append_data(&buf, ",", 1); + ul_buffer_append_string(&buf, "BBB"); + ul_buffer_append_string(&buf, "="); + ul_buffer_append_string(&buf, "bbb"); + ul_buffer_save_pointer(&buf, PTR_BBB); + + str = ul_buffer_get_data(&buf, &sz, NULL); + printf("data [%zu] '%s'\n", sz, str); + + printf(" pointer data len: AAA=%zu, BBB=%zu\n", + ul_buffer_get_pointer_length(&buf, PTR_AAA), + ul_buffer_get_pointer_length(&buf, PTR_BBB)); + printf(" pointer data width: AAA=%zu, BBB=%zu\n", + ul_buffer_get_safe_pointer_width(&buf, PTR_AAA), + ul_buffer_get_safe_pointer_width(&buf, PTR_BBB)); + + ul_buffer_reset_data(&buf); + ul_buffer_append_string(&buf, "This is really long string to test the buffer function."); + ul_buffer_save_pointer(&buf, PTR_AAA); + ul_buffer_append_string(&buf, " YES!"); + str = ul_buffer_get_data(&buf, &sz, NULL); + printf("data [%zu] '%s'\n", sz, str); + printf(" pointer data len: AAA=%zu\n", ul_buffer_get_pointer_length(&buf, PTR_AAA)); + + ul_buffer_free_data(&buf); + str = strdup("foo"); + ul_buffer_refer_string(&buf, str); + ul_buffer_append_data(&buf, ",", 1); + ul_buffer_append_string(&buf, "bar"); + str = ul_buffer_get_data(&buf, &sz, NULL); + printf("data [%zu] '%s'\n", sz, str); + + ul_buffer_free_data(&buf); +} +#endif /* TEST_PROGRAM_BUFFER */ |