view buf.c @ 164:654f32079cdc

Use enum
author David Demelier <markand@malikania.fr>
date Wed, 05 Sep 2012 07:44:07 +0200
parents 44e7b975618a
children 7f214f26a4c0
line wrap: on
line source

/*
 * buf.c -- easy way to manipulate strings
 *
 * Copyright (c) 2011, 2012, 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 <string.h>

#include "buf.h"

#define BUF_AVAIL(buf)	((buf)->alsize - (buf)->length)
#define BUF_SAFE(buf)	(!((buf)->flags & BUF_UNSAFE))
#define BUF_FIXED(buf)	((buf)->flags & BUF_FIXED)
#define BUF_AUTO(buf)	(buf->flags == 0)

static void	copy(struct buf *, const void *, size_t);
static int	grow(struct buf *, size_t);

int
buf_init(struct buf *buf)
{
	/* Set defaults if needed */
	buf->bsize	= (buf->bsize <= 0) ? BUF_DEFAULT_BSIZE : buf->bsize;
	buf->alsize	= buf->bsize + 1;
	buf->malloc	= (buf->malloc == NULL) ? &malloc : buf->malloc;
	buf->realloc	= (buf->realloc == NULL) ? &realloc : buf->realloc;

	if ((buf->text = buf->malloc(buf->alsize)) == NULL)
		return -1;

	memset(buf->text, 0, buf->alsize);

	return 0;
}

/*
 * Valid options that can be set for a buffer :
 * l -> optional buf block size of type int
 * m -> malloc function that must matches void * (*malloc)(size_t)
 * r -> realloc function that must matches void * (*realloc)(void *, size_t)
 * f -> buffer flags that are OR'ed
 */

void
buf_set(struct buf *buf, const char *fmt, ...)
{
	va_list ap;
	const char *p;

	va_start(ap, fmt);
	for (p = fmt; *p != '\0'; ++p)
		switch (*p) {
		case 'l':
			buf->bsize = va_arg(ap, int);
			break;
		case 'm':
			buf->malloc = va_arg(ap, void *(*)(size_t));
			break;
		case 'r':
			buf->realloc = va_arg(ap, void *(*)(void *, size_t));
			break;
		case 'f':
		case 't':
			buf->flags = va_arg(ap, int);
			break;
		default:
			break;
		}
}

/*
 * This function appends not more than max characters from str.
 */

int
buf_ncat(struct buf *buf, const char *str, size_t max)
{
	size_t tocopy = max;

	for (tocopy = 0; str[tocopy] != '\0' && tocopy < max; ++tocopy)
		continue;

	if (BUF_AVAIL(buf) <= tocopy) {
		/* Can't add more */
		if (BUF_SAFE(buf) && BUF_FIXED(buf))
			return -1;

		if (BUF_AUTO(buf)) {
			if (grow(buf, tocopy) < 0)
				return -1;
		} else {
			/* String is unsafe, truncate to the available size */
			tocopy = BUF_AVAIL(buf) - 1;
		}
	}

	copy(buf, str, tocopy);
	
	return 0;
}

/*
 * Append the string str to the end of the string buffer.
 */

int
buf_cat(struct buf *buf, const char *str)
{
	return buf_ncat(buf, str, strlen(str));
}

/*
 * Append the caracter c to the end of buffer
 */

int
buf_putc(struct buf *buf, int c)
{
	char str[2] = { c, '\0' };

	return buf_ncat(buf, str, 1);
}

/*
 * Concatenate the printf(3) like call to the string buffer, this function
 * returns -1 on fixed safe buffer, otherwise 0 is returned if there
 * is no allocation failure.
 */

int
buf_vprintf(struct buf *buf, const char *fmt, va_list ap)
{
	int copied, rv = 0, done = 0;

	if (BUF_FIXED(buf) && BUF_SAFE(buf))
		return -1;

	do {
		copied = vsnprintf(&buf->text[buf->length],
		    BUF_AVAIL(buf), fmt, ap);

		if (copied >= (signed int)BUF_AVAIL(buf) || copied == -1) {
			if (BUF_FIXED(buf))
				done = 1;

			/*
			 * vsnprintf returns -1 on windows, we need to grow
			 * the buffer by block size.
			 */
			if (grow(buf, buf->alsize + buf->bsize) < 0) {
				done = 1;
				rv = -1;
			}
		} else {
			done = 1;
		}
	} while (!done);

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

	return rv;
}

int
buf_printf(struct buf *buf, const char *fmt, ...)
{
	va_list ap;
	int rv;

	va_start(ap, fmt);
	rv = buf_vprintf(buf, fmt, ap);
	va_end(ap);

	return rv;
}

/*
 * Realloc the string to it's size and remove useless bytes.
 */

int
buf_trim(struct buf *buf)
{
	if ((buf->text = realloc(buf->text, buf->length + 1)) == NULL) {
		buf->alsize = 0;
		return -1;
	}

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

	return 0;
}

/*
 * Remove `n' characters from the buffer, a positive value will cut the string
 * from beginning, negative value will cut from end.
 */

void
buf_cut(struct buf *buf, int start)
{
	if ((start > 0 && start >= buf->length) ||
	    (start < 0 && (int)buf->length + start < 0))
		return;

	if (start < 0 && (int)buf->length + start >= 0)
		start = buf->length + start;

	buf->text[start]	= '\0';
	buf->length		-= buf->length - start;
}

/*
 * Clear the string buffer.
 */

void
buf_clear(struct buf *buf)
{
	memset(buf->text, 0, buf->alsize);

	buf->length = 0;
}

void
buf_free(struct buf *buf)
{
	if (buf != NULL) {
		buf_clear(buf);
		free(buf->text);

		buf->text = NULL;
		buf->alsize = 0;
	}
}

/*
 * Append to the end of buffer the void ptr of count size.
 */

static void
copy(struct buf *buf, const void *ptr, size_t count)
{
	memcpy(buf->text + buf->length, ptr, count);

	buf->text[buf->length + count] = '\0';
	buf->length += count;
}

/*
 * Grow the text buffer until the available size fit the needed
 * size. This function may return -1 on allocation failure.
 */

static int
grow(struct buf *buf, size_t needed)
{
	while (BUF_AVAIL(buf) <= needed) {
		buf->text = buf->realloc(buf->text, buf->alsize + buf->bsize);
	
		if (buf->text == NULL)
			return -1;

		buf->alsize += buf->bsize;
	}

	return 0;
}