diff --git a/cbits/array.c b/cbits/array.c
deleted file mode 100644
--- a/cbits/array.c
+++ /dev/null
@@ -1,300 +0,0 @@
-/* array.c - automatic dynamic array for pointers */
-
-/*
- * Copyright (c) 2008, Natacha Porté
- *
- * Permission to use, copy, modify, and 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 "array.h"
-
-#include <string.h>
-
-
-/***************************
- * STATIC HELPER FUNCTIONS *
- ***************************/
-
-/* arr_realloc • realloc memory of a struct array */
-static int
-arr_realloc(struct array* arr, int neosz) {
-	void* neo;
-	neo = realloc(arr->base, neosz * arr->unit);
-	if (neo == 0) return 0;
-	arr->base = neo;
-	arr->asize = neosz;
-	if (arr->size > neosz) arr->size = neosz;
-	return 1; }
-
-
-/* parr_realloc • realloc memory of a struct parray */
-static int
-parr_realloc(struct parray* arr, int neosz) {
-	void* neo;
-	neo = realloc(arr->item, neosz * sizeof (void*));
-	if (neo == 0) return 0;
-	arr->item = neo;
-	arr->asize = neosz;
-	if (arr->size > neosz) arr->size = neosz;
-	return 1; }
-
-
-
-/***************************
- * GENERIC ARRAY FUNCTIONS *
- ***************************/
-
-/* arr_adjust • shrink the allocated memory to fit exactly the needs */
-int
-arr_adjust(struct array *arr) {
-	return arr_realloc(arr, arr->size); }
-
-
-/* arr_free • frees the structure contents (buf NOT the struct itself) */
-void
-arr_free(struct array *arr) {
-	if (!arr) return;
-	free(arr->base);
-	arr->base = 0;
-	arr->size = arr->asize = 0; }
-
-
-/* arr_grow • increases the array size to fit the given number of elements */
-int
-arr_grow(struct array *arr, int need) {
-	if (arr->asize >= need) return 1;
-	else return arr_realloc(arr, need); }
-
-
-/* arr_init • initialization of the contents of the struct */
-void
-arr_init(struct array *arr, size_t unit) {
-	arr->base = 0;
-	arr->size = arr->asize = 0;
-	arr->unit = unit; }
-
-
-/* arr_insert • inserting nb elements before the nth one */
-int
-arr_insert(struct array *arr, int nb, int n) {
-	char *src, *dst;
-	size_t len;
-	if (!arr || nb <= 0 || n < 0
-	|| !arr_grow(arr, arr->size + nb))
-		return 0;
-	if (n < arr->size) {
-		src = arr->base;
-		src += n * arr->unit;
-		dst = src + nb * arr->unit;
-		len = (arr->size - n) * arr->unit;
-		memmove(dst, src, len); }
-	arr->size += nb;
-	return 1; }
-
-
-/* arr_item • returns a pointer to the n-th element */
-void *
-arr_item(struct array *arr, int no) {
-	char *ptr;
-	if (!arr || no < 0 || no >= arr->size) return 0;
-	ptr = arr->base;
-	ptr += no * arr->unit;
-	return ptr; }
-
-
-/* arr_newitem • returns the index of a new element appended to the array */
-int
-arr_newitem(struct array *arr) {
-	if (!arr_grow(arr, arr->size + 1)) return -1;
-	arr->size += 1;
-	return arr->size - 1; }
-
-
-/* arr_remove • removes the n-th elements of the array */
-void
-arr_remove(struct array *arr, int idx) {
-	if (!arr || idx < 0 || idx >= arr->size) return;
-	arr->size -= 1;
-	if (idx < arr->size) {
-		char *dst = arr->base;
-		char *src;
-		dst += idx * arr->unit;
-		src = dst + arr->unit;
-		memmove(dst, src, (arr->size - idx) * arr->unit); } }
-
-
-/* arr_sorted_find • O(log n) search in a sorted array, returning entry */
-void *
-arr_sorted_find(struct array *arr, void *key, array_cmp_fn cmp) {
-	int mi, ma, cu, ret;
-	char *ptr = arr->base;
-	mi = -1;
-	ma = arr->size;
-	while (mi < ma - 1) {
-		cu = mi + (ma - mi) / 2;
-		ret = cmp(key, ptr + cu * arr->unit);
-		if (ret == 0) return ptr + cu * arr->unit;
-		else if (ret < 0) ma = cu;
-		else /* if (ret > 0) */ mi = cu; }
-	return 0; }
-
-
-/* arr_sorted_find_i • O(log n) search in a sorted array,
- *      returning index of the smallest element larger than the key */
-int
-arr_sorted_find_i(struct array *arr, void *key, array_cmp_fn cmp) {
-	int mi, ma, cu, ret;
-	char *ptr = arr->base;
-	mi = -1;
-	ma = arr->size;
-	while (mi < ma - 1) {
-		cu = mi + (ma - mi) / 2;
-		ret = cmp(key, ptr + cu * arr->unit);
-		if (ret == 0) {
-			while (cu < arr->size && ret == 0) {
-				cu += 1;
-				ret = cmp(key, ptr + cu * arr->unit); }
-			return cu; }
-		else if (ret < 0) ma = cu;
-		else /* if (ret > 0) */ mi = cu; }
-	return ma; }
-
-
-
-/***************************
- * POINTER ARRAY FUNCTIONS *
- ***************************/
-
-/* parr_adjust • shrinks the allocated memory to fit exactly the needs */
-int
-parr_adjust(struct parray* arr) {
-	return parr_realloc (arr, arr->size); }
-
-
-/* parr_free • frees the structure contents (buf NOT the struct itself) */
-void
-parr_free(struct parray *arr) {
-	if (!arr) return;
-	free (arr->item);
-	arr->item = 0;
-	arr->size = 0;
-	arr->asize = 0; }
-
-
-/* parr_grow • increases the array size to fit the given number of elements */
-int
-parr_grow(struct parray *arr, int need) {
-	if (arr->asize >= need) return 1;
-	else return parr_realloc (arr, need); }
-
-
-/* parr_init • initialization of the struct (which is equivalent to zero) */
-void
-parr_init(struct parray *arr) {
-	arr->item = 0;
-	arr->size = 0;
-	arr->asize = 0; }
-
-
-/* parr_insert • inserting nb elements before the nth one */
-int
-parr_insert(struct parray *parr, int nb, int n) {
-	char *src, *dst;
-	size_t len, i;
-	if (!parr || nb <= 0 || n < 0
-	|| !parr_grow(parr, parr->size + nb))
-		return 0;
-	if (n < parr->size) {
-		src = (void *)parr->item;
-		src += n * sizeof (void *);
-		dst = src + nb * sizeof (void *);
-		len = (parr->size - n) * sizeof (void *);
-		memmove(dst, src, len);
-		for (i = 0; i < (size_t)nb; ++i)
-			parr->item[n + i] = 0; }
-	parr->size += nb;
-	return 1; }
-
-
-/* parr_pop • pops the last item of the array and returns it */
-void *
-parr_pop(struct parray *arr) {
-	if (arr->size <= 0) return 0;
-	arr->size -= 1;
-	return arr->item[arr->size]; }
-
-
-/* parr_push • pushes a pointer at the end of the array (= append) */
-int
-parr_push(struct parray *arr, void *i) {
-	if (!parr_grow(arr, arr->size + 1)) return 0;
-	arr->item[arr->size] = i;
-	arr->size += 1;
-	return 1; }
-
-
-/* parr_remove • removes the n-th element of the array and returns it */
-void *
-parr_remove(struct parray *arr, int idx) {
-	void* ret;
-	int i;
-	if (!arr || idx < 0 || idx >= arr->size) return 0;
-	ret = arr->item[idx];
-	for (i = idx+1; i < arr->size; ++i)
-		arr->item[i - 1] = arr->item[i];
-	arr->size -= 1;
-	return ret; }
-
-
-/* parr_sorted_find • O(log n) search in a sorted array, returning entry */
-void *
-parr_sorted_find(struct parray *arr, void *key, array_cmp_fn cmp) {
-	int mi, ma, cu, ret;
-	mi = -1;
-	ma = arr->size;
-	while (mi < ma - 1) {
-		cu = mi + (ma - mi) / 2;
-		ret = cmp(key, arr->item[cu]);
-		if (ret == 0) return arr->item[cu];
-		else if (ret < 0) ma = cu;
-		else /* if (ret > 0) */ mi = cu; }
-	return 0; }
-
-
-/* parr_sorted_find_i • O(log n) search in a sorted array,
- *      returning index of the smallest element larger than the key */
-int
-parr_sorted_find_i(struct parray *arr, void *key, array_cmp_fn cmp) {
-	int mi, ma, cu, ret;
-	mi = -1;
-	ma = arr->size;
-	while (mi < ma - 1) {
-		cu = mi + (ma - mi) / 2;
-		ret = cmp(key, arr->item[cu]);
-		if (ret == 0) {
-			while (cu < arr->size && ret == 0) {
-				cu += 1;
-				ret = cmp(key, arr->item[cu]); }
-			return cu; }
-		else if (ret < 0) ma = cu;
-		else /* if (ret > 0) */ mi = cu; }
-	return ma; }
-
-
-/* parr_top • returns the top the stack (i.e. the last element of the array) */
-void *
-parr_top(struct parray *arr) {
-	if (arr == 0 || arr->size <= 0) return 0;
-	else return arr->item[arr->size - 1]; }
-
-/* vim: set filetype=c: */
diff --git a/cbits/array.h b/cbits/array.h
deleted file mode 100644
--- a/cbits/array.h
+++ /dev/null
@@ -1,147 +0,0 @@
-/* array.h - automatic dynamic array for pointers */
-
-/*
- * Copyright (c) 2008, Natacha Porté
- *
- * Permission to use, copy, modify, and 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 LITHIUM_ARRAY_H
-#define LITHIUM_ARRAY_H
-
-#include <stdlib.h>
-
-/********************
- * TYPE DEFINITIONS *
- ********************/
-
-/* struct array • generic linear array */
-struct array {
-	void*	base;
-	int	size;
-	int	asize;
-	size_t	unit; };
-
-
-/* struct parray • array of pointers */
-struct parray {
-	void **	item;
-	int	size;
-	int	asize; };
-
-
-/* array_cmp_fn • comparison functions for sorted arrays */
-typedef int (*array_cmp_fn)(void *key, void *array_entry);
-
-
-
-/***************************
- * GENERIC ARRAY FUNCTIONS *
- ***************************/
-
-/* arr_adjust • shrink the allocated memory to fit exactly the needs */
-int
-arr_adjust(struct array *);
-
-/* arr_free • frees the structure contents (buf NOT the struct itself) */
-void
-arr_free(struct array *);
-
-/* arr_grow • increases the array size to fit the given number of elements */
-int
-arr_grow(struct array *, int);
-
-/* arr_init • initialization of the contents of the struct */
-void
-arr_init(struct array *, size_t);
-
-/* arr_insert • inserting elements nb before the nth one */
-int
-arr_insert(struct array *, int nb, int n);
-
-/* arr_item • returns a pointer to the n-th element */
-void *
-arr_item(struct array *, int);
-
-/* arr_newitem • returns the index of a new element appended to the array */
-int
-arr_newitem(struct array *);
-
-/* arr_remove • removes the n-th elements of the array */
-void
-arr_remove(struct array *, int);
-
-/* arr_sorted_find • O(log n) search in a sorted array, returning entry */
-/* equivalent to bsearch(key, arr->base, arr->size, arr->unit, cmp) */
-void *
-arr_sorted_find(struct array *, void *key, array_cmp_fn cmp);
-
-/* arr_sorted_find_i • O(log n) search in a sorted array,
- *      returning index of the smallest element larger than the key */
-int
-arr_sorted_find_i(struct array *, void *key, array_cmp_fn cmp);
-
-
-/***************************
- * POINTER ARRAY FUNCTIONS *
- ***************************/
-
-/* parr_adjust • shrinks the allocated memory to fit exactly the needs */
-int
-parr_adjust(struct parray *);
-
-/* parr_free • frees the structure contents (buf NOT the struct itself) */
-void
-parr_free(struct parray *);
-
-/* parr_grow • increases the array size to fit the given number of elements */
-int
-parr_grow(struct parray *, int);
-
-/* parr_init • initialization of the struct (which is equivalent to zero) */
-void
-parr_init(struct parray *);
-
-/* parr_insert • inserting nb elements before the nth one */
-int
-parr_insert(struct parray *, int nb, int n);
-
-/* parr_pop • pops the last item of the array and returns it */
-void *
-parr_pop(struct parray *);
-
-/* parr_push • pushes a pointer at the end of the array (= append) */
-int
-parr_push(struct parray *, void *);
-
-/* parr_remove • removes the n-th element of the array and returns it */
-void *
-parr_remove(struct parray *, int);
-
-/* parr_sorted_find • O(log n) search in a sorted array, returning entry */
-void *
-parr_sorted_find(struct parray *, void *key, array_cmp_fn cmp);
-
-/* parr_sorted_find_i • O(log n) search in a sorted array,
- *      returning index of the smallest element larger than the key */
-int
-parr_sorted_find_i(struct parray *, void *key, array_cmp_fn cmp);
-
-/* parr_top • returns the top the stack (i.e. the last element of the array) */
-void *
-parr_top(struct parray *);
-
-
-#endif /* ndef LITHIUM_ARRAY_H */
-
-/* vim: set filetype=c: */
diff --git a/cbits/autolink.c b/cbits/autolink.c
--- a/cbits/autolink.c
+++ b/cbits/autolink.c
@@ -154,7 +154,7 @@
 	if (offset > 0 && !ispunct(data[-1]) && !isspace(data[-1]))
 		return 0;
 
