diff options
Diffstat (limited to 'src/util/support/k5buf.c')
| -rw-r--r-- | src/util/support/k5buf.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/src/util/support/k5buf.c b/src/util/support/k5buf.c new file mode 100644 index 000000000000..f619f6a489ba --- /dev/null +++ b/src/util/support/k5buf.c @@ -0,0 +1,243 @@ +/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ +/* util/support/k5buf.c - string buffer functions */ + +/* + * Copyright 2008 Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. Furthermore if you modify this software you must label + * your software as modified software and not distribute it in such a + * fashion that it might be confused with the original M.I.T. software. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + */ + +/* + * Can't include krb5.h here, or k5-int.h which includes it, because krb5.h + * needs to be generated with error tables, after util/et, which builds after + * this directory. + */ +#include "k5-platform.h" +#include "k5-buf.h" +#include <assert.h> + +/* + * Structure invariants: + * + * buftype is K5BUF_FIXED, K5BUF_DYNAMIC, or K5BUF_ERROR + * if buftype is K5BUF_ERROR, the other fields are NULL or 0 + * if buftype is not K5BUF_ERROR: + * space > 0 + * len < space + * data[len] = '\0' + */ + +/* Return a character pointer to the current end of buf. */ +static inline char * +endptr(struct k5buf *buf) +{ + return (char *)buf->data + buf->len; +} + +static inline void +set_error(struct k5buf *buf) +{ + buf->buftype = K5BUF_ERROR; + buf->data = NULL; + buf->space = buf->len = 0; +} + +/* + * Make sure there is room for LEN more characters in BUF, in addition to the + * null terminator and what's already in there. Return true on success. On + * failure, set the error flag and return false. + */ +static int +ensure_space(struct k5buf *buf, size_t len) +{ + size_t new_space; + char *new_data; + + if (buf->buftype == K5BUF_ERROR) + return 0; + if (buf->space - 1 - buf->len >= len) /* Enough room already. */ + return 1; + if (buf->buftype == K5BUF_FIXED) /* Can't resize a fixed buffer. */ + goto error_exit; + assert(buf->buftype == K5BUF_DYNAMIC); + new_space = buf->space * 2; + while (new_space - buf->len - 1 < len) { + if (new_space > SIZE_MAX / 2) + goto error_exit; + new_space *= 2; + } + new_data = realloc(buf->data, new_space); + if (new_data == NULL) + goto error_exit; + buf->data = new_data; + buf->space = new_space; + return 1; + +error_exit: + if (buf->buftype == K5BUF_DYNAMIC) + free(buf->data); + set_error(buf); + return 0; +} + +void +k5_buf_init_fixed(struct k5buf *buf, char *data, size_t space) +{ + assert(space > 0); + buf->buftype = K5BUF_FIXED; + buf->data = data; + buf->space = space; + buf->len = 0; + *endptr(buf) = '\0'; +} + +void +k5_buf_init_dynamic(struct k5buf *buf) +{ + buf->buftype = K5BUF_DYNAMIC; + buf->space = 128; + buf->data = malloc(buf->space); + if (buf->data == NULL) { + set_error(buf); + return; + } + buf->len = 0; + *endptr(buf) = '\0'; +} + +void +k5_buf_add(struct k5buf *buf, const char *data) +{ + k5_buf_add_len(buf, data, strlen(data)); +} + +void +k5_buf_add_len(struct k5buf *buf, const void *data, size_t len) +{ + if (!ensure_space(buf, len)) + return; + if (len > 0) + memcpy(endptr(buf), data, len); + buf->len += len; + *endptr(buf) = '\0'; +} + +void +k5_buf_add_fmt(struct k5buf *buf, const char *fmt, ...) +{ + va_list ap; + int r; + size_t remaining; + char *tmp; + + if (buf->buftype == K5BUF_ERROR) + return; + remaining = buf->space - buf->len; + + if (buf->buftype == K5BUF_FIXED) { + /* Format the data directly into the fixed buffer. */ + va_start(ap, fmt); + r = vsnprintf(endptr(buf), remaining, fmt, ap); + va_end(ap); + if (SNPRINTF_OVERFLOW(r, remaining)) + set_error(buf); + else + buf->len += (unsigned int) r; + return; + } + + /* Optimistically format the data directly into the dynamic buffer. */ + assert(buf->buftype == K5BUF_DYNAMIC); + va_start(ap, fmt); + r = vsnprintf(endptr(buf), remaining, fmt, ap); + va_end(ap); + if (!SNPRINTF_OVERFLOW(r, remaining)) { + buf->len += (unsigned int) r; + return; + } + + if (r >= 0) { + /* snprintf correctly told us how much space is required. */ + if (!ensure_space(buf, r)) + return; + remaining = buf->space - buf->len; + va_start(ap, fmt); + r = vsnprintf(endptr(buf), remaining, fmt, ap); + va_end(ap); + if (SNPRINTF_OVERFLOW(r, remaining)) /* Shouldn't ever happen. */ + k5_buf_free(buf); + else + buf->len += (unsigned int) r; + return; + } + + /* It's a pre-C99 snprintf implementation, or something else went wrong. + * Fall back to asprintf. */ + va_start(ap, fmt); + r = vasprintf(&tmp, fmt, ap); + va_end(ap); + if (r < 0) { + k5_buf_free(buf); + return; + } + if (ensure_space(buf, r)) { + /* Copy the temporary string into buf, including terminator. */ + memcpy(endptr(buf), tmp, r + 1); + buf->len += r; + } + free(tmp); +} + +void * +k5_buf_get_space(struct k5buf *buf, size_t len) +{ + if (!ensure_space(buf, len)) + return NULL; + buf->len += len; + *endptr(buf) = '\0'; + return endptr(buf) - len; +} + +void +k5_buf_truncate(struct k5buf *buf, size_t len) +{ + if (buf->buftype == K5BUF_ERROR) + return; + assert(len <= buf->len); + buf->len = len; + *endptr(buf) = '\0'; +} + +int +k5_buf_status(struct k5buf *buf) +{ + return (buf->buftype == K5BUF_ERROR) ? ENOMEM : 0; +} + +void +k5_buf_free(struct k5buf *buf) +{ + if (buf->buftype == K5BUF_ERROR) + return; + assert(buf->buftype == K5BUF_DYNAMIC); + free(buf->data); + set_error(buf); +} |
