2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A/*
2N/A * k5buf.c
2N/A *
2N/A * Copyright 2008 Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A * Implement the k5buf string buffer module.
2N/A */
2N/A
2N/A/* Can't include krb5.h here, or k5-int.h which includes it, because
2N/A krb5.h needs to be generated with error tables, after util/et,
2N/A which builds after this directory. */
2N/A#include "k5buf-int.h"
2N/A#include <assert.h>
2N/A
2N/A/*
2N/A * Structure invariants:
2N/A *
2N/A * buftype is BUFTYPE_FIXED, BUFTYPE_DYNAMIC, or BUFTYPE_ERROR
2N/A * if buftype is not BUFTYPE_ERROR:
2N/A * space > 0
2N/A * space <= floor(SIZE_MAX / 2) (to fit within ssize_t)
2N/A * len < space
2N/A * data[len] = '\0'
2N/A */
2N/A
2N/A/* Make sure there is room for LEN more characters in BUF, in addition
2N/A to the null terminator and what's already in there. Return true on
2N/A success. On failure, set the error flag and return false. */
2N/Astatic int ensure_space(struct k5buf *buf, size_t len)
2N/A{
2N/A size_t new_space;
2N/A char *new_data;
2N/A
2N/A if (buf->buftype == BUFTYPE_ERROR)
2N/A return 0;
2N/A if (buf->space - 1 - buf->len >= len) /* Enough room already. */
2N/A return 1;
2N/A if (buf->buftype == BUFTYPE_FIXED) /* Can't resize a fixed buffer. */
2N/A goto error_exit;
2N/A assert(buf->buftype == BUFTYPE_DYNAMIC);
2N/A new_space = buf->space * 2;
2N/A while (new_space <= SPACE_MAX && new_space - buf->len - 1 < len)
2N/A new_space *= 2;
2N/A if (new_space > SPACE_MAX)
2N/A goto error_exit;
2N/A new_data = realloc(buf->data, new_space);
2N/A if (new_data == NULL)
2N/A goto error_exit;
2N/A buf->data = new_data;
2N/A buf->space = new_space;
2N/A return 1;
2N/A
2N/Aerror_exit:
2N/A if (buf->buftype == BUFTYPE_DYNAMIC) {
2N/A free(buf->data);
2N/A buf->data = NULL;
2N/A }
2N/A buf->buftype = BUFTYPE_ERROR;
2N/A return 0;
2N/A}
2N/A
2N/Avoid krb5int_buf_init_fixed(struct k5buf *buf, char *data, size_t space)
2N/A{
2N/A assert(space > 0);
2N/A buf->buftype = BUFTYPE_FIXED;
2N/A buf->data = data;
2N/A buf->space = space;
2N/A buf->len = 0;
2N/A buf->data[0] = '\0';
2N/A}
2N/A
2N/Avoid krb5int_buf_init_dynamic(struct k5buf *buf)
2N/A{
2N/A buf->buftype = BUFTYPE_DYNAMIC;
2N/A buf->space = DYNAMIC_INITIAL_SIZE;
2N/A buf->data = malloc(buf->space);
2N/A if (buf->data == NULL) {
2N/A buf->buftype = BUFTYPE_ERROR;
2N/A return;
2N/A }
2N/A buf->len = 0;
2N/A buf->data[0] = '\0';
2N/A}
2N/A
2N/Avoid krb5int_buf_add(struct k5buf *buf, const char *data)
2N/A{
2N/A krb5int_buf_add_len(buf, data, strlen(data));
2N/A}
2N/A
2N/Avoid krb5int_buf_add_len(struct k5buf *buf, const char *data, size_t len)
2N/A{
2N/A if (!ensure_space(buf, len))
2N/A return;
2N/A memcpy(buf->data + buf->len, data, len);
2N/A buf->len += len;
2N/A buf->data[buf->len] = '\0';
2N/A}
2N/A
2N/Avoid krb5int_buf_add_fmt(struct k5buf *buf, const char *fmt, ...)
2N/A{
2N/A va_list ap;
2N/A int r;
2N/A size_t remaining;
2N/A char *tmp;
2N/A
2N/A if (buf->buftype == BUFTYPE_ERROR)
2N/A return;
2N/A remaining = buf->space - buf->len;
2N/A
2N/A if (buf->buftype == BUFTYPE_FIXED) {
2N/A /* Format the data directly into the fixed buffer. */
2N/A va_start(ap, fmt);
2N/A r = vsnprintf(buf->data + buf->len, remaining, fmt, ap);
2N/A va_end(ap);
2N/A if (SNPRINTF_OVERFLOW(r, remaining))
2N/A buf->buftype = BUFTYPE_ERROR;
2N/A else
2N/A buf->len += (unsigned int) r;
2N/A return;
2N/A }
2N/A
2N/A /* Optimistically format the data directly into the dynamic buffer. */
2N/A assert(buf->buftype == BUFTYPE_DYNAMIC);
2N/A va_start(ap, fmt);
2N/A r = vsnprintf(buf->data + buf->len, remaining, fmt, ap);
2N/A va_end(ap);
2N/A if (!SNPRINTF_OVERFLOW(r, remaining)) {
2N/A buf->len += (unsigned int) r;
2N/A return;
2N/A }
2N/A
2N/A if (r >= 0) {
2N/A /* snprintf correctly told us how much space is required. */
2N/A if (!ensure_space(buf, r))
2N/A return;
2N/A remaining = buf->space - buf->len;
2N/A va_start(ap, fmt);
2N/A r = vsnprintf(buf->data + buf->len, remaining, fmt, ap);
2N/A va_end(ap);
2N/A if (SNPRINTF_OVERFLOW(r, remaining)) /* Shouldn't ever happen. */
2N/A buf->buftype = BUFTYPE_ERROR;
2N/A else
2N/A buf->len += (unsigned int) r;
2N/A return;
2N/A }
2N/A
2N/A /* It's a pre-C99 snprintf implementation, or something else went
2N/A wrong. Fall back to asprintf. */
2N/A va_start(ap, fmt);
2N/A r = vasprintf(&tmp, fmt, ap);
2N/A va_end(ap);
2N/A if (r < 0) {
2N/A buf->buftype = BUFTYPE_ERROR;
2N/A return;
2N/A }
2N/A if (ensure_space(buf, r)) {
2N/A /* Copy the temporary string into buf, including terminator. */
2N/A memcpy(buf->data + buf->len, tmp, r + 1);
2N/A buf->len += r;
2N/A }
2N/A free(tmp);
2N/A}
2N/A
2N/Avoid krb5int_buf_truncate(struct k5buf *buf, size_t len)
2N/A{
2N/A if (buf->buftype == BUFTYPE_ERROR)
2N/A return;
2N/A assert(len <= buf->len);
2N/A buf->len = len;
2N/A buf->data[buf->len] = '\0';
2N/A}
2N/A
2N/A
2N/Achar *krb5int_buf_data(struct k5buf *buf)
2N/A{
2N/A return (buf->buftype == BUFTYPE_ERROR) ? NULL : buf->data;
2N/A}
2N/A
2N/Assize_t krb5int_buf_len(struct k5buf *buf)
2N/A{
2N/A return (buf->buftype == BUFTYPE_ERROR) ? -1 : (ssize_t) buf->len;
2N/A}
2N/A
2N/Avoid krb5int_free_buf(struct k5buf *buf)
2N/A{
2N/A if (buf->buftype == BUFTYPE_ERROR)
2N/A return;
2N/A assert(buf->buftype == BUFTYPE_DYNAMIC);
2N/A free(buf->data);
2N/A buf->data = NULL;
2N/A buf->buftype = BUFTYPE_ERROR;
2N/A}