-	if (size < 4 || memcmp(data, "www.", STRLEN("www.")) != 0)
+	if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)
 		return 0;
 
 	link_end = check_domain(data, size);
@@ -238,7 +238,7 @@
 
 	if (!sd_autolink_issafe(data - rewind, size + rewind))
 		return 0;
-	link_end = STRLEN("://");
+	link_end = strlen("://");
 
 	domain_len = check_domain(data + link_end, size - link_end);
 	if (domain_len == 0)
diff --git a/cbits/buffer.c b/cbits/buffer.c
--- a/cbits/buffer.c
+++ b/cbits/buffer.c
@@ -1,7 +1,6 @@
-/* buffer.c - automatic buffer structure */
-
 /*
  * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,13 +15,6 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/*
- * COMPILE TIME OPTIONS
- *
- * BUFFER_STATS • if defined, stats are kept about memory usage
- */
-
-#define BUFFER_STDARG
 #define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb
 
 #include "buffer.h"
@@ -31,77 +23,64 @@
 #include <stdlib.h>
 #include <string.h>
 
-
-/********************
- * GLOBAL VARIABLES *
- ********************/
-
-#ifdef BUFFER_STATS
-long buffer_stat_nb = 0;
-size_t buffer_stat_alloc_bytes = 0;
+/* MSVC compat */
+#if defined(_MSC_VER)
+#	define _buf_vsnprintf _vsnprintf
+#else
+#	define _buf_vsnprintf vsnprintf
 #endif
 
-
-/***************************
- * STATIC HELPER FUNCTIONS *
- ***************************/
-
-/* lower • retruns the lower-case variant of the input char */
-static char
-lower(char c) {
-	return (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; }
+/* bufcmp: buffer comparison */
+int
+bufcmp(const struct buf *a, const struct buf *b)
+{
+	size_t i = 0;
+	size_t cmplen;
 
+	if (a == b)
+		return 0;
 
+	if (!a)
+		return -1;
 
-/********************
- * BUFFER FUNCTIONS *
- ********************/
+	if (!b)
+		return 1;
 
-/* bufcasecmp • case-insensitive buffer comparison */
-int
-bufcasecmp(const struct buf *a, const struct buf *b) {
-	size_t i = 0;
-	size_t cmplen;
-	if (a == b) return 0;
-	if (!a) return -1; else if (!b) return 1;
 	cmplen = (a->size < b->size) ? a->size : b->size;
-	while (i < cmplen && lower(a->data[i]) == lower(b->data[i])) ++i;
-	if (i < a->size) {
-		if (i < b->size)  return lower(a->data[i]) - lower(b->data[i]);
-		else		  return 1; }
-	else {	if (i < b->size)  return -1;
-		else		  return 0; } }
 
+	while (i < cmplen && a->data[i] == b->data[i])
+		++i;
 
-/* bufcmp • case-sensitive buffer comparison */
-int
-bufcmp(const struct buf *a, const struct buf *b) {
-	size_t i = 0;
-	size_t cmplen;
-	if (a == b) return 0;
-	if (!a) return -1; else if (!b) return 1;
-	cmplen = (a->size < b->size) ? a->size : b->size;
-	while (i < cmplen && a->data[i] == b->data[i]) ++i;
 	if (i < a->size) {
-		if (i < b->size)	return a->data[i] - b->data[i];
-		else			return 1; }
-	else {	if (i < b->size)	return -1;
-		else			return 0; } }
-
+		if (i < b->size) return a->data[i] - b->data[i];
+		return 1;
+	} else {
+		if (i < b->size) return -1;
+		return 0;
+	}
+}
 
-/* bufcmps • case-sensitive comparison of a string to a buffer */
+/* bufcmps: comparison of a string to a buffer */
 int
-bufcmps(const struct buf *a, const char *b) {
+bufcmps(const struct buf *a, const char *b)
+{
 	const size_t len = strlen(b);
 	size_t cmplen = len;
 	int r;
-	if (!a || !a->size) return b ? 0 : -1;
-	if (len < a->size) cmplen = a->size;
+
+	if (!a || !a->size)
+		return b ? 0 : -1;
+
+	if (len < a->size)
+		cmplen = a->size;
+
 	r = strncmp(a->data, b, cmplen);
+
 	if (r) return r;
 	else if (a->size == len) return 0;
 	else if (a->size < len) return -1;
-	else return 1; }
+	else return 1;
+}
 
 int
 bufprefix(const struct buf *buf, const char *prefix)
@@ -120,199 +99,231 @@
 }
 
 
-/* bufdup • buffer duplication */
+/* bufdup: buffer duplication */
 struct buf *
-bufdup(const struct buf *src, size_t dupunit) {
+bufdup(const struct buf *src, size_t dupunit)
+{
 	size_t blocks;
 	struct buf *ret;
-	if (src == 0) return 0;
+	if (src == 0)
+		return 0;
+
 	ret = malloc(sizeof (struct buf));
-	if (ret == 0) return 0;
+	if (ret == 0)
+		return 0;
+
 	ret->unit = dupunit;
 	ret->size = src->size;
 	ret->ref = 1;
 	if (!src->size) {
 		ret->asize = 0;
 		ret->data = 0;
-		return ret; }
+		return ret;
+	}
+
 	blocks = (src->size + dupunit - 1) / dupunit;
 	ret->asize = blocks * dupunit;
 	ret->data = malloc(ret->asize);
+
 	if (ret->data == 0) {
 		free(ret);
-		return 0; }
+		return 0;
+	}
+
 	memcpy(ret->data, src->data, src->size);
-#ifdef BUFFER_STATS
-	buffer_stat_nb += 1;
-	buffer_stat_alloc_bytes += ret->asize;
-#endif
-	return ret; }
+	return ret;
+}
 
-/* bufgrow • increasing the allocated size to the given value */
+/* bufgrow: increasing the allocated size to the given value */
 int
-bufgrow(struct buf *buf, size_t neosz) {
+bufgrow(struct buf *buf, size_t neosz)
+{
 	size_t neoasz;
 	void *neodata;
-	if (!buf || !buf->unit || neosz > BUFFER_MAX_ALLOC_SIZE) return 0;
-	if (buf->asize >= neosz) return 1;
+	if (!buf || !buf->unit || neosz > BUFFER_MAX_ALLOC_SIZE)
+		return BUF_ENOMEM;
+
+	if (buf->asize >= neosz)
+		return BUF_OK;
+
 	neoasz = buf->asize + buf->unit;
-	while (neoasz < neosz) neoasz += buf->unit;
+	while (neoasz < neosz)
+		neoasz += buf->unit;
+
 	neodata = realloc(buf->data, neoasz);
-	if (!neodata) return 0;
-#ifdef BUFFER_STATS
-	buffer_stat_alloc_bytes += (neoasz - buf->asize);
-#endif
+	if (!neodata)
+		return BUF_ENOMEM;
+
 	buf->data = neodata;
 	buf->asize = neoasz;
-	return 1; }
+	return BUF_OK;
+}
 
 
-/* bufnew • allocation of a new buffer */
+/* bufnew: allocation of a new buffer */
 struct buf *
-bufnew(size_t unit) {
+bufnew(size_t unit)
+{
 	struct buf *ret;
 	ret = malloc(sizeof (struct buf));
+
 	if (ret) {
-#ifdef BUFFER_STATS
-		buffer_stat_nb += 1;
-#endif
 		ret->data = 0;
 		ret->size = ret->asize = 0;
 		ret->ref = 1;
-		ret->unit = unit; }
-	return ret; }
+		ret->unit = unit;
+	}
+	return ret;
+}
 
+/* bufnullterm: NULL-termination of the string array */
+const char *
+bufcstr(struct buf *buf)
+{
+	if (!buf || !buf->unit)
+		return NULL;
 
-/* bufnullterm • NUL-termination of the string array (making a C-string) */
-void
-bufnullterm(struct buf *buf) {
-	if (!buf || !buf->unit) return;
-	if (buf->size < buf->asize && buf->data[buf->size] == 0) return;
-	if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1))
-		buf->data[buf->size] = 0; }
+	if (buf->size < buf->asize && buf->data[buf->size] == 0)
+		return buf->data;
 
+	if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) {
+		buf->data[buf->size] = 0;
+		return buf->data;
+	}
 
