view buffer.c @ 4:e19a50d6a5be

Fix security if using bad flags
author David Demelier <markand@malikania.fr>
date Tue, 06 Sep 2011 18:55:19 +0200
parents 22d7bb03e569
children 0ed27735fa87
line wrap: on
line source

/*
 * buffer.c -- safe unlimited size string
 *
 * Copyright (c) 2011, David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

#include "buffer.h"

static int	buffer_grow(struct buffer *, size_t);

/*
 * Initialize a new buffer with the optional `str' as begin of buffer.
 */

struct buffer *
buffer_new(const char *str, size_t bsize, int flags)
{
	struct buffer *buf;

	if (!(buf = malloc(sizeof (struct buffer))))
		return NULL;

	memset(buf, 0, sizeof (struct buffer));

	if (bsize == 0)
		bsize = BUFFER_DEFAULT_BSIZE;

	if (!(buf->data = calloc(bsize + 1, 1))) {
		free(buf);
		return NULL;
	}

	buf->size	= bsize + 1;
	buf->bsize	= bsize;
	buf->flags	= flags;

	if (str && buffer_strcat(buf, str) < 0) {
		free(buf);
		return NULL;
	}

	return buf;
}

/*
 * Concatenate the string into the buffer.
 */

int
buffer_strcat(struct buffer *buf, const char *str)
{
	size_t length;

	length = strlen(str);
	if (buffer_grow(buf, length) < 0)
		return -1;

	if (buf->flags & BUFFER_FIXED)
		length = buf->size - buf->length - 1;

	strncat(buf->data, str, length);
	buf->length = strlen(buf->data);

	return 0;
}

/*
 * Append the character c to the end of buffer.
 */

int
buffer_putc(struct buffer *buf, char c)
{
	if (buffer_grow(buf, 1) < 0)
		return -1;

	buf->data[buf->length++]	= c;
	buf->data[buf->length]		= '\0';

	return 0;
}

/*
 * Concatenate the byte pointer into the buffer.
 */

int
buffer_bcat(struct buffer *buf, const void *data, size_t size)
{
	if (buffer_grow(buf, size) < 0)
		return -1;

	/* Do not truncate void pointer */
	if (buf->flags & BUFFER_FIXED && size > (buf->size - buf->length - 1))
		return -1;

	memcpy(buf->data + buf->length, data, size);
	buf->length += size;

	return 0;
}

/*
 * Print into the buffer using va_list from stdarg.h
 */

int
buffer_vprintf(struct buffer *buf, const char *fmt, va_list ap)
{
	va_list ap_save;
	int nb, result = 1;

	/*
	 * vsnprintf on windows returns -1 instead of the number of bytes that
	 * should be printed...
	 */
	if (buf->flags & BUFFER_AUTO) {
		do {
			va_copy(ap_save, ap);
			nb = vsnprintf(buf->data + buf->length,
			    buf->size - buf->length - 1, fmt, ap_save);

			if (nb == -1 || nb >= buf->size - buf->length - 1) {
				if (buffer_grow(buf, buf->size + buf->bsize))
					result = -1;
			} else
				result = 0;
		} while (result == 1);

		va_end(ap_save);
	} else {
		result = 0;
		vsnprintf(buf->data + buf->length, buf->size - buf->length - 1,
		    fmt, ap);
	}

	buf->length = strlen(buf->data);

	return result;
}

/*
 * Use printf(3) functions like to write into the buffer.
 */

int
buffer_printf(struct buffer *buf, const char *fmt, ...)
{
	va_list ap;
	int status;

	va_start(ap, fmt);
	status = buffer_vprintf(buf, fmt, ap);
	va_end(ap);

	return status;
}

/*
 * Reduce the data buffer to the exact size. Returns -1 on failure.
 */

int
buffer_shrink(struct buffer *buf)
{
	if (!(buf->data = realloc(buf->data, buf->length + 1)))
		return -1;

	buf->size = buf->length + 1;

	return 0;
}

/*
 * Returns the buffer data and then free the buffer object.
 */

char *
buffer_end(struct buffer *buf)
{
	char *data;

	data = buf->data;
	free(buf);

	return data;
}

/*
 * Reset the struct buffer.
 */

void
buffer_clear(struct buffer *buf)
{
	if (buf->data)
		free(buf->data);

	buf->length	= 0;
	buf->size	= 0;
}

void
buffer_free(struct buffer *buf)
{
	buffer_clear(buf);
	free(buf);
}

/*
 * Functions that tries to extend the buffer to the `needed' size if the buffer
 * allows it. If the buffer is fixed size and there is not enough space the
 * functions returns -1 otherwise 0 is returned.
 */

static int
buffer_grow(struct buffer *buf, size_t needed)
{
	if ((buf->size - buf->length) > needed)
		return 0;

	if (buf->flags & BUFFER_AUTO) {
		while (buf->size - buf->length - 1 < needed)
			buf->size += buf->bsize;

		if (!(buf->data = realloc(buf->data, buf->size)))
			return -1;
	} else
		return (buf->bsize == buf->length) ? -1 : 0;

	memset(buf->data + buf->length, 0, buf->size - buf->length);

	return 0;
}