# HG changeset patch # User David Demelier # Date 1331110261 -3600 # Node ID b9723c197038c256cdea08af825a7d100b8b2c4b # Parent 5917096facb9b47685fbd05cdedd5381502725de Add brand new string buffer diff -r 5917096facb9 -r b9723c197038 buf.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/buf.c Wed Mar 07 09:51:01 2012 +0100 @@ -0,0 +1,250 @@ +/* + * buf.c -- easy way to manipulate strings + * + * Copyright (c) 2011, 2012, David Demelier + * + * 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 +#include +#include + +#include "buf.h" + +#define BUF_AVAIL(buf) ((buf)->alsize - (buf)->length - 1) +#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; +} + +/* + * 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); + } + } + + 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)); +} + +/* + * Copy at most max character from str to string buffer. + */ + +int +buf_ncopy(struct buf *buf, const char *str, size_t max) +{ + buf_clear(buf); + + return buf_ncat(buf, str, max); +} + +/* + * Copy the string str to the string buffer. + */ + +int +buf_copy(struct buf *buf, const char *str) +{ + return buf_ncopy(buf, str, strlen(str)); +} + +/* + * Copy 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, done = 0; + + if (BUF_FIXED(buf) && BUF_SAFE(buf)) + return -1; + + buf_clear(buf); + + do { + copied = vsnprintf(buf->text, buf->alsize, fmt, ap); + + if (copied > BUF_AVAIL(buf)) { + 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) + return -1; + } else { + done = 1; + } + } while (!done); + + return 0; +} + +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; +} + +/* + * 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) +{ + buf_clear(buf); + + free(buf->text); + buf->text = NULL; +} + +/* + * 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; + } +} + +/* + * 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; +} diff -r 5917096facb9 -r b9723c197038 buf.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/buf.h Wed Mar 07 09:51:01 2012 +0100 @@ -0,0 +1,63 @@ +/* + * buf.h -- easy way to manipulate strings + * + * Copyright (c) 2011, 2012, David Demelier + * + * 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. + */ + +#ifndef _BUF_H_ +#define _BUF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#ifndef BUF_DEFAULT_BSIZE +#define BUF_DEFAULT_BSIZE 128 +#endif + +enum { + BUF_AUTO = 0, /* string grows automatically */ + BUF_FIXED = (1 << 0), /* fixed size string */ + BUF_UNSAFE = (1 << 1), /* string may be truncated */ +}; + +struct buf { + int flags; /* string flags */ + char *text; /* string text */ + size_t length; /* string length */ + size_t alsize; /* allocated size */ + int bsize; /* block size (used when growing array) */ + + /* Own allocation functions */ + void * (*malloc)(size_t); + void * (*realloc)(void *, size_t); +}; + +int buf_init(struct buf *); +void buf_set(struct buf *, const char *, ...); +int buf_ncat(struct buf *, const char *, size_t); +int buf_cat(struct buf *, const char *); +int buf_ncopy(struct buf *, const char *, size_t); +int buf_copy(struct buf *, const char *); +void buf_clear(struct buf *); +void buf_free(struct buf *); + +#ifdef __cplusplus +} +#endif + +#endif /* _BUF_H_ */