-/* bufprintf • formatted printing to a buffer */
+	return NULL;
+}
+
+/* bufprintf: formatted printing to a buffer */
 void
-bufprintf(struct buf *buf, const char *fmt, ...) {
+bufprintf(struct buf *buf, const char *fmt, ...)
+{
 	va_list ap;
-	if (!buf || !buf->unit) return;
+	if (!buf || !buf->unit)
+		return;
+
 	va_start(ap, fmt);
 	vbufprintf(buf, fmt, ap);
-	va_end(ap); }
-
+	va_end(ap);
+}
 
-/* bufput • appends raw data to a buffer */
+/* bufput: appends raw data to a buffer */
 void
-bufput(struct buf *buf, const void *data, size_t len) {
-	if (!buf) return;
-	if (buf->size + len > buf->asize && !bufgrow(buf, buf->size + len))
+bufput(struct buf *buf, const void *data, size_t len)
+{
+	if (!buf)
 		return;
-	memcpy(buf->data + buf->size, data, len);
-	buf->size += len; }
 
+	if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0)
+		return;
 
-/* bufputs • appends a NUL-terminated string to a buffer */
+	memcpy(buf->data + buf->size, data, len);
+	buf->size += len;
+}
+
+/* bufputs: appends a NUL-terminated string to a buffer */
 void
-bufputs(struct buf *buf, const char *str) {
-	bufput(buf, str, strlen (str)); }
+bufputs(struct buf *buf, const char *str)
+{
+	bufput(buf, str, strlen(str));
+}
 
 
-/* bufputc • appends a single char to a buffer */
+/* bufputc: appends a single char to a buffer */
 void
-bufputc(struct buf *buf, char c) {
-	if (!buf) return;
-	if (buf->size + 1 > buf->asize && !bufgrow(buf, buf->size + 1))
+bufputc(struct buf *buf, char c)
+{
+	if (!buf)
 		return;
-	buf->data[buf->size] = c;
-	buf->size += 1; }
 
+	if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0)
+		return;
 
-/* bufrelease • decrease the reference count and free the buffer if needed */
+	buf->data[buf->size] = c;
+	buf->size += 1;
+}
+
+/* bufrelease: decrease the reference count and free the buffer if needed */
 void
-bufrelease(struct buf *buf) {
-	if (!buf) return;
-	buf->ref -= 1;
-	if (buf->ref == 0) {
-#ifdef BUFFER_STATS
-		buffer_stat_nb -= 1;
-		buffer_stat_alloc_bytes -= buf->asize;
-#endif
+bufrelease(struct buf *buf)
+{
+	if (!buf)
+		return;
+
+	if (--buf->ref == 0) {
 		free(buf->data);
-		free(buf); } }
+		free(buf);
+	}
+}
 
 
-/* bufreset • frees internal data of the buffer */
+/* bufreset: frees internal data of the buffer */
 void
-bufreset(struct buf *buf) {
-	if (!buf) return;
-#ifdef BUFFER_STATS
-	buffer_stat_alloc_bytes -= buf->asize;
-#endif
+bufreset(struct buf *buf)
+{
+	if (!buf)
+		return;
+
 	free(buf->data);
-	buf->data = 0;
-	buf->size = buf->asize = 0; }
+	buf->data = NULL;
+	buf->size = buf->asize = 0;
+}
 
 
-/* bufset • safely assigns a buffer to another */
+/* bufset: safely assigns a buffer to another */
 void
-bufset(struct buf **dest, struct buf *src) {
+bufset(struct buf **dest, struct buf *src)
+{
 	if (src) {
 		if (!src->asize) src = bufdup(src, 1);
-		else src->ref += 1; }
-	bufrelease(*dest);
-	*dest = src; }
+		else src->ref += 1;
+	}
 
+	bufrelease(*dest);
+	*dest = src;
+}
 
-/* bufslurp • removes a given number of bytes from the head of the array */
+/* bufslurp: removes a given number of bytes from the head of the array */
 void
-bufslurp(struct buf *buf, size_t len) {
-	if (!buf || !buf->unit || len <= 0) return;
+bufslurp(struct buf *buf, size_t len)
+{
+	if (!buf || !buf->unit || len <= 0)
+		return;
+
 	if (len >= buf->size) {
 		buf->size = 0;
-		return; }
-	buf->size -= len;
-	memmove(buf->data, buf->data + len, buf->size); }
-
-
-/* buftoi • converts the numbers at the beginning of the buf into an int */
-int
-buftoi(struct buf *buf, size_t offset_i, size_t *offset_o) {
-	int r = 0, neg = 0;
-	size_t i = offset_i;
-	if (!buf || !buf->size) return 0;
-	if (buf->data[i] == '+') i += 1;
-	else if (buf->data[i] == '-') {
-		neg = 1;
-		i += 1; }
-	while (i < buf->size && buf->data[i] >= '0' && buf->data[i] <= '9') {
-		r = (r * 10) + buf->data[i] - '0';
-		i += 1; }
-	if (offset_o) *offset_o = i;
-	return neg ? -r : r; }
-
+		return;
+	}
 
+	buf->size -= len;
+	memmove(buf->data, buf->data + len, buf->size);
+}
 
-/* vbufprintf • stdarg variant of formatted printing into a buffer */
+/* vbufprintf: stdarg variant of formatted printing into a buffer */
 void
-vbufprintf(struct buf *buf, const char *fmt, va_list ap) {
+vbufprintf(struct buf *buf, const char *fmt, va_list ap)
+{
 	int n;
-	va_list ap_save;
-	if (buf == 0
-	|| (buf->size >= buf->asize && !bufgrow (buf, buf->size + 1)))
+
+	if (buf == 0 || (buf->size >= buf->asize && bufgrow(buf, buf->size + 1)) < 0)
 		return;
 
-	va_copy(ap_save, ap);
-	n = vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap);
+	n = _buf_vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap);
 
-	if (n < 0 || (size_t)n >= buf->asize - buf->size) {
-		size_t new_size = (n > 0) ? n : buf->size;
-		if (!bufgrow (buf, buf->size + new_size + 1))
+	if (n < 0) {
+#ifdef _MSC_VER
+		n = _vscprintf(fmt, ap);
+#else
+		return;
+#endif
+	}
+
+	if ((size_t)n >= buf->asize - buf->size) {
+		if (bufgrow(buf, buf->size + n + 1) < 0)
 			return;
 
-		n = vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap_save);
+		n = _buf_vsnprintf(buf->data + buf->size, buf->asize - buf->size, fmt, ap);
 	}
-	va_end(ap_save);
 
 	if (n < 0)
 		return;
@@ -320,4 +331,3 @@
 	buf->size += n;
 }
 
-/* vim: set filetype=c: */
diff --git a/cbits/buffer.h b/cbits/buffer.h
--- a/cbits/buffer.h
+++ b/cbits/buffer.h
@@ -1,7 +1,6 @@
-/* buffer.h - automatic buffer structure */
-
 /*
  * Copyright (c) 2008, Natacha Porté
+ * Copyright (c) 2011, Vicent Martí
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -16,139 +15,89 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#ifndef LITHIUM_BUFFER_H
-#define LITHIUM_BUFFER_H
+#ifndef __GEN_BUFFER_H__
+#define __GEN_BUFFER_H__
 
 #include <stddef.h>
+#include <stdarg.h>
 
 #if defined(_MSC_VER)
 #define __attribute__(x)
 #define inline
-#define strncasecmp _strnicmp
-#define snprintf _snprintf
-#define va_copy(d,s) ((d) = (s))
 #endif
 
-/********************
- * TYPE DEFINITIONS *
- ********************/
+typedef enum {
+	BUF_OK = 0,
+	BUF_ENOMEM = -1,
+} buferror_t;
 
-/* struct buf • character array buffer */
+/* struct buf: character array buffer */
 struct buf {
-	char *	data;	/* actual character data */
-	size_t	size;	/* size of the string */
-	size_t	asize;	/* allocated size (0 = volatile buffer) */
-	size_t	unit;	/* reallocation unit size (0 = read-only buffer) */
-	int	ref; };	/* reference count */
-
-/**********
- * MACROS *
- **********/
-
-#define STRLEN(x) (sizeof(x) - 1)
-
-/* CONST_BUF • global buffer from a string litteral */
-#define CONST_BUF(name, string) \
-	static struct buf name = { string, sizeof string -1, sizeof string }
-
+	char *data;		/* actual character data */
+	size_t size;	/* size of the string */
+	size_t asize;	/* allocated size (0 = volatile buffer) */
+	size_t unit;	/* reallocation unit size (0 = read-only buffer) */
+	int	ref;		/* reference count */
+};	
 
-/* VOLATILE_BUF • macro for creating a volatile buffer on the stack */
-#define VOLATILE_BUF(name, strname) \
-	struct buf name = { strname, strlen(strname) }
+/* CONST_BUF: global buffer from a string litteral */
+#define BUF_STATIC(string) \
+	{ string, sizeof string -1, sizeof string, 0, 0 }
 
+/* VOLATILE_BUF: macro for creating a volatile buffer on the stack */
+#define BUF_VOLATILE(strname) \
+	{ strname, strlen(strname), 0, 0, 0 }
 
-/* BUFPUTSL • optimized bufputs of a string litteral */
+/* BUFPUTSL: optimized bufputs of a string litteral */
 #define BUFPUTSL(output, litteral) \
 	bufput(output, litteral, sizeof litteral - 1)
 
-
-
-/********************
- * BUFFER FUNCTIONS *
- ********************/
-
-/* bufcasecmp • case-insensitive buffer comparison */
-int
-bufcasecmp(const struct buf *, const struct buf *);
-
-/* bufcmp • case-sensitive buffer comparison */
-int
-bufcmp(const struct buf *, const struct buf *);
-
-/* bufcmps • case-sensitive comparison of a string to a buffer */
-int
-bufcmps(const struct buf *, const char *);
-
-/* bufprefix * compare the beginning of a buffer with a string */
-int
-bufprefix(const struct buf *buf, const char *prefix);
-
-/* bufdup • buffer duplication */
-struct buf *
-bufdup(const struct buf *, size_t)
-	__attribute__ ((malloc));
-
-/* bufgrow • increasing the allocated size to the given value */
-int
-bufgrow(struct buf *, size_t);
-
-/* bufnew • allocation of a new buffer */
-struct buf *
-bufnew(size_t)
-	__attribute__ ((malloc));
-
-/* bufnullterm • NUL-termination of the string array (making a C-string) */
-void
-bufnullterm(struct buf *);
+/* bufcmp: case-sensitive buffer comparison */
+int bufcmp(const struct buf *, const struct buf *);
 
-/* bufprintf • formatted printing to a buffer */
-void
-bufprintf(struct buf *, const char *, ...)
-	__attribute__ ((format (printf, 2, 3)));
+/* bufcmps: case-sensitive comparison of a string to a buffer */
+int bufcmps(const struct buf *, const char *); 
 
