/* * strbuf.c: A simple string buffer * * Copyright (c) 2006-2008 Dennis Stosberg * * 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_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; }