sundown 0.1.2 → 0.2
raw patch · 15 files changed
+944/−1049 lines, 15 files
Files
- cbits/array.c +0/−300
- cbits/array.h +0/−147
- cbits/autolink.c +2/−2
- cbits/buffer.c +186/−176
- cbits/buffer.h +55/−106
- cbits/html.c +18/−19
- cbits/html_blocks.h +205/−0
- cbits/markdown.c +311/−271
- cbits/markdown.h +13/−3
- cbits/stack.c +78/−0
- cbits/stack.h +21/−0
- src/Text/Sundown.hs +1/−1
- src/Text/Sundown/Markdown/Foreign.hsc +27/−11
- src/Text/Sundown/Renderers/Html.hs +17/−5
- sundown.cabal +10/−8
− cbits/array.c
@@ -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: */
− cbits/array.h
@@ -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: */
cbits/autolink.c view
@@ -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)
cbits/buffer.c view
@@ -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: */
cbits/buffer.h view
@@ -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
cbits/html.c view
@@ -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
+ cbits/html_blocks.h view
@@ -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;+}
cbits/markdown.c view
@@ -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
cbits/markdown.h view
@@ -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);
+ cbits/stack.c view
@@ -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];+}+
+ cbits/stack.h view
@@ -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
src/Text/Sundown.hs view
@@ -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 -}
src/Text/Sundown/Markdown/Foreign.hsc view
@@ -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 ()
src/Text/Sundown/Renderers/Html.hs view
@@ -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
sundown.cabal view
@@ -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