-/* bufput • appends raw data to a buffer */
-void
-bufput(struct buf *, const void*, size_t);
+/* bufprefix: compare the beginning of a buffer with a string */
+int bufprefix(const struct buf *buf, const char *prefix); 
 
-/* bufputs • appends a NUL-terminated string to a buffer */
-void
-bufputs(struct buf *, const char*);
+/* bufdup: buffer duplication */
+struct buf *bufdup(const struct buf *, size_t) __attribute__ ((malloc));
 
-/* bufputc • appends a single char to a buffer */
-void
-bufputc(struct buf *, char);
+/* bufgrow: increasing the allocated size to the given value */
+int bufgrow(struct buf *, size_t);
 
-/* bufrelease • decrease the reference count and free the buffer if needed */
-void
-bufrelease(struct buf *);
+/* bufnew: allocation of a new buffer */
+struct buf *bufnew(size_t) __attribute__ ((malloc));
 
-/* bufreset • frees internal data of the buffer */
-void
-bufreset(struct buf *);
+/* bufnullterm: NUL-termination of the string array (making a C-string) */
+const char *bufcstr(struct buf *);
 
-/* bufset • safely assigns a buffer to another */
-void
-bufset(struct buf **, struct buf *);
+/* bufprintf: formatted printing to a buffer */
+void bufprintf(struct buf *, const char *, ...) __attribute__ ((format (printf, 2, 3)));
 
-/* bufslurp • removes a given number of bytes from the head of the array */
-void
-bufslurp(struct buf *, size_t);
+/* bufput: appends raw data to a buffer */
+void bufput(struct buf *, const void*, size_t);
 
-/* buftoi • converts the numbers at the beginning of the buf into an int */
-int
-buftoi(struct buf *, size_t, size_t *);
+/* bufputs: appends a NUL-terminated string to a buffer */
+void bufputs(struct buf *, const char*);
 
+/* bufputc: appends a single char to a buffer */
+void bufputc(struct buf *, char);
 
+/* bufrelease: decrease the reference count and free the buffer if needed */
+void bufrelease(struct buf *);
 
-#ifdef BUFFER_STDARG
-#include <stdarg.h>
+/* bufreset: frees internal data of the buffer */
+void bufreset(struct buf *);
 
-/* vbufprintf • stdarg variant of formatted printing into a buffer */
-void
-vbufprintf(struct buf *, const char*, va_list);
+/* bufset: safely assigns a buffer to another */
+void bufset(struct buf **, struct buf *);
 
-#endif /* def BUFFER_STDARG */
+/* bufslurp: removes a given number of bytes from the head of the array */
+void bufslurp(struct buf *, size_t);
 
-#endif /* ndef LITHIUM_BUFFER_H */
+/* vbufprintf: stdarg variant of formatted printing into a buffer */
+void vbufprintf(struct buf *, const char*, va_list);
 
