Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
346 lines (262 sloc) 6.3 KB
/*
* strbuf.c: A simple string buffer
*
* Copyright (c) 2006-2009 Dennis Stosberg <dennis@stosberg.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License,
* version 2 as published by the Free Software Foundation
*/
#include "strbuf.h"
static const size_t strbuf_start_sz = 128;
static const size_t strbuf_grow_sz = 128;
static void strbuf_grow(STRBUF *buf); /* enlarge a buffer by strbuf_grow_sz */
#ifdef STRBUF_CHECK
static void die(const char *format, ...) {
va_list argp;
va_start(argp, format);
vfprintf(stderr, format, argp);
va_end(argp);
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
}
static void strbuf_check(STRBUF *buf)
{
if (!buf)
die("buf is null");
if (!buf->data)
die("buf->data is null");
if (!(buf->opt & STRBUF_NULLOK) && strlen(buf->data) != buf->len)
die("length mismatch. strlen says %u, len says %u",
(unsigned int)strlen(buf->data), buf->len);
if (buf->len + 1 > buf->buf_sz)
die("overlap");
}
#else
#define strbuf_check(a)
#endif
STRBUF *strbuf_new(void)
{
STRBUF *buf = ymalloc(sizeof(STRBUF));
buf->buf_sz = strbuf_start_sz;
buf->data = ymalloc(strbuf_start_sz);
buf->len = 0;
buf->data[0] = '\0';
buf->opt = 0;
strbuf_check(buf);
return buf;
}
void strbuf_free(STRBUF *buf)
{
strbuf_check(buf);
yfree(buf->data);
yfree(buf);
}
void strbuf_shrink(STRBUF *buf)
{
strbuf_check(buf);
buf->buf_sz = buf->len + 1;
buf->data = yrealloc(buf->data, buf->buf_sz);
strbuf_check(buf);
}
size_t strbuf_append_n(STRBUF *buf, const char *str, size_t n)
{
strbuf_check(buf);
if (n == 0)
return buf->len;
while (buf->len + n + 1 > buf->buf_sz)
strbuf_grow(buf);
memcpy(buf->data + buf->len, str, n);
buf->len += n;
*(buf->data + buf->len) = 0;
strbuf_check(buf);
return buf->len;
}
size_t strbuf_append(STRBUF *buf, const char *str)
{
return strbuf_append_n(buf, str, strlen(str));
}
const char *strbuf_get(STRBUF *buf)
{
strbuf_check(buf);
return buf->data;
}
size_t strbuf_len(STRBUF *buf)
{
strbuf_check(buf);
return buf->len;
}
int strbuf_subst(STRBUF *buf,
size_t start, size_t stop,
const char *subst)
{
size_t len;
size_t subst_len;
int diff;
strbuf_check(buf);
if (start > stop) {
size_t tmp = start;
start = stop;
stop = tmp;
}
len = stop - start;
subst_len = strlen(subst);
diff = subst_len - len;
if (0 > diff) {
memcpy(buf->data + start, subst, subst_len);
memmove(buf->data + start + subst_len, buf->data + stop,
buf->len - stop + 1);
} else if (0 == diff) {
memcpy(buf->data + start, subst, subst_len);
} else { /* 0 < diff */
while (buf->len + diff + 1 > buf->buf_sz)
strbuf_grow(buf);
memmove(buf->data + start + subst_len, buf->data + stop,
buf->len - stop + 1);
memcpy(buf->data + start, subst, subst_len);
}
buf->len += diff;
strbuf_check(buf);
return diff;
}
size_t strbuf_append_file(STRBUF *buf, FILE *in)
{
strbuf_check(buf);
/* save NULLOK flag */
int nullok = (buf->opt & STRBUF_NULLOK) ? 1 : 0;
strbuf_setopt(buf, STRBUF_NULLOK);
size_t len = 0;
size_t read_len = 0;
char readbuf[1024];
do {
read_len = fread(readbuf, 1, sizeof(readbuf), in);
len += read_len;
if (read_len > 0) {
while (buf->buf_sz < buf->len + sizeof(readbuf))
strbuf_grow(buf);
memcpy(buf->data + buf->len, readbuf, read_len);
buf->len += read_len;
}
} while (read_len == sizeof(readbuf));
/* terminate buffer */
if (buf->len + 1 > buf->buf_sz)
strbuf_grow(buf);
*(buf->data + buf->len) = '\0';
/* restore NULLOK option */
if (!nullok)
strbuf_unsetopt(buf, STRBUF_NULLOK);
strbuf_check(buf);
return len;
}
size_t strbuf_append_inflate(STRBUF *buf, FILE *in)
{
size_t len;
z_stream strm;
Bytef readbuf[1024];
int z_ret;
int nullok;
strbuf_check(buf);
/* save NULLOK flag */
nullok = (buf->opt & STRBUF_NULLOK) ? 1 : 0;
strbuf_setopt(buf, STRBUF_NULLOK);
/* zlib init */
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.next_in = Z_NULL;
strm.avail_in = 0;
z_ret = inflateInit2(&strm, -15);
if (z_ret != Z_OK) {
fprintf(stderr, "A: zlib returned error: %d\n", z_ret);
exit(EXIT_FAILURE);
}
do {
int f_err;
strm.avail_in = (uInt)fread(readbuf, 1, sizeof(readbuf), in);
f_err = ferror(in);
if (f_err) {
(void)inflateEnd(&strm);
fprintf(stderr, "stdio error: %d\n", f_err);
exit(EXIT_FAILURE); /* TODO: errmsg? continue? */
}
if (strm.avail_in == 0)
break;
strm.next_in = readbuf;
do {
size_t bytes_inflated;
while (buf->buf_sz < buf->len + sizeof(readbuf) * 2)
strbuf_grow(buf);
strm.next_out = (Bytef*)(buf->data + buf->len);
strm.avail_out = (uInt)(buf->buf_sz - buf->len);
z_ret = inflate(&strm, Z_SYNC_FLUSH);
switch (z_ret) {
case Z_NEED_DICT:
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
fprintf(stderr, "B: zlib returned error: %d\n", z_ret);
exit(EXIT_FAILURE);
}
bytes_inflated = (buf->buf_sz - buf->len) - strm.avail_out;
buf->len += bytes_inflated;
} while (strm.avail_out == 0);
} while (z_ret != Z_STREAM_END);
/* terminate buffer */
if (buf->len + 1 > buf->buf_sz)
strbuf_grow(buf);
*(buf->data + buf->len) = '\0';
/* restore NULLOK option */
if (!nullok)
strbuf_unsetopt(buf, STRBUF_NULLOK);
strbuf_check(buf);
len = (size_t)strm.total_out;
(void)inflateEnd(&strm);
if (z_ret != Z_STREAM_END) {
fprintf(stderr, "ERR\n");
exit(EXIT_FAILURE);
}
return len;
}
static void strbuf_grow(STRBUF *buf)
{
buf->buf_sz += strbuf_grow_sz;
buf->data = yrealloc(buf->data, buf->buf_sz);
strbuf_check(buf);
}
STRBUF *strbuf_slurp(char *str)
{
return strbuf_slurp_n(str, strlen(str));
}
STRBUF *strbuf_slurp_n(char *str, size_t len)
{
STRBUF *buf = ymalloc(sizeof(STRBUF));
buf->len = len;
buf->buf_sz = len + 1;
buf->data = yrealloc(str, buf->buf_sz);
*(buf->data + len) = '\0';
buf->opt = 0;
return buf;
}
char *strbuf_spit(STRBUF *buf)
{
char *data;
strbuf_check(buf);
strbuf_shrink(buf);
data = buf->data;
yfree(buf);
return data;
}
unsigned int strbuf_crc32(STRBUF *buf)
{
uLong crc = crc32(0L, Z_NULL, 0);
crc = crc32(crc, (Bytef *)buf->data, buf->len);
return (unsigned int)crc;
}
void strbuf_setopt(STRBUF *buf, enum strbuf_opt opt)
{
buf->opt |= opt;
}
void strbuf_unsetopt(STRBUF *buf, enum strbuf_opt opt)
{
buf->opt &= ~opt;
}