view buffer.c @ 25:e09000fc013a

Switch to void * thus the user do not need to cast
author David Demelier <markand@malikania.fr>
date Thu, 15 Sep 2011 17:53:43 +0200
parents ae4128d16c92
children 904a373aa120
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));
	buf->bsize	= (bsize == 0) ? BUFFER_DEFAULT_BSIZE : bsize;
	buf->size	= bsize + 1;
	buf->flags	= flags;

	if (!(buf->data = calloc(bsize + 1, 1)) ||
	    (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_AUTO))
		length = buf->size - buf->length - 1;

	strncpy(buf->data + buf->length, 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;

	if (!(buf->flags & BUFFER_AUTO) && buf->size - buf->length - 1 <= 2)
		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_AUTO) && 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)
{
	size_t newlen;

	if ((buf->size - buf->length) > needed)
		return 0;

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

		if (!(buf->data = realloc(buf->data, newlen)))
			return -1;

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

	return 0;
}