-/* vim: set filetype=c: */
+#endif
diff --git a/cbits/html.c b/cbits/html.c
--- a/cbits/html.c
+++ b/cbits/html.c
@@ -541,24 +541,26 @@
 {
 	struct html_renderopt *options = opaque;
 
-	while (level > options->toc_data.current_level) {
-		if (options->toc_data.current_level > 0)
-			BUFPUTSL(ob, "<li>");
-		BUFPUTSL(ob, "<ul>\n");
-		options->toc_data.current_level++;
-	}
-
-	while (level < options->toc_data.current_level) {
-		BUFPUTSL(ob, "</ul>");
-		if (options->toc_data.current_level > 1)
-			BUFPUTSL(ob, "</li>\n");
-		options->toc_data.current_level--;
+	if (level > options->toc_data.current_level) {
+		while (level > options->toc_data.current_level) {
+			BUFPUTSL(ob, "<ul>\n<li>\n");
+			options->toc_data.current_level++;
+		}
+	} else if (level < options->toc_data.current_level) {
+		BUFPUTSL(ob, "</li>\n");
+		while (level < options->toc_data.current_level) {
+			BUFPUTSL(ob, "</ul>\n</li>\n");
+			options->toc_data.current_level--;
+		}
+		BUFPUTSL(ob,"<li>\n");
+	} else {
+		BUFPUTSL(ob,"</li>\n<li>\n");
 	}
 
-	bufprintf(ob, "<li><a href=\"#toc_%d\">", options->toc_data.header_count++);
+	bufprintf(ob, "<a href=\"#toc_%d\">", options->toc_data.header_count++);
 	if (text)
 		bufput(ob, text->data, text->size);
-	BUFPUTSL(ob, "</a></li>\n");
+	BUFPUTSL(ob, "</a>\n");
 }
 
 static void
@@ -566,13 +568,10 @@
 {
 	struct html_renderopt *options = opaque;
 
-	while (options->toc_data.current_level > 1) {
-		BUFPUTSL(ob, "</ul></li>\n");
+	while (options->toc_data.current_level > 0) {
+		BUFPUTSL(ob, "</li>\n</ul>\n");
 		options->toc_data.current_level--;
 	}
-
-	if (options->toc_data.current_level)
-		BUFPUTSL(ob, "</ul>\n");
 }
 
 void
diff --git a/cbits/html_blocks.h b/cbits/html_blocks.h
new file mode 100644
--- /dev/null
+++ b/cbits/html_blocks.h
@@ -0,0 +1,205 @@
+/* C code produced by gperf version 3.0.3 */
+/* Command-line: gperf -N find_block_tag -H hash_block_tag -C -c -E --ignore-case html_block_names.txt  */
+/* Computed positions: -k'1-2' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+      && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+      && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+      && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+      && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+      && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+      && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+      && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+      && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+      && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+      && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+      && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+      && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+      && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+      && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+      && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+      && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+      && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+      && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+      && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+      && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+      && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+      && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646.  */
+error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gnu-gperf@gnu.org>."
+#endif
+
+/* maximum key range = 37, duplicates = 0 */
+
+#ifndef GPERF_DOWNCASE
+#define GPERF_DOWNCASE 1
+static unsigned char gperf_downcase[256] =
+  {
+      0,   1,   2,   3,   4,   5,   6,   7,   8,   9,  10,  11,  12,  13,  14,
+     15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
+     30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,
+     45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
+     60,  61,  62,  63,  64,  97,  98,  99, 100, 101, 102, 103, 104, 105, 106,
+    107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121,
+    122,  91,  92,  93,  94,  95,  96,  97,  98,  99, 100, 101, 102, 103, 104,
+    105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
+    120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+    135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
+    150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164,
+    165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179,
+    180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
+    195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209,
+    210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224,
+    225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
+    240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254,
+    255
+  };
+#endif
+
+#ifndef GPERF_CASE_STRNCMP
+#define GPERF_CASE_STRNCMP 1
+static int
+gperf_case_strncmp (s1, s2, n)
+     register const char *s1;
+     register const char *s2;
+     register unsigned int n;
+{
+  for (; n > 0;)
+    {
+      unsigned char c1 = gperf_downcase[(unsigned char)*s1++];
+      unsigned char c2 = gperf_downcase[(unsigned char)*s2++];
+      if (c1 != 0 && c1 == c2)
+        {
+          n--;
+          continue;
+        }
+      return (int)c1 - (int)c2;
+    }
+  return 0;
+}
+#endif
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+hash_block_tag (str, len)
+     register const char *str;
+     register unsigned int len;
+{
+  static const unsigned char asso_values[] =
+    {
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+       8, 30, 25, 20, 15, 10, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38,  0, 38,  0, 38,
+       5,  5,  5, 15,  0, 38, 38,  0, 15, 10,
+       0, 38, 38, 15,  0,  5, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38,  0, 38,
+       0, 38,  5,  5,  5, 15,  0, 38, 38,  0,
+      15, 10,  0, 38, 38, 15,  0,  5, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
+      38, 38, 38, 38, 38, 38, 38
+    };
+  register int hval = len;
+
+  switch (hval)
+    {
+      default:
+        hval += asso_values[(unsigned char)str[1]+1];
+      /*FALLTHROUGH*/
+      case 1:
+        hval += asso_values[(unsigned char)str[0]];
+        break;
+    }
+  return hval;
+}
+
+#ifdef __GNUC__
+__inline
+#ifdef __GNUC_STDC_INLINE__
+__attribute__ ((__gnu_inline__))
+#endif
+#endif
+const char *
+find_block_tag (str, len)
+     register const char *str;
+     register unsigned int len;
+{
+  enum
+    {
+      TOTAL_KEYWORDS = 23,
+      MIN_WORD_LENGTH = 1,
+      MAX_WORD_LENGTH = 10,
+      MIN_HASH_VALUE = 1,
+      MAX_HASH_VALUE = 37
+    };
+
+  static const char * const wordlist[] =
+    {
+      "",
+      "p",
+      "dl",
+      "div",
+      "math",
+      "table",
+      "",
+      "ul",
+      "del",
+      "form",
+      "blockquote",
+      "figure",
+      "ol",
+      "fieldset",
+      "",
+      "h1",
+      "",
+      "h6",
+      "pre",
+      "", "",
+      "script",
+      "h5",
+      "noscript",
+      "", "",
+      "iframe",
+      "h4",
+      "ins",
+      "", "", "",
+      "h3",
+      "", "", "", "",
+      "h2"
+    };
+
+  if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+    {
+      register int key = hash_block_tag (str, len);
+
+      if (key <= MAX_HASH_VALUE && key >= 0)
+        {
+          register const char *s = wordlist[key];
+
+          if ((((unsigned char)*str ^ (unsigned char)*s) & ~32) == 0 && !gperf_case_strncmp (str, s, len) && s[len] == '\0')
+            return s;
+        }
+    }
+  return 0;
+}
diff --git a/cbits/markdown.c b/cbits/markdown.c
--- a/cbits/markdown.c
+++ b/cbits/markdown.c
@@ -18,49 +18,58 @@
  */
 
 #include "markdown.h"
-#include "array.h"
+#include "stack.h"
 
 #include <assert.h>
 #include <string.h>
-//#include <strings.h> /* for strncasecmp */
 #include <ctype.h>
 #include <stdio.h>
 
+#define REF_TABLE_SIZE 8
+
 #define BUFFER_BLOCK 0
 #define BUFFER_SPAN 1
 
 #define MKD_LI_END 8	/* internal list flag */
 
+#define gperf_case_strncmp(s1, s2, n) strncasecmp(s1, s2, n)
+#define GPERF_DOWNCASE 1
+#define GPERF_CASE_STRNCMP 1
+#include "html_blocks.h"
+
 /***************
  * LOCAL TYPES *
  ***************/
 
-/* link_ref • reference to a link */
+/* link_ref: reference to a link */
 struct link_ref {
-	struct buf *id;
-	struct buf *link;
-	struct buf *title;
+	unsigned int id;
+
+	struct buf link;
+	struct buf title;
+
+	struct link_ref *next;
 };
 
-/* char_trigger • function pointer to render active chars */
+/* char_trigger: function pointer to render active chars */
 /*   returns the number of chars taken care of */
 /*   data is the pointer of the beginning of the span */
 /*   offset is the number of valid chars before data */
-struct render;
+struct sd_markdown;
 typedef size_t
-(*char_trigger)(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
+(*char_trigger)(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
 
-static size_t char_emphasis(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_linebreak(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_codespan(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_escape(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_langle_tag(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_autolink_url(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
-static size_t char_superscript(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size);
+static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_link(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
+static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size);
 
 enum markdown_char_t {
 	MD_CHAR_NONE = 0,
@@ -93,84 +102,46 @@
 };
 
 /* render • structure containing one particular render */
-struct render {
+struct sd_markdown {
 	struct sd_callbacks	cb;
 	void *opaque;
 
-	struct array refs;
+	struct link_ref *refs[REF_TABLE_SIZE];
 	char active_char[256];
-	struct parray work_bufs[2];
+	struct stack work_bufs[2];
 	unsigned int ext_flags;
 	size_t max_nesting;
 };
 
-/* html_tag • structure for quick HTML tag search (inspired from discount) */
-struct html_tag {
-	const char *text;
-	size_t size;
-};
+/***************************
+ * HELPER FUNCTIONS *
+ ***************************/
 
 static inline struct buf *
-rndr_newbuf(struct render *rndr, int type)
+rndr_newbuf(struct sd_markdown *rndr, int type)
 {
 	static const size_t buf_size[2] = {256, 64};
 	struct buf *work = NULL;
-	struct parray *queue = &rndr->work_bufs[type];
+	struct stack *pool = &rndr->work_bufs[type];
 
-	if (queue->size < queue->asize) {
-		work = queue->item[queue->size++];
+	if (pool->size < pool->asize &&
+		pool->item[pool->size] != NULL) {
+		work = pool->item[pool->size++];
 		work->size = 0;
 	} else {
 		work = bufnew(buf_size[type]);
-		parr_push(queue, work);
+		stack_push(pool, work);
 	}
 
 	return work;
 }
 
 static inline void
-rndr_popbuf(struct render *rndr, int type)
+rndr_popbuf(struct sd_markdown *rndr, int type)
 {
 	rndr->work_bufs[type].size--;
 }
 
-/********************
- * GLOBAL VARIABLES *
- ********************/
-
-/* block_tags • recognised block tags, sorted by cmp_html_tag */
-static struct html_tag block_tags[] = {
-/*0*/	{ "p",		1 },
-	{ "dl",		2 },
-	{ "h1",		2 },
-	{ "h2",		2 },
-	{ "h3",		2 },
-	{ "h4",		2 },
-	{ "h5",		2 },
-	{ "h6",		2 },
-	{ "ol",		2 },
-	{ "ul",		2 },
-	{ "del",	3 }, /* 10 */
-	{ "div",	3 },
-	{ "ins",	3 }, /* 12 */
-	{ "pre",	3 },
-	{ "form",	4 },
-	{ "math",	4 },
-	{ "table",	5 },
-	{ "figure",	6 },
-	{ "iframe",	6 },
-	{ "script",	6 },
-	{ "fieldset",	8 },
-	{ "noscript",	8 },
-	{ "blockquote",	10 }
-};
-
-#define INS_TAG (block_tags + 12)
-#define DEL_TAG (block_tags + 10)
-
-/***************************
- * HELPER FUNCTIONS *
- ***************************/
 static void
 unscape_text(struct buf *ob, struct buf *src)
 {
@@ -191,54 +162,72 @@
 	}
 }
 
-/* cmp_link_ref • comparison function for link_ref sorted arrays */
-static int
-cmp_link_ref(void *key, void *array_entry)
+static unsigned int
+hash_link_ref(char *link_ref, size_t length)
 {
-	struct link_ref *lr = array_entry;
-	return bufcasecmp(key, lr->id);
+	size_t i;
+	unsigned int hash = 0;
+
+	for (i = 0; i < length; ++i)
+		hash = tolower(link_ref[i]) + (hash << 6) + (hash << 16) - hash;
+
+	return hash;
 }
 
-/* cmp_link_ref_sort • comparison function for link_ref qsort */
-static int
-cmp_link_ref_sort(const void *a, const void *b)
+static void add_link_ref(
+	struct link_ref **references,
+	char *name, size_t name_size,
+	char *link, size_t link_size,
+	char *title, size_t title_size)
 {
-	const struct link_ref *lra = a;
-	const struct link_ref *lrb = b;
-	return bufcasecmp(lra->id, lrb->id);
+	struct link_ref *ref = calloc(1, sizeof(struct link_ref));
+
+	if (!ref)
+		return;
+
+	ref->id = hash_link_ref(name, name_size);
+	ref->next = references[ref->id % REF_TABLE_SIZE];
+
+	ref->link.data = link;
+	ref->link.size = link_size;
+
+	ref->title.data = title;
+	ref->title.size = title_size;
+
+	references[ref->id % REF_TABLE_SIZE] = ref;
 }
 
-/* cmp_html_tag • comparison function for bsearch() (stolen from discount) */
-static int
-cmp_html_tag(const void *a, const void *b)
+static struct link_ref *
+find_link_ref(struct link_ref **references, char *name, size_t length)
 {
-	const struct html_tag *hta = a;
-	const struct html_tag *htb = b;
-	if (hta->size != htb->size) return (int)(hta->size - htb->size);
-	return strncasecmp(hta->text, htb->text, hta->size);
-}
+	unsigned int hash = hash_link_ref(name, length);
+	struct link_ref *ref = NULL;
 
+	ref = references[hash % REF_TABLE_SIZE];
 
-/* find_block_tag • returns the current block tag */
-static struct html_tag *
-find_block_tag(char *data, size_t size)
+	while (ref != NULL) {
+		if (ref->id == hash)
+			return ref;
+
+		ref = ref->next;
+	}
+
+	return NULL;
+}
+
+static void
+free_link_refs(struct link_ref **references)
 {
-	size_t i = 0;
-	struct html_tag key;
+	size_t i;
 
-	/* looking for the word end */
-	while (i < size && ((data[i] >= '0' && data[i] <= '9')
-				|| (data[i] >= 'A' && data[i] <= 'Z')
-				|| (data[i] >= 'a' && data[i] <= 'z')))
-		i++;
-	if (i >= size) return 0;
+	for (i = 0; i < REF_TABLE_SIZE; ++i) {
+		struct link_ref *r = references[i];
+		struct link_ref *next;
 
-	/* binary search of the tag */
-	key.text = data;
-	key.size = i;
-	return bsearch(&key, block_tags,
-				sizeof block_tags / sizeof block_tags[0],
-				sizeof block_tags[0], cmp_html_tag);
+		while (r) {
+			next = r->next; free(r); r = next;
+		}
+	}
 }
 
 /****************************
@@ -340,7 +329,7 @@
 
 /* parse_inline • parses inline markdown elements */
 static void
-parse_inline(struct buf *ob, struct render *rndr, char *data, size_t size)
+parse_inline(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t i = 0, end = 0;
 	char action = 0;
@@ -371,10 +360,10 @@
 		end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i);
 		if (!end) /* no action from the callback */
 			end = i + 1;
-		else { 
+		else {
 			i += end;
 			end = i;
-		} 
+		}
 	}
 }
 
@@ -440,12 +429,20 @@
 			if (i >= size)
 				return tmp_i;
 
-			if (data[i] != '[' && data[i] != '(') { /* not a link*/
-				if (tmp_i) return tmp_i;
-				else continue;
+			switch (data[i]) {
+			case '[':
+				cc = ']'; break;
+
+			case '(':
+				cc = ')'; break;
+
+			default:
+				if (tmp_i)
+					return tmp_i;
+				else
+					continue;
 			}
 
-			cc = data[i];
 			i++;
 			while (i < size && data[i] != cc) {
 				if (!tmp_i && data[i] == c) tmp_i = i;
@@ -465,7 +462,7 @@
 /* parse_emph1 • parsing single emphase */
 /* closed by a symbol not preceded by whitespace and not followed by symbol */
 static size_t
-parse_emph1(struct buf *ob, struct render *rndr, char *data, size_t size, char c)
+parse_emph1(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, char c)
 {
 	size_t i = 0, len;
 	struct buf *work = 0;
@@ -482,11 +479,6 @@
 		i += len;
 		if (i >= size) return 0;
 
-		if (i + 1 < size && data[i + 1] == c) {
-			i++;
-			continue;
-		}
-
 		if (data[i] == c && !isspace(data[i - 1])) {
 
 			if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {
@@ -507,7 +499,7 @@
 
 /* parse_emph2 • parsing single emphase */
 static size_t
-parse_emph2(struct buf *ob, struct render *rndr, char *data, size_t size, char c)
+parse_emph2(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, char c)
 {
 	int (*render_method)(struct buf *ob, struct buf *text, void *opaque);
 	size_t i = 0, len;
@@ -518,7 +510,7 @@
 
 	if (!render_method)
 		return 0;
-	
+
 	while (i < size) {
 		len = find_emph_char(data + i, size - i, c);
 		if (!len) return 0;
@@ -539,7 +531,7 @@
 /* parse_emph3 • parsing single emphase */
 /* finds the first closing tag, and delegates to the other emph */
 static size_t
-parse_emph3(struct buf *ob, struct render *rndr, char *data, size_t size, char c)
+parse_emph3(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, char c)
 {
 	size_t i = 0, len;
 	int r;
@@ -575,12 +567,12 @@
 			else return len - 1;
 		}
 	}
-	return 0; 
+	return 0;
 }
 
 /* char_emphasis • single and double emphasis parsing */
 static size_t
-char_emphasis(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_emphasis(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	char c = data[0];
 	size_t ret;
@@ -608,13 +600,13 @@
 		return ret + 3;
 	}
 
-	return 0; 
+	return 0;
 }
 
 
 /* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */
 static size_t
-char_linebreak(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_linebreak(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	if (offset < 2 || data[-1] != ' ' || data[-2] != ' ')
 		return 0;
@@ -629,7 +621,7 @@
 
 /* char_codespan • '`' parsing a code span (assuming codespan != 0) */
 static size_t
-char_codespan(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_codespan(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	size_t end, nb = 0, i, f_begin, f_end;
 
@@ -672,7 +664,7 @@
 
 /* char_escape • '\\' backslash escape */
 static size_t
-char_escape(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_escape(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>";
 	struct buf work = { 0, 0, 0, 0, 0 };
@@ -695,7 +687,7 @@
 /* char_entity • '&' escaped when it doesn't belong to an entity */
 /* valid entities are assumed to be anything matching &#?[A-Za-z0-9]+; */
 static size_t
-char_entity(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_entity(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	size_t end = 1;
 	struct buf work;
@@ -723,7 +715,7 @@
 
 /* char_langle_tag • '<' when tags or autolinks are allowed */
 static size_t
-char_langle_tag(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_langle_tag(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	enum mkd_autolink altype = MKDA_NOT_AUTOLINK;
 	size_t end = tag_length(data, size, &altype);
@@ -748,7 +740,7 @@
 }
 
 static size_t
-char_autolink_www(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_autolink_www(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	struct buf *link, *link_url;
 	size_t link_len, rewind;
@@ -773,7 +765,7 @@
 }
 
 static size_t
-char_autolink_email(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_autolink_email(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	struct buf *link;
 	size_t link_len, rewind;
@@ -793,7 +785,7 @@
 }
 
 static size_t
-char_autolink_url(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_autolink_url(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	struct buf *link;
 	size_t link_len, rewind;
@@ -814,7 +806,7 @@
 
 /* char_link • '[': parsing a link or an image */
 static size_t
-char_link(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_link(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	int is_img = (offset && data[-1] == '!'), level;
 	size_t i = 1, txt_e, link_b = 0, link_e = 0, title_b = 0, title_e = 0;
@@ -961,12 +953,14 @@
 			id.size = link_e - link_b;
 		}
 
-		lr = arr_sorted_find(&rndr->refs, &id, cmp_link_ref);
-		if (!lr) goto cleanup;
+		lr = find_link_ref(rndr->refs, id.data, id.size);
+		if (!lr)
+			goto cleanup;
 
 		/* keeping link and title from link_ref */
-		link = lr->link;
-		title = lr->title;
+		link = &lr->link;
+		if (lr->title.size)
+			title = &lr->title;
 		i++;
 	}
 
@@ -995,12 +989,14 @@
 		}
 
 		/* finding the link_ref */
-		lr = arr_sorted_find(&rndr->refs, &id, cmp_link_ref);
-		if (!lr) goto cleanup;
+		lr = find_link_ref(rndr->refs, id.data, id.size);
+		if (!lr)
+			goto cleanup;
 
 		/* keeping link and title from link_ref */
-		link = lr->link;
-		title = lr->title;
+		link = &lr->link;
+		if (lr->title.size)
+			title = &lr->title;
 
 		/* rewinding the whitespace */
 		i = txt_e + 1;
@@ -1035,7 +1031,7 @@
 }
 
 static size_t
-char_superscript(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
+char_superscript(struct buf *ob, struct sd_markdown *rndr, char *data, size_t offset, size_t size)
 {
 	size_t sup_start, sup_len;
 	struct buf *sup;
@@ -1191,7 +1187,7 @@
 
 /* is_atxheader • returns whether the line is a hash-prefixed header */
 static int
-is_atxheader(struct render *rndr, char *data, size_t size)
+is_atxheader(struct sd_markdown *rndr, char *data, size_t size)
 {
 	if (data[0] != '#')
 		return 0;
@@ -1230,6 +1226,20 @@
 	return 0;
 }
 
+static int
+is_next_headerline(char *data, size_t size)
+{
+	size_t i = 0;
+	
+	while (i < size && data[i] != '\n')
+		i++;
+
+	if (++i >= size)
+		return 0;
+
+	return is_headerline(data + i, size - i);
+}
+
 /* prefix_quote • returns blockquote prefix length */
 static size_t
 prefix_quote(char *data, size_t size)
@@ -1260,13 +1270,25 @@
 prefix_oli(char *data, size_t size)
 {
 	size_t i = 0;
+
 	if (i < size && data[i] == ' ') i++;
 	if (i < size && data[i] == ' ') i++;
 	if (i < size && data[i] == ' ') i++;
-	if (i >= size || data[i] < '0' || data[i] > '9') return 0;
-	while (i < size && data[i] >= '0' && data[i] <= '9') i++;
-	if (i + 1 >= size || data[i] != '.'
-	|| (data[i + 1] != ' ' && data[i + 1] != '\t')) return 0;
+
+	if (i >= size || data[i] < '0' || data[i] > '9')
+		return 0;
+
+	while (i < size && data[i] >= '0' && data[i] <= '9')
+		i++;
+
+	if (i + 1 >= size ||
+		data[i] != '.' ||
+		(data[i + 1] != ' ' && data[i + 1] != '\t'))
+		return 0;
+
+	if (is_next_headerline(data + i, size - i))
+		return 0;
+
 	return i + 2;
 }
 
@@ -1275,25 +1297,31 @@
 prefix_uli(char *data, size_t size)
 {
 	size_t i = 0;
+
 	if (i < size && data[i] == ' ') i++;
 	if (i < size && data[i] == ' ') i++;
 	if (i < size && data[i] == ' ') i++;
-	if (i + 1 >= size
-	|| (data[i] != '*' && data[i] != '+' && data[i] != '-')
-	|| (data[i + 1] != ' ' && data[i + 1] != '\t'))
+
+	if (i + 1 >= size || 
+		(data[i] != '*' && data[i] != '+' && data[i] != '-') ||
+		(data[i + 1] != ' ' && data[i + 1] != '\t'))
 		return 0;
+
+	if (is_next_headerline(data + i, size - i))
+		return 0;
+
 	return i + 2;
 }
 
 
 /* parse_block • parsing of one block, returning next char to parse */
-static void parse_block(struct buf *ob, struct render *rndr,
+static void parse_block(struct buf *ob, struct sd_markdown *rndr,
 			char *data, size_t size);
 
 
 /* parse_blockquote • handles parsing of a blockquote fragment */
 static size_t
-parse_blockquote(struct buf *ob, struct render *rndr, char *data, size_t size)
+parse_blockquote(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t beg, end = 0, pre, work_size = 0;
 	char *work_data = 0;
@@ -1334,11 +1362,11 @@
 }
 
 static size_t
-parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, int do_render);
+parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, int do_render);
 
 /* parse_blockquote • handles parsing of a regular paragraph */
 static size_t
-parse_paragraph(struct buf *ob, struct render *rndr, char *data, size_t size)
+parse_paragraph(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t i = 0, end = 0;
 	int level = 0;
@@ -1418,7 +1446,7 @@
 
 /* parse_fencedcode • handles parsing of a block-level code fragment */
 static size_t
-parse_fencedcode(struct buf *ob, struct render *rndr, char *data, size_t size)
+parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t beg, end;
 	struct buf *work = 0;
@@ -1461,7 +1489,7 @@
 }
 
 static size_t
-parse_blockcode(struct buf *ob, struct render *rndr, char *data, size_t size)
+parse_blockcode(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t beg, end, pre;
 	struct buf *work = 0;
@@ -1504,7 +1532,7 @@
 /* parse_listitem • parsing of a single list item */
 /*	assuming initial prefix is already removed */
 static size_t
-parse_listitem(struct buf *ob, struct render *rndr, char *data, size_t size, int *flags)
+parse_listitem(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, int *flags)
 {
 	struct buf *work = 0, *inter = 0;
 	size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;
@@ -1594,7 +1622,7 @@
 		/* intermediate render of block li */
 		if (sublist && sublist < work->size) {
 			parse_block(inter, rndr, work->data, sublist);
-			parse_block(inter, rndr, work->data + sublist, work->size - sublist); 
+			parse_block(inter, rndr, work->data + sublist, work->size - sublist);
 		}
 		else
 			parse_block(inter, rndr, work->data, work->size);
@@ -1620,7 +1648,7 @@
 
 /* parse_list • parsing ordered or unordered list block */
 static size_t
-parse_list(struct buf *ob, struct render *rndr, char *data, size_t size, int flags)
+parse_list(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, int flags)
 {
 	struct buf *work = 0;
 	size_t i = 0, j;
@@ -1643,7 +1671,7 @@
 
 /* parse_atxheader • parsing of atx-style headers */
 static size_t
-parse_atxheader(struct buf *ob, struct render *rndr, char *data, size_t size)
+parse_atxheader(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t level = 0;
 	size_t i, end, skip;
@@ -1680,20 +1708,18 @@
 /* htmlblock_end • checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */
 /*	returns the length on match, 0 otherwise */
 static size_t
-htmlblock_end(struct html_tag *tag, struct render *rndr, char *data, size_t size)
+htmlblock_end(const char *tag, size_t tag_len, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t i, w;
 
-	/* assuming data[0] == '<' && data[1] == '/' already tested */
-
 	/* checking if tag is a match */
-	if (tag->size + 3 >= size
-	|| strncasecmp(data + 2, tag->text, tag->size)
-	|| data[tag->size + 2] != '>')
+	if (tag_len + 3 >= size ||
+		strncasecmp(data + 2, tag, tag_len) != 0 ||
+		data[tag_len + 2] != '>')
 		return 0;
 
 	/* checking white lines */
-	i = tag->size + 3;
+	i = tag_len + 3;
 	w = 0;
 	if (i < size && (w = is_empty(data + i, size - i)) == 0)
 		return 0; /* non-blank after tag */
@@ -1714,10 +1740,10 @@
 
 /* parse_htmlblock • parsing of inline HTML block */
 static size_t
-parse_htmlblock(struct buf *ob, struct render *rndr, char *data, size_t size, int do_render)
+parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, int do_render)
 {
 	size_t i, j = 0;
-	struct html_tag *curtag;
+	const char *curtag;
 	int found;
 	struct buf work = { data, 0, 0, 0, 0 };
 
@@ -1745,7 +1771,7 @@
 				if (do_render && rndr->cb.blockhtml)
 					rndr->cb.blockhtml(ob, &work, rndr->opaque);
 				return work.size;
-			} 
+			}
 		}
 
 		/* HR, which is the only self-closing block tag considered */
@@ -1763,7 +1789,7 @@
 						rndr->cb.blockhtml(ob, &work, rndr->opaque);
 					return work.size;
 				}
-			} 
+			}
 		}
 
 		/* no special case recognised */
@@ -1777,24 +1803,25 @@
 
 	/* if not found, trying a second pass looking for indented match */
 	/* but not if tag is "ins" or "del" (following original Markdown.pl) */
-	if (curtag != INS_TAG && curtag != DEL_TAG) {
+	if (strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) {
+		size_t tag_size = strlen(curtag);
 		i = 1;
 		while (i < size) {
 			i++;
 			while (i < size && !(data[i - 1] == '<' && data[i] == '/'))
 				i++;
 
-			if (i + 2 + curtag->size >= size)
+			if (i + 2 + tag_size >= size)
 				break;
 
-			j = htmlblock_end(curtag, rndr, data + i - 1, size - i + 1);
+			j = htmlblock_end(curtag, tag_size, rndr, data + i - 1, size - i + 1);
 
 			if (j) {
 				i += j - 1;
 				found = 1;
 				break;
 			}
-		} 
+		}
 	}
 
 	if (!found) return 0;
@@ -1808,7 +1835,7 @@
 }
 
 static void
-parse_table_row(struct buf *ob, struct render *rndr, char *data, size_t size, size_t columns, int *col_data)
+parse_table_row(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, size_t columns, int *col_data)
 {
 	size_t i = 0, col;
 	struct buf *row_work = 0;
@@ -1858,7 +1885,7 @@
 }
 
 static size_t
-parse_table_header(struct buf *ob, struct render *rndr, char *data, size_t size, size_t *columns, int **column_data)
+parse_table_header(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size, size_t *columns, int **column_data)
 {
 	int pipes;
 	size_t i = 0, col, header_end, under_end;
@@ -1933,7 +1960,7 @@
 }
 
 static size_t
-parse_table(struct buf *ob, struct render *rndr, char *data, size_t size)
+parse_table(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t i;
 
@@ -1980,7 +2007,7 @@
 
 /* parse_block • parsing of one block, returning next char to parse */
 static void
-parse_block(struct buf *ob, struct render *rndr, char *data, size_t size)
+parse_block(struct buf *ob, struct sd_markdown *rndr, char *data, size_t size)
 {
 	size_t beg, end, i;
 	char *txt_data;
@@ -2047,7 +2074,7 @@
 
 /* is_ref • returns whether a line is a reference or not */
 static int
-is_ref(char *data, size_t beg, size_t end, size_t *last, struct array *refs)
+is_ref(char *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)
 {
 /*	int n; */
 	size_t i = 0;
@@ -2055,8 +2082,6 @@
 	size_t link_offset, link_end;
 	size_t title_offset, title_end;
 	size_t line_end;
-	struct link_ref *lr;
-/*	struct buf id = { 0, 0, 0, 0, 0 }; / * volatile buf for id search */
 
 	/* up to 3 optional leading spaces */
 	if (beg + 3 >= end) return 0;
@@ -2130,22 +2155,33 @@
 		&& (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {
 			line_end = title_end;
 			title_end = i; } }
-	if (!line_end) return 0; /* garbage after the link */
 
+	if (!line_end)
+		return 0; /* garbage after the link */
+
 	/* a valid ref has been found, filling-in return structures */
-	if (last) *last = line_end;
-	if (!refs) return 1;
-	lr = arr_item(refs, arr_newitem(refs));
-	lr->id = bufnew(id_end - id_offset);
-	bufput(lr->id, data + id_offset, id_end - id_offset);
-	lr->link = bufnew(link_end - link_offset);
-	bufput(lr->link, data + link_offset, link_end - link_offset);
-	if (title_end > title_offset) {
-		lr->title = bufnew(title_end - title_offset);
-		bufput(lr->title, data + title_offset,
-					title_end - title_offset); }
-	else lr->title = 0;
-	return 1; 
+	if (last)
+		*last = line_end;
+
+	if (refs) {
+		char *title_str = NULL;
+		size_t title_size = 0;
+
+		if (title_end > title_offset) {
+			title_str = data + title_offset;
+			title_size = title_end - title_offset;
+		}
+
+		add_link_ref( refs,
+			data + id_offset, /* identifier */
+			id_end - id_offset,
+			data + link_offset, /* link url */
+			link_end - link_offset,
+			title_str, /* title (optional) */
+			title_size);
+	}
+
+	return 1;
 }
 
 static void expand_tabs(struct buf *ob, const char *line, size_t size)
@@ -2177,80 +2213,87 @@
  * EXPORTED FUNCTIONS *
  **********************/
 
-/* markdown • parses the input buffer and renders it into the output buffer */
-void
-sd_markdown(struct buf *ob,
-	const struct buf *ib,
+struct sd_markdown *
+sd_markdown_new(
 	unsigned int extensions,
+	size_t max_nesting,
 	const struct sd_callbacks *callbacks,
-	void *opaque) {
-
-	static const float MARKDOWN_GROW_FACTOR = 1.4f;
-
-	struct link_ref *lr;
-	struct buf *text;
-	size_t i, beg, end;
-	struct render rndr;
-
-	/* filling the render structure */
-	if (!callbacks)
-		return;
+	void *opaque)
+{
+	struct sd_markdown *md = NULL;
 
-	text = bufnew(64);
-	if (!text)
-		return;
+	assert(max_nesting > 0 && callbacks);
 
-	/* Preallocate enough space for our buffer to avoid expanding while copying */
-	bufgrow(text, ib->size);
+	md = malloc(sizeof(struct sd_markdown));
+	if (!md)
+		return NULL;
 
-	memcpy(&rndr.cb, callbacks, sizeof(struct sd_callbacks));
-	arr_init(&rndr.refs, sizeof (struct link_ref));
-	parr_init(&rndr.work_bufs[BUFFER_BLOCK]);
-	parr_init(&rndr.work_bufs[BUFFER_SPAN]);
+	memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));
 
-/*	for (i = 0; i < 256; i++)
-		rndr.active_char[i] = 0; */
+	stack_init(&md->work_bufs[BUFFER_BLOCK], 4);
+	stack_init(&md->work_bufs[BUFFER_SPAN], 8);
 
-	memset(rndr.active_char, 0x0, 256);
+	memset(md->active_char, 0x0, 256);
 
-	if (rndr.cb.emphasis || rndr.cb.double_emphasis || rndr.cb.triple_emphasis) {
-		rndr.active_char['*'] = MD_CHAR_EMPHASIS;
-		rndr.active_char['_'] = MD_CHAR_EMPHASIS;
+	if (md->cb.emphasis || md->cb.double_emphasis || md->cb.triple_emphasis) {
+		md->active_char['*'] = MD_CHAR_EMPHASIS;
+		md->active_char['_'] = MD_CHAR_EMPHASIS;
 		if (extensions & MKDEXT_STRIKETHROUGH)
-			rndr.active_char['~'] = MD_CHAR_EMPHASIS;
+			md->active_char['~'] = MD_CHAR_EMPHASIS;
 	}
 
-	if (rndr.cb.codespan)
-		rndr.active_char['`'] = MD_CHAR_CODESPAN;
+	if (md->cb.codespan)
+		md->active_char['`'] = MD_CHAR_CODESPAN;
 
-	if (rndr.cb.linebreak)
-		rndr.active_char['\n'] = MD_CHAR_LINEBREAK;
+	if (md->cb.linebreak)
+		md->active_char['\n'] = MD_CHAR_LINEBREAK;
 
-	if (rndr.cb.image || rndr.cb.link)
-		rndr.active_char['['] = MD_CHAR_LINK;
+	if (md->cb.image || md->cb.link)
+		md->active_char['['] = MD_CHAR_LINK;
 
-	rndr.active_char['<'] = MD_CHAR_LANGLE;
-	rndr.active_char['\\'] = MD_CHAR_ESCAPE;
-	rndr.active_char['&'] = MD_CHAR_ENTITITY;
+	md->active_char['<'] = MD_CHAR_LANGLE;
+	md->active_char['\\'] = MD_CHAR_ESCAPE;
+	md->active_char['&'] = MD_CHAR_ENTITITY;
 
 	if (extensions & MKDEXT_AUTOLINK) {
-		rndr.active_char[':'] = MD_CHAR_AUTOLINK_URL;
-		rndr.active_char['@'] = MD_CHAR_AUTOLINK_EMAIL;
-		rndr.active_char['w'] = MD_CHAR_AUTOLINK_WWW;
+		md->active_char[':'] = MD_CHAR_AUTOLINK_URL;
+		md->active_char['@'] = MD_CHAR_AUTOLINK_EMAIL;
+		md->active_char['w'] = MD_CHAR_AUTOLINK_WWW;
 	}
 
 	if (extensions & MKDEXT_SUPERSCRIPT)
-		rndr.active_char['^'] = MD_CHAR_SUPERSCRIPT;
+		md->active_char['^'] = MD_CHAR_SUPERSCRIPT;
 
 	/* Extension data */
-	rndr.ext_flags = extensions;
-	rndr.opaque = opaque;
-	rndr.max_nesting = 16;
+	md->ext_flags = extensions;
+	md->opaque = opaque;
+	md->max_nesting = max_nesting;
 
+	return md;
+}
+
+void
+sd_markdown_render(struct buf *ob, const struct buf *ib, struct sd_markdown *md)
+{
+	static const float MARKDOWN_GROW_FACTOR = 1.4f;
+
+	struct buf *text;
+	size_t beg, end;
+
+	text = bufnew(64);
+	if (!text)
+		return;
+
+	/* Preallocate enough space for our buffer to avoid expanding while copying */
+	bufgrow(text, ib->size);
+
+	/* reset the references table */
+	memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));
+
 	/* first pass: looking for references, copying everything else */
 	beg = 0;
 	while (beg < ib->size) /* iterating over lines */
-		if (is_ref(ib->data, beg, ib->size, &end, &rndr.refs))
+		if (is_ref(ib->data, beg, ib->size, &end, md->refs))
 			beg = end;
 		else { /* skipping to the next line */
 			end = beg;
@@ -2271,50 +2314,47 @@
 			beg = end;
 		}
 
-	/* sorting the reference array */
-	if (rndr.refs.size)
-		qsort(rndr.refs.base, rndr.refs.size, rndr.refs.unit, cmp_link_ref_sort);
-
 	/* pre-grow the output buffer to minimize allocations */
 	bufgrow(ob, text->size * MARKDOWN_GROW_FACTOR);
 
 	/* second pass: actual rendering */
-	if (rndr.cb.doc_header)
-		rndr.cb.doc_header(ob, rndr.opaque);
+	if (md->cb.doc_header)
+		md->cb.doc_header(ob, md->opaque);
 
 	if (text->size) {
 		/* adding a final newline if not already present */
 		if (text->data[text->size - 1] != '\n' &&  text->data[text->size - 1] != '\r')
 			bufputc(text, '\n');
 
-		parse_block(ob, &rndr, text->data, text->size);
+		parse_block(ob, md, text->data, text->size);
 	}
 
-	if (rndr.cb.doc_footer)
-		rndr.cb.doc_footer(ob, rndr.opaque);
+	if (md->cb.doc_footer)
+		md->cb.doc_footer(ob, md->opaque);
 
 	/* clean-up */
 	bufrelease(text);
-	lr = rndr.refs.base;
-	for (i = 0; i < (size_t)rndr.refs.size; i++) {
-		bufrelease(lr[i].id);
-		bufrelease(lr[i].link);
-		bufrelease(lr[i].title);
-	}
+	free_link_refs(md->refs);
 
-	arr_free(&rndr.refs);
+	assert(md->work_bufs[BUFFER_SPAN].size == 0);
+	assert(md->work_bufs[BUFFER_BLOCK].size == 0);
+}
 
-	assert(rndr.work_bufs[BUFFER_SPAN].size == 0);
-	assert(rndr.work_bufs[BUFFER_BLOCK].size == 0);
+void
+sd_markdown_free(struct sd_markdown *md)
+{
+	size_t i;
 
-	for (i = 0; i < (size_t)rndr.work_bufs[BUFFER_SPAN].asize; ++i)
-		bufrelease(rndr.work_bufs[BUFFER_SPAN].item[i]);
+	for (i = 0; i < (size_t)md->work_bufs[BUFFER_SPAN].asize; ++i)
+		bufrelease(md->work_bufs[BUFFER_SPAN].item[i]);
 
-	for (i = 0; i < (size_t)rndr.work_bufs[BUFFER_BLOCK].asize; ++i)
-		bufrelease(rndr.work_bufs[BUFFER_BLOCK].item[i]);
+	for (i = 0; i < (size_t)md->work_bufs[BUFFER_BLOCK].asize; ++i)
+		bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]);
 
-	parr_free(&rndr.work_bufs[BUFFER_SPAN]);
-	parr_free(&rndr.work_bufs[BUFFER_BLOCK]);
+	stack_free(&md->work_bufs[BUFFER_SPAN]);
+	stack_free(&md->work_bufs[BUFFER_BLOCK]);
+
+	free(md);
 }
 
 void
diff --git a/cbits/markdown.h b/cbits/markdown.h
--- a/cbits/markdown.h
+++ b/cbits/markdown.h
@@ -95,6 +95,8 @@
 	void (*doc_footer)(struct buf *ob, void *opaque);
 };
 
+struct sd_markdown;
+
 /*********
  * FLAGS *
  *********/
@@ -107,11 +109,19 @@
  * EXPORTED FUNCTIONS *
  **********************/
 
-/* sd_markdown * parses the input buffer and renders it into the output buffer */
+extern struct sd_markdown *
+sd_markdown_new(
+	unsigned int extensions,
+	size_t max_nesting,
+	const struct sd_callbacks *callbacks,
+	void *opaque); 
+
 extern void
-sd_markdown(struct buf *ob, const struct buf *ib, unsigned int extensions, const struct sd_callbacks *rndr, void *opaque);
+sd_markdown_render(struct buf *ob, const struct buf *ib, struct sd_markdown *md);
 
-/* sd_version * returns the library version as major.minor.rev */
+extern void
+sd_markdown_free(struct sd_markdown *md);
+
 extern void
 sd_version(int *major, int *minor, int *revision);
 
diff --git a/cbits/stack.c b/cbits/stack.c
new file mode 100644
--- /dev/null
+++ b/cbits/stack.c
@@ -0,0 +1,78 @@
+#include "stack.h"
+#include <string.h>
+
+int
+stack_grow(struct stack *st, size_t new_size)
+{
+	void *new_st;
+
+	if (st->asize >= new_size)
+		return 0;
+
+	new_st = realloc(st->item, new_size * sizeof(void *));
+	if (new_st == NULL)
+		return -1;
+
+	st->item = new_st;
+	st->asize = new_size;
+
+	if (st->size > new_size)
+		st->size = new_size;
+
+	return 0;
+}
+
+void
+stack_free(struct stack *st)
+{
+	if (!st)
+		return;
+
+	free(st->item);
+
+	st->item = NULL;
+	st->size = 0;
+	st->asize = 0;
+}
+
+int
+stack_init(struct stack *st, size_t initial_size)
+{
+	st->item = NULL;
+	st->size = 0;
+	st->asize = 0;
+
+	if (!initial_size)
+		initial_size = 8;
+
+	return stack_grow(st, initial_size);
+}
+
+void *
+stack_pop(struct stack *st)
+{
+	if (!st->size)
+		return NULL;
+
+	return st->item[st->size--];
+}
+
+int
+stack_push(struct stack *st, void *item)
+{
+	if (stack_grow(st, st->size * 2) < 0)
+		return -1;
+
+	st->item[st->size++] = item;
+	return 0;
+}
+
+void *
+stack_top(struct stack *st)
+{
+	if (!st->size)
+		return NULL;
+
+	return st->item[st->size - 1];
+}
+
diff --git a/cbits/stack.h b/cbits/stack.h
new file mode 100644
--- /dev/null
+++ b/cbits/stack.h
@@ -0,0 +1,21 @@
+#ifndef __CR_STACK_H__
+#define __CR_STACK_H__
+
+#include <stdlib.h>
+
+struct stack {
+	void **item;
+	size_t size;
+	size_t asize;
+};
+
+void stack_free(struct stack *);
+int stack_grow(struct stack *, size_t);
+int stack_init(struct stack *, size_t);
+
+int stack_push(struct stack *, void *);
+
+void *stack_pop(struct stack *);
+void *stack_top(struct stack *);
+
+#endif
diff --git a/src/Text/Sundown.hs b/src/Text/Sundown.hs
--- a/src/Text/Sundown.hs
+++ b/src/Text/Sundown.hs
@@ -15,7 +15,7 @@
 > main :: IO ()
 > main = do
 >   input <- liftM (!! 0) getArgs >>= BS.readFile
->   putStrLn $ UTF8.toString $ renderHtml input allExtensions noHtmlModes
+>   putStrLn $ UTF8.toString $ renderHtml input allExtensions noHtmlModes Nothing
 
 -}
 
diff --git a/src/Text/Sundown/Markdown/Foreign.hsc b/src/Text/Sundown/Markdown/Foreign.hsc
--- a/src/Text/Sundown/Markdown/Foreign.hsc
+++ b/src/Text/Sundown/Markdown/Foreign.hsc
@@ -3,7 +3,9 @@
 module Text.Sundown.Markdown.Foreign
        ( Callbacks
        , Extensions (..)
-       , c_sd_markdown
+       , c_sd_markdown_new
+       , c_sd_markdown_render
+       , c_sd_markdown_free
        ) where
 
 import Foreign
@@ -14,8 +16,6 @@
 
 #include "markdown.h"
 
-data Callbacks
-
 -- | A set of switches to enable or disable markdown features.
 data Extensions = Extensions { extNoIntraEmphasis :: Bool
                              , extTables          :: Bool
@@ -35,14 +35,30 @@
                      ]
 
 
+data Callbacks
 
 instance Storable Callbacks where
-    sizeOf _ = (#size struct sd_callbacks)
-    alignment _ = alignment (undefined :: Ptr ())
-    peek _ = error "Callbacks.peek is not implemented"
-    poke _ _ = error "Callbacks.poke is not implemented"
+  sizeOf _ = (#size struct sd_callbacks)
+  alignment _ = alignment (undefined :: Ptr ())
+  peek _ = error "Callbacks.peek is not implemented"
+  poke _ _ = error "Callbacks.poke is not implemented"
 
-c_sd_markdown :: Ptr Buffer -> Ptr Buffer -> Extensions -> Ptr Callbacks -> Ptr () -> IO ()
-c_sd_markdown ob ib exts rndr opaque = c_sd_markdown' ob ib (toCUInt exts) rndr opaque
-foreign import ccall "markdown.h sd_markdown"
-  c_sd_markdown' :: Ptr Buffer -> Ptr Buffer -> CUInt -> Ptr Callbacks -> Ptr () -> IO ()
+data Markdown
+
+instance Storable Markdown where
+  sizeOf _ = error "Markdown.sizeOf is not implemented"
+  alignment _ = alignment (undefined :: Ptr ())
+  peek _ = error "Markdown.peek is not implemented"
+  poke _ = error "Markdown.poke is not implemented"
+
+c_sd_markdown_new :: Extensions -> CSize -> Ptr Callbacks -> Ptr () -> IO (Ptr Markdown)
+c_sd_markdown_new extensions max_nesting callbacks opaque =
+  c_sd_markdown_new' (toCUInt extensions) max_nesting callbacks opaque
+foreign import ccall "markdown.h sd_markdown_new"
+  c_sd_markdown_new' :: CUInt -> CSize -> Ptr Callbacks -> Ptr () -> IO (Ptr Markdown)
+
+foreign import ccall "markdown.h sd_markdown_render"
+  c_sd_markdown_render :: Ptr Buffer -> Ptr Buffer -> Ptr Markdown -> IO ()
+
+foreign import ccall "markdown.h sd_markdown_free"
+  c_sd_markdown_free :: Ptr Markdown -> IO ()
diff --git a/src/Text/Sundown/Renderers/Html.hs b/src/Text/Sundown/Renderers/Html.hs
--- a/src/Text/Sundown/Renderers/Html.hs
+++ b/src/Text/Sundown/Renderers/Html.hs
@@ -13,18 +13,27 @@
 
 import Data.ByteString (ByteString)
 import qualified Data.ByteString as BS
+import Data.Maybe (fromMaybe)
 
 import Text.Sundown.Markdown.Foreign
 import Text.Sundown.Buffer.Foreign
 import Text.Sundown.Renderers.Html.Foreign
 
+defaultMaxNesting :: Int
+defaultMaxNesting = 16
+
 -- | Parses a 'ByteString' containing the markdown, returns the Html
 -- code.
 {-# NOINLINE renderHtml #-}
-renderHtml :: ByteString -> Extensions -> HtmlRenderMode -> ByteString
-renderHtml input exts mode =
+renderHtml :: ByteString
+           -> Extensions
+           -> HtmlRenderMode
+           -> Maybe Int -- ^ The maximum nesting of the HTML. If Nothing, a default value (16)
+                       -- will be used.
+           -> ByteString
+renderHtml input exts mode maxNestingM =
   unsafePerformIO $
-  alloca $ \renderer ->
+  alloca $ \callbacks ->
   alloca $ \options -> do
     -- Allocate buffers
     ob <- c_bufnew 64
@@ -34,8 +43,11 @@
     c_bufputs ib input
     
     -- Do the markdown
-    c_sdhtml_renderer renderer options mode
-    c_sd_markdown ob ib exts renderer (castPtr options)
+    c_sdhtml_renderer callbacks options mode
+    let maxNesting = fromIntegral $ fromMaybe defaultMaxNesting maxNestingM
+    markdown <- c_sd_markdown_new exts maxNesting callbacks (castPtr options)
+    c_sd_markdown_render ob ib markdown
+    c_sd_markdown_free markdown
     
     -- Get the result
     Buffer {bufData = output} <- peek ob
diff --git a/sundown.cabal b/sundown.cabal
--- a/sundown.cabal
+++ b/sundown.cabal
@@ -1,6 +1,6 @@
 Cabal-version:          >= 1.6
 Name:                   sundown
-Version:                0.1.2
+Version:                0.2
 Author:                 Francesco Mazzoli (f@mazzo.li)
 Maintainer:             Francesco Mazzoli (f@mazzo.li)
 Build-Type:             Simple
@@ -35,13 +35,15 @@
                       , Text.Sundown.Renderers.Html.Foreign
                       , Text.Sundown.Flag
                       , Text.Sundown.Markdown.Foreign
-  
-  C-Sources:            cbits/array.c, cbits/buffer.c, cbits/markdown.c,
-                        cbits/html.c, cbits/html_smartypants.c,
-                        cbits/autolink.c
+
+  C-Sources:            cbits/autolink.c, cbits/buffer.c, cbits/html.c,
+                        cbits/html_smartypants.c, cbits/markdown.c,
+                        cbits/stack.c
   
   Include-Dirs:         cbits
   
-  Includes:             array.h, autolink.h, buffer.h, markdown.h, html.h
-  
-  Install-includes:     array.h, autolink.h, buffer.h, markdown.h, html.h
+  Includes:             autolink.h, buffer.h, html.h, html_blocks.h, markdown.h,
+                        stack.h
+
+  Install-includes:     autolink.h, buffer.h, html.h, html_blocks.h, markdown.h,
+                        stack.h
