packages feed

sundown 0.4.2 → 0.5

raw patch · 43 files changed

+5365/−5298 lines, 43 filesdep +textdep +utf8-string

Dependencies added: text, utf8-string

Files

− cbits/autolink.c
@@ -1,296 +0,0 @@-/*- * Copyright (c) 2011, Vicent Marti- *- * 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 "buffer.h"-#include "autolink.h"--#include <string.h>-#include <stdlib.h>-#include <stdio.h>-#include <ctype.h>--#if defined(_WIN32)-#define strncasecmp	_strnicmp-#endif--int-sd_autolink_issafe(const uint8_t *link, size_t link_len)-{-	static const size_t valid_uris_count = 5;-	static const char *valid_uris[] = {-		"/", "http://", "https://", "ftp://", "mailto:"-	};--	size_t i;--	for (i = 0; i < valid_uris_count; ++i) {-		size_t len = strlen(valid_uris[i]);--		if (link_len > len &&-			strncasecmp((char *)link, valid_uris[i], len) == 0 &&-			isalnum(link[len]))-			return 1;-	}--	return 0;-}--static size_t-autolink_delim(uint8_t *data, size_t link_end, size_t offset, size_t size)-{-	uint8_t cclose, copen = 0;-	size_t i;--	for (i = 0; i < link_end; ++i)-		if (data[i] == '<') {-			link_end = i;-			break;-		}--	while (link_end > 0) {-		if (strchr("?!.,", data[link_end - 1]) != NULL)-			link_end--;--		else if (data[link_end - 1] == ';') {-			size_t new_end = link_end - 2;--			while (new_end > 0 && isalpha(data[new_end]))-				new_end--;--			if (new_end < link_end - 2 && data[new_end] == '&')-				link_end = new_end;-			else-				link_end--;-		}-		else break;-	}--	if (link_end == 0)-		return 0;--	cclose = data[link_end - 1];--	switch (cclose) {-	case '"':	copen = '"'; break;-	case '\'':	copen = '\''; break;-	case ')':	copen = '('; break;-	case ']':	copen = '['; break;-	case '}':	copen = '{'; break;-	}--	if (copen != 0) {-		size_t closing = 0;-		size_t opening = 0;-		size_t i = 0;--		/* Try to close the final punctuation sign in this same line;-		 * if we managed to close it outside of the URL, that means that it's-		 * not part of the URL. If it closes inside the URL, that means it-		 * is part of the URL.-		 *-		 * Examples:-		 *-		 *	foo http://www.pokemon.com/Pikachu_(Electric) bar-		 *		=> http://www.pokemon.com/Pikachu_(Electric)-		 *-		 *	foo (http://www.pokemon.com/Pikachu_(Electric)) bar-		 *		=> http://www.pokemon.com/Pikachu_(Electric)-		 *-		 *	foo http://www.pokemon.com/Pikachu_(Electric)) bar-		 *		=> http://www.pokemon.com/Pikachu_(Electric))-		 *-		 *	(foo http://www.pokemon.com/Pikachu_(Electric)) bar-		 *		=> foo http://www.pokemon.com/Pikachu_(Electric)-		 */--		while (i < link_end) {-			if (data[i] == copen)-				opening++;-			else if (data[i] == cclose)-				closing++;--			i++;-		}--		if (closing != opening)-			link_end--;-	}--	return link_end;-}--static size_t-check_domain(uint8_t *data, size_t size, int allow_short)-{-	size_t i, np = 0;--	if (!isalnum(data[0]))-		return 0;--	for (i = 1; i < size - 1; ++i) {-		if (data[i] == '.') np++;-		else if (!isalnum(data[i]) && data[i] != '-') break;-	}--	if (allow_short) {-		/* We don't need a valid domain in the strict sense (with-		 * least one dot; so just make sure it's composed of valid-		 * domain characters and return the length of the the valid-		 * sequence. */-		return i;-	} else {-		/* a valid domain needs to have at least a dot.-		 * that's as far as we get */-		return np ? i : 0;-	}-}--size_t-sd_autolink__www(-	size_t *rewind_p,-	struct buf *link,-	uint8_t *data,-	size_t offset,-	size_t size,-	unsigned int flags)-{-	size_t link_end;--	if (offset > 0 && !ispunct(data[-1]) && !isspace(data[-1]))-		return 0;--	if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)-		return 0;--	link_end = check_domain(data, size, 0);--	if (link_end == 0)-		return 0;--	while (link_end < size && !isspace(data[link_end]))-		link_end++;--	link_end = autolink_delim(data, link_end, offset, size);--	if (link_end == 0)-		return 0;--	bufput(link, data, link_end);-	*rewind_p = 0;--	return (int)link_end;-}--size_t-sd_autolink__email(-	size_t *rewind_p,-	struct buf *link,-	uint8_t *data,-	size_t offset,-	size_t size,-	unsigned int flags)-{-	size_t link_end, rewind;-	int nb = 0, np = 0;--	for (rewind = 0; rewind < offset; ++rewind) {-		uint8_t c = data[-rewind - 1];--		if (isalnum(c))-			continue;--		if (strchr(".+-_", c) != NULL)-			continue;--		break;-	}--	if (rewind == 0)-		return 0;--	for (link_end = 0; link_end < size; ++link_end) {-		uint8_t c = data[link_end];--		if (isalnum(c))-			continue;--		if (c == '@')-			nb++;-		else if (c == '.' && link_end < size - 1)-			np++;-		else if (c != '-' && c != '_')-			break;-	}--	if (link_end < 2 || nb != 1 || np == 0)-		return 0;--	link_end = autolink_delim(data, link_end, offset, size);--	if (link_end == 0)-		return 0;--	bufput(link, data - rewind, link_end + rewind);-	*rewind_p = rewind;--	return link_end;-}--size_t-sd_autolink__url(-	size_t *rewind_p,-	struct buf *link,-	uint8_t *data,-	size_t offset,-	size_t size,-	unsigned int flags)-{-	size_t link_end, rewind = 0, domain_len;--	if (size < 4 || data[1] != '/' || data[2] != '/')-		return 0;--	while (rewind < offset && isalpha(data[-rewind - 1]))-		rewind++;--	if (!sd_autolink_issafe(data - rewind, size + rewind))-		return 0;--	link_end = strlen("://");--	domain_len = check_domain(-		data + link_end,-		size - link_end,-		flags & SD_AUTOLINK_SHORT_DOMAINS);--	if (domain_len == 0)-		return 0;--	link_end += domain_len;-	while (link_end < size && !isspace(data[link_end]))-		link_end++;--	link_end = autolink_delim(data, link_end, offset, size);--	if (link_end == 0)-		return 0;--	bufput(link, data - rewind, link_end + rewind);-	*rewind_p = rewind;--	return link_end;-}-
− cbits/autolink.h
@@ -1,51 +0,0 @@-/*- * Copyright (c) 2011, Vicent Marti- *- * 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 UPSKIRT_AUTOLINK_H-#define UPSKIRT_AUTOLINK_H--#include "buffer.h"--#ifdef __cplusplus-extern "C" {-#endif--enum {-	SD_AUTOLINK_SHORT_DOMAINS = (1 << 0),-};--int-sd_autolink_issafe(const uint8_t *link, size_t link_len);--size_t-sd_autolink__www(size_t *rewind_p, struct buf *link,-	uint8_t *data, size_t offset, size_t size, unsigned int flags);--size_t-sd_autolink__email(size_t *rewind_p, struct buf *link,-	uint8_t *data, size_t offset, size_t size, unsigned int flags);--size_t-sd_autolink__url(size_t *rewind_p, struct buf *link,-	uint8_t *data, size_t offset, size_t size, unsigned int flags);--#ifdef __cplusplus-}-#endif--#endif--/* vim: set filetype=c: */
− cbits/buffer.c
@@ -1,225 +0,0 @@-/*- * 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- * 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.- */--#define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb--#include "buffer.h"--#include <stdio.h>-#include <stdlib.h>-#include <string.h>-#include <assert.h>--/* MSVC compat */-#if defined(_MSC_VER)-#	define _buf_vsnprintf _vsnprintf-#else-#	define _buf_vsnprintf vsnprintf-#endif--int-bufprefix(const struct buf *buf, const char *prefix)-{-	size_t i;-	assert(buf && buf->unit);--	for (i = 0; i < buf->size; ++i) {-		if (prefix[i] == 0)-			return 0;--		if (buf->data[i] != prefix[i])-			return buf->data[i] - prefix[i];-	}--	return 0;-}--/* bufgrow: increasing the allocated size to the given value */-int-bufgrow(struct buf *buf, size_t neosz)-{-	size_t neoasz;-	void *neodata;--	assert(buf && buf->unit);--	if (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;--	neodata = realloc(buf->data, neoasz);-	if (!neodata)-		return BUF_ENOMEM;--	buf->data = neodata;-	buf->asize = neoasz;-	return BUF_OK;-}---/* bufnew: allocation of a new buffer */-struct buf *-bufnew(size_t unit)-{-	struct buf *ret;-	ret = malloc(sizeof (struct buf));--	if (ret) {-		ret->data = 0;-		ret->size = ret->asize = 0;-		ret->unit = unit;-	}-	return ret;-}--/* bufnullterm: NULL-termination of the string array */-const char *-bufcstr(struct buf *buf)-{-	assert(buf && buf->unit);--	if (buf->size < buf->asize && buf->data[buf->size] == 0)-		return (char *)buf->data;--	if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) {-		buf->data[buf->size] = 0;-		return (char *)buf->data;-	}--	return NULL;-}--/* bufprintf: formatted printing to a buffer */-void-bufprintf(struct buf *buf, const char *fmt, ...)-{-	va_list ap;-	int n;--	assert(buf && buf->unit);--	if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < 0)-		return;-	-	va_start(ap, fmt);-	n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);-	va_end(ap);--	if (n < 0) {-#ifdef _MSC_VER-		va_start(ap, fmt);-		n = _vscprintf(fmt, ap);-		va_end(ap);-#else-		return;-#endif-	}--	if ((size_t)n >= buf->asize - buf->size) {-		if (bufgrow(buf, buf->size + n + 1) < 0)-			return;--		va_start(ap, fmt);-		n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);-		va_end(ap);-	}--	if (n < 0)-		return;--	buf->size += n;-}--/* bufput: appends raw data to a buffer */-void-bufput(struct buf *buf, const void *data, size_t len)-{-	assert(buf && buf->unit);--	if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0)-		return;--	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));-}---/* bufputc: appends a single uint8_t to a buffer */-void-bufputc(struct buf *buf, int c)-{-	assert(buf && buf->unit);--	if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0)-		return;--	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;--	free(buf->data);-	free(buf);-}---/* bufreset: frees internal data of the buffer */-void-bufreset(struct buf *buf)-{-	if (!buf)-		return;--	free(buf->data);-	buf->data = NULL;-	buf->size = buf->asize = 0;-}--/* bufslurp: removes a given number of bytes from the head of the array */-void-bufslurp(struct buf *buf, size_t len)-{-	assert(buf && buf->unit);--	if (len >= buf->size) {-		buf->size = 0;-		return;-	}--	buf->size -= len;-	memmove(buf->data, buf->data + len, buf->size);-}-
− cbits/buffer.h
@@ -1,96 +0,0 @@-/*- * 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- * 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 BUFFER_H__-#define BUFFER_H__--#include <stddef.h>-#include <stdarg.h>-#include <stdint.h>--#ifdef __cplusplus-extern "C" {-#endif--#if defined(_MSC_VER)-#define __attribute__(x)-#define inline-#endif--typedef enum {-	BUF_OK = 0,-	BUF_ENOMEM = -1,-} buferror_t;--/* struct buf: character array buffer */-struct buf {-	uint8_t *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) */-};--/* CONST_BUF: global buffer from a string litteral */-#define BUF_STATIC(string) \-	{ (uint8_t *)string, sizeof string -1, sizeof string, 0, 0 }--/* VOLATILE_BUF: macro for creating a volatile buffer on the stack */-#define BUF_VOLATILE(strname) \-	{ (uint8_t *)strname, strlen(strname), 0, 0, 0 }--/* BUFPUTSL: optimized bufputs of a string litteral */-#define BUFPUTSL(output, literal) \-	bufput(output, literal, sizeof literal - 1)--/* 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) */-const char *bufcstr(struct buf *);--/* bufprefix: compare the beginning of a buffer with a string */-int bufprefix(const struct buf *buf, const char *prefix);--/* bufput: appends raw data to a buffer */-void bufput(struct buf *, const void *, 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 *, int);--/* bufrelease: decrease the reference count and free the buffer if needed */-void bufrelease(struct buf *);--/* bufreset: frees internal data of the buffer */-void bufreset(struct buf *);--/* bufslurp: removes a given number of bytes from the head of the array */-void bufslurp(struct buf *, size_t);--/* bufprintf: formatted printing to a buffer */-void bufprintf(struct buf *, const char *, ...) __attribute__ ((format (printf, 2, 3)));--#ifdef __cplusplus-}-#endif--#endif
− cbits/houdini.h
@@ -1,29 +0,0 @@-#ifndef HOUDINI_H__-#define HOUDINI_H__--#include "buffer.h"--#ifdef HOUDINI_USE_LOCALE-#	define _isxdigit(c) isxdigit(c)-#	define _isdigit(c) isdigit(c)-#else-/*- * Helper _isdigit methods -- do not trust the current locale- * */-#	define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL)-#	define _isdigit(c) ((c) >= '0' && (c) <= '9')-#endif--extern void houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure);-extern void houdini_unescape_html(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_escape_xml(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_escape_uri(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_escape_url(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_unescape_uri(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_unescape_url(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_escape_js(struct buf *ob, const uint8_t *src, size_t size);-extern void houdini_unescape_js(struct buf *ob, const uint8_t *src, size_t size);--#endif
− cbits/houdini_href_e.c
@@ -1,108 +0,0 @@-#include <assert.h>-#include <stdio.h>-#include <string.h>--#include "houdini.h"--#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10)--/*- * The following characters will not be escaped:- *- *		-_.+!*'(),%#@?=;:/,+&$ alphanum- *- * Note that this character set is the addition of:- *- *	- The characters which are safe to be in an URL- *	- The characters which are *not* safe to be in- *	an URL because they are RESERVED characters.- *- * We asume (lazily) that any RESERVED char that- * appears inside an URL is actually meant to- * have its native function (i.e. as an URL - * component/separator) and hence needs no escaping.- *- * There are two exceptions: the chacters & (amp)- * and ' (single quote) do not appear in the table.- * They are meant to appear in the URL as components,- * yet they require special HTML-entity escaping- * to generate valid HTML markup.- *- * All other characters will be escaped to %XX.- *- */-static const char HREF_SAFE[] = {-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, -	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, -	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, -	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-};--void-houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size)-{-	static const char hex_chars[] = "0123456789ABCDEF";-	size_t  i = 0, org;-	char hex_str[3];--	bufgrow(ob, ESCAPE_GROW_FACTOR(size));-	hex_str[0] = '%';--	while (i < size) {-		org = i;-		while (i < size && HREF_SAFE[src[i]] != 0)-			i++;--		if (i > org)-			bufput(ob, src + org, i - org);--		/* escaping */-		if (i >= size)-			break;--		switch (src[i]) {-		/* amp appears all the time in URLs, but needs-		 * HTML-entity escaping to be inside an href */-		case '&': -			BUFPUTSL(ob, "&amp;");-			break;--		/* the single quote is a valid URL character-		 * according to the standard; it needs HTML-		 * entity escaping too */-		case '\'':-			BUFPUTSL(ob, "&#x27;");-			break;-		-		/* the space can be escaped to %20 or a plus-		 * sign. we're going with the generic escape-		 * for now. the plus thing is more commonly seen-		 * when building GET strings */-#if 0-		case ' ':-			bufputc(ob, '+');-			break;-#endif--		/* every other character goes with a %XX escaping */-		default:-			hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];-			hex_str[2] = hex_chars[src[i] & 0xF];-			bufput(ob, hex_str, 3);-		}--		i++;-	}-}
− cbits/houdini_html_e.c
@@ -1,84 +0,0 @@-#include <assert.h>-#include <stdio.h>-#include <string.h>--#include "houdini.h"--#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) /* this is very scientific, yes */--/**- * According to the OWASP rules:- *- * & --> &amp;- * < --> &lt;- * > --> &gt;- * " --> &quot;- * ' --> &#x27;     &apos; is not recommended- * / --> &#x2F;     forward slash is included as it helps end an HTML entity- *- */-static const char HTML_ESCAPE_TABLE[] = {-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-};--static const char *HTML_ESCAPES[] = {-        "",-        "&quot;",-        "&amp;",-        "&#39;",-        "&#47;",-        "&lt;",-        "&gt;"-};--void-houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure)-{-	size_t i = 0, org, esc = 0;--	bufgrow(ob, ESCAPE_GROW_FACTOR(size));--	while (i < size) {-		org = i;-		while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)-			i++;--		if (i > org)-			bufput(ob, src + org, i - org);--		/* escaping */-		if (i >= size)-			break;--		/* The forward slash is only escaped in secure mode */-		if (src[i] == '/' && !secure) {-			bufputc(ob, '/');-		} else {-			bufputs(ob, HTML_ESCAPES[esc]);-		}--		i++;-	}-}--void-houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size)-{-	houdini_escape_html0(ob, src, size, 1);-}-
− cbits/html.c
@@ -1,620 +0,0 @@-/*- * Copyright (c) 2009, Natacha Porté- * Copyright (c) 2011, Vicent Marti- *- * 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 "markdown.h"-#include "html.h"--#include <string.h>-#include <stdlib.h>-#include <stdio.h>-#include <ctype.h>--#include "houdini.h"--#define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML)--int-sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname)-{-	size_t i;-	int closed = 0;--	if (tag_size < 3 || tag_data[0] != '<')-		return HTML_TAG_NONE;--	i = 1;--	if (tag_data[i] == '/') {-		closed = 1;-		i++;-	}--	for (; i < tag_size; ++i, ++tagname) {-		if (*tagname == 0)-			break;--		if (tag_data[i] != *tagname)-			return HTML_TAG_NONE;-	}--	if (i == tag_size)-		return HTML_TAG_NONE;--	if (isspace(tag_data[i]) || tag_data[i] == '>')-		return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;--	return HTML_TAG_NONE;-}--static inline void escape_html(struct buf *ob, const uint8_t *source, size_t length)-{-	houdini_escape_html0(ob, source, length, 0);-}--static inline void escape_href(struct buf *ob, const uint8_t *source, size_t length)-{-	houdini_escape_href(ob, source, length);-}--/********************- * GENERIC RENDERER *- ********************/-static int-rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque)-{-	struct html_renderopt *options = opaque;--	if (!link || !link->size)-		return 0;--	if ((options->flags & HTML_SAFELINK) != 0 &&-		!sd_autolink_issafe(link->data, link->size) &&-		type != MKDA_EMAIL)-		return 0;--	BUFPUTSL(ob, "<a href=\"");-	if (type == MKDA_EMAIL)-		BUFPUTSL(ob, "mailto:");-	escape_href(ob, link->data, link->size);--	if (options->link_attributes) {-		bufputc(ob, '\"');-		options->link_attributes(ob, link, opaque);-		bufputc(ob, '>');-	} else {-		BUFPUTSL(ob, "\">");-	}--	/*-	 * Pretty printing: if we get an email address as-	 * an actual URI, e.g. `mailto:foo@bar.com`, we don't-	 * want to print the `mailto:` prefix-	 */-	if (bufprefix(link, "mailto:") == 0) {-		escape_html(ob, link->data + 7, link->size - 7);-	} else {-		escape_html(ob, link->data, link->size);-	}--	BUFPUTSL(ob, "</a>");--	return 1;-}--static void-rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque)-{-	if (ob->size) bufputc(ob, '\n');--	if (lang && lang->size) {-		size_t i, cls;-		BUFPUTSL(ob, "<pre><code class=\"");--		for (i = 0, cls = 0; i < lang->size; ++i, ++cls) {-			while (i < lang->size && isspace(lang->data[i]))-				i++;--			if (i < lang->size) {-				size_t org = i;-				while (i < lang->size && !isspace(lang->data[i]))-					i++;--				if (lang->data[org] == '.')-					org++;--				if (cls) bufputc(ob, ' ');-				escape_html(ob, lang->data + org, i - org);-			}-		}--		BUFPUTSL(ob, "\">");-	} else-		BUFPUTSL(ob, "<pre><code>");--	if (text)-		escape_html(ob, text->data, text->size);--	BUFPUTSL(ob, "</code></pre>\n");-}--static void-rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque)-{-	if (ob->size) bufputc(ob, '\n');-	BUFPUTSL(ob, "<blockquote>\n");-	if (text) bufput(ob, text->data, text->size);-	BUFPUTSL(ob, "</blockquote>\n");-}--static int-rndr_codespan(struct buf *ob, const struct buf *text, void *opaque)-{-	BUFPUTSL(ob, "<code>");-	if (text) escape_html(ob, text->data, text->size);-	BUFPUTSL(ob, "</code>");-	return 1;-}--static int-rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque)-{-	if (!text || !text->size)-		return 0;--	BUFPUTSL(ob, "<del>");-	bufput(ob, text->data, text->size);-	BUFPUTSL(ob, "</del>");-	return 1;-}--static int-rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque)-{-	if (!text || !text->size)-		return 0;--	BUFPUTSL(ob, "<strong>");-	bufput(ob, text->data, text->size);-	BUFPUTSL(ob, "</strong>");--	return 1;-}--static int-rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque)-{-	if (!text || !text->size) return 0;-	BUFPUTSL(ob, "<em>");-	if (text) bufput(ob, text->data, text->size);-	BUFPUTSL(ob, "</em>");-	return 1;-}--static int-rndr_linebreak(struct buf *ob, void *opaque)-{-	struct html_renderopt *options = opaque;-	bufputs(ob, USE_XHTML(options) ? "<br/>\n" : "<br>\n");-	return 1;-}--static void-rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)-{-	struct html_renderopt *options = opaque;--	if (ob->size)-		bufputc(ob, '\n');--	if (options->flags & HTML_TOC)-		bufprintf(ob, "<h%d id=\"toc_%d\">", level, options->toc_data.header_count++);-	else-		bufprintf(ob, "<h%d>", level);--	if (text) bufput(ob, text->data, text->size);-	bufprintf(ob, "</h%d>\n", level);-}--static int-rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)-{-	struct html_renderopt *options = opaque;--	if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))-		return 0;--	BUFPUTSL(ob, "<a href=\"");--	if (link && link->size)-		escape_href(ob, link->data, link->size);--	if (title && title->size) {-		BUFPUTSL(ob, "\" title=\"");-		escape_html(ob, title->data, title->size);-	}--	if (options->link_attributes) {-		bufputc(ob, '\"');-		options->link_attributes(ob, link, opaque);-		bufputc(ob, '>');-	} else {-		BUFPUTSL(ob, "\">");-	}--	if (content && content->size) bufput(ob, content->data, content->size);-	BUFPUTSL(ob, "</a>");-	return 1;-}--static void-rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque)-{-	if (ob->size) bufputc(ob, '\n');-	bufput(ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);-	if (text) bufput(ob, text->data, text->size);-	bufput(ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);-}--static void-rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque)-{-	BUFPUTSL(ob, "<li>");-	if (text) {-		size_t size = text->size;-		while (size && text->data[size - 1] == '\n')-			size--;--		bufput(ob, text->data, size);-	}-	BUFPUTSL(ob, "</li>\n");-}--static void-rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)-{-	struct html_renderopt *options = opaque;-	size_t i = 0;--	if (ob->size) bufputc(ob, '\n');--	if (!text || !text->size)-		return;--	while (i < text->size && isspace(text->data[i])) i++;--	if (i == text->size)-		return;--	BUFPUTSL(ob, "<p>");-	if (options->flags & HTML_HARD_WRAP) {-		size_t org;-		while (i < text->size) {-			org = i;-			while (i < text->size && text->data[i] != '\n')-				i++;--			if (i > org)-				bufput(ob, text->data + org, i - org);--			/*-			 * do not insert a line break if this newline-			 * is the last character on the paragraph-			 */-			if (i >= text->size - 1)-				break;--			rndr_linebreak(ob, opaque);-			i++;-		}-	} else {-		bufput(ob, &text->data[i], text->size - i);-	}-	BUFPUTSL(ob, "</p>\n");-}--static void-rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)-{-	size_t org, sz;-	if (!text) return;-	sz = text->size;-	while (sz > 0 && text->data[sz - 1] == '\n') sz--;-	org = 0;-	while (org < sz && text->data[org] == '\n') org++;-	if (org >= sz) return;-	if (ob->size) bufputc(ob, '\n');-	bufput(ob, text->data + org, sz - org);-	bufputc(ob, '\n');-}--static int-rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque)-{-	if (!text || !text->size) return 0;-	BUFPUTSL(ob, "<strong><em>");-	bufput(ob, text->data, text->size);-	BUFPUTSL(ob, "</em></strong>");-	return 1;-}--static void-rndr_hrule(struct buf *ob, void *opaque)-{-	struct html_renderopt *options = opaque;-	if (ob->size) bufputc(ob, '\n');-	bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");-}--static int-rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)-{-	struct html_renderopt *options = opaque;-	if (!link || !link->size) return 0;--	BUFPUTSL(ob, "<img src=\"");-	escape_href(ob, link->data, link->size);-	BUFPUTSL(ob, "\" alt=\"");--	if (alt && alt->size)-		escape_html(ob, alt->data, alt->size);--	if (title && title->size) {-		BUFPUTSL(ob, "\" title=\"");-		escape_html(ob, title->data, title->size); }--	bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");-	return 1;-}--static int-rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)-{-	struct html_renderopt *options = opaque;--	/* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES-	* It doens't see if there are any valid tags, just escape all of them. */-	if((options->flags & HTML_ESCAPE) != 0) {-		escape_html(ob, text->data, text->size);-		return 1;-	}--	if ((options->flags & HTML_SKIP_HTML) != 0)-		return 1;--	if ((options->flags & HTML_SKIP_STYLE) != 0 &&-		sdhtml_is_tag(text->data, text->size, "style"))-		return 1;--	if ((options->flags & HTML_SKIP_LINKS) != 0 &&-		sdhtml_is_tag(text->data, text->size, "a"))-		return 1;--	if ((options->flags & HTML_SKIP_IMAGES) != 0 &&-		sdhtml_is_tag(text->data, text->size, "img"))-		return 1;--	bufput(ob, text->data, text->size);-	return 1;-}--static void-rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque)-{-	if (ob->size) bufputc(ob, '\n');-	BUFPUTSL(ob, "<table><thead>\n");-	if (header)-		bufput(ob, header->data, header->size);-	BUFPUTSL(ob, "</thead><tbody>\n");-	if (body)-		bufput(ob, body->data, body->size);-	BUFPUTSL(ob, "</tbody></table>\n");-}--static void-rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque)-{-	BUFPUTSL(ob, "<tr>\n");-	if (text)-		bufput(ob, text->data, text->size);-	BUFPUTSL(ob, "</tr>\n");-}--static void-rndr_tablecell(struct buf *ob, const struct buf *text, int flags, void *opaque)-{-	if (flags & MKD_TABLE_HEADER) {-		BUFPUTSL(ob, "<th");-	} else {-		BUFPUTSL(ob, "<td");-	}--	switch (flags & MKD_TABLE_ALIGNMASK) {-	case MKD_TABLE_ALIGN_CENTER:-		BUFPUTSL(ob, " align=\"center\">");-		break;--	case MKD_TABLE_ALIGN_L:-		BUFPUTSL(ob, " align=\"left\">");-		break;--	case MKD_TABLE_ALIGN_R:-		BUFPUTSL(ob, " align=\"right\">");-		break;--	default:-		BUFPUTSL(ob, ">");-	}--	if (text)-		bufput(ob, text->data, text->size);--	if (flags & MKD_TABLE_HEADER) {-		BUFPUTSL(ob, "</th>\n");-	} else {-		BUFPUTSL(ob, "</td>\n");-	}-}--static int-rndr_superscript(struct buf *ob, const struct buf *text, void *opaque)-{-	if (!text || !text->size) return 0;-	BUFPUTSL(ob, "<sup>");-	bufput(ob, text->data, text->size);-	BUFPUTSL(ob, "</sup>");-	return 1;-}--static void-rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque)-{-	if (text)-		escape_html(ob, text->data, text->size);-}--static void-toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)-{-	struct html_renderopt *options = opaque;--	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, "<a href=\"#toc_%d\">", options->toc_data.header_count++);-	if (text)-		bufput(ob, text->data, text->size);-	BUFPUTSL(ob, "</a>\n");-}--static void-toc_finalize(struct buf *ob, void *opaque)-{-	struct html_renderopt *options = opaque;--	while (options->toc_data.current_level > 0) {-		BUFPUTSL(ob, "</li>\n</ul>\n");-		options->toc_data.current_level--;-	}-}--void-sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options)-{-	static const struct sd_callbacks cb_default = {-		NULL,-		NULL,-		NULL,-		toc_header,-		NULL,-		NULL,-		NULL,-		NULL,-		NULL,-		NULL,-		NULL,--		NULL,-		rndr_codespan,-		rndr_double_emphasis,-		rndr_emphasis,-		NULL,-		NULL,-		NULL,-		NULL,-		rndr_triple_emphasis,-		rndr_strikethrough,-		rndr_superscript,--		NULL,-		NULL,--		NULL,-		toc_finalize,-	};--	memset(options, 0x0, sizeof(struct html_renderopt));-	options->flags = HTML_TOC;--	memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));-}--void-sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)-{-	static const struct sd_callbacks cb_default = {-		rndr_blockcode,-		rndr_blockquote,-		rndr_raw_block,-		rndr_header,-		rndr_hrule,-		rndr_list,-		rndr_listitem,-		rndr_paragraph,-		rndr_table,-		rndr_tablerow,-		rndr_tablecell,--		rndr_autolink,-		rndr_codespan,-		rndr_double_emphasis,-		rndr_emphasis,-		rndr_image,-		rndr_linebreak,-		rndr_link,-		rndr_raw_html,-		rndr_triple_emphasis,-		rndr_strikethrough,-		rndr_superscript,--		NULL,-		rndr_normal_text,--		NULL,-		NULL,-	};--	/* Prepare the options pointer */-	memset(options, 0x0, sizeof(struct html_renderopt));-	options->flags = render_flags;--	/* Prepare the callbacks */-	memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));--	if (render_flags & HTML_SKIP_IMAGES)-		callbacks->image = NULL;--	if (render_flags & HTML_SKIP_LINKS) {-		callbacks->link = NULL;-		callbacks->autolink = NULL;-	}--	if (render_flags & HTML_SKIP_HTML || render_flags & HTML_ESCAPE)-		callbacks->blockhtml = NULL;-}
− cbits/html.h
@@ -1,68 +0,0 @@-/*- * Copyright (c) 2011, Vicent Marti- *- * 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 UPSKIRT_HTML_H-#define UPSKIRT_HTML_H--#include "markdown.h"-#include "buffer.h"-#include <stdlib.h>--struct html_renderopt {-	struct {-		int header_count;-		int current_level;-	} toc_data;--	unsigned int flags;--	/* extra callbacks */-	void (*link_attributes)(struct buf *ob, const struct buf *url, void *self);-};--typedef enum {-	HTML_SKIP_HTML = (1 << 0),-	HTML_SKIP_STYLE = (1 << 1),-	HTML_SKIP_IMAGES = (1 << 2),-	HTML_SKIP_LINKS = (1 << 3),-	HTML_EXPAND_TABS = (1 << 4),-	HTML_SAFELINK = (1 << 5),-	HTML_TOC = (1 << 6),-	HTML_HARD_WRAP = (1 << 7),-	HTML_USE_XHTML = (1 << 8),-	HTML_ESCAPE = (1 << 9),-} html_render_mode;--typedef enum {-	HTML_TAG_NONE = 0,-	HTML_TAG_OPEN,-	HTML_TAG_CLOSE,-} html_tag;--int-sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname);--extern void-sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, unsigned int render_flags);--extern void-sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr);--extern void-sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size);--#endif-
− cbits/html_blocks.h
@@ -1,206 +0,0 @@-/* 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 = 24,-      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",-      "",-      "style",-      "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/html_smartypants.c
@@ -1,385 +0,0 @@-/*- * Copyright (c) 2011, Vicent Marti- *- * 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 "buffer.h"-#include "html.h"--#include <string.h>-#include <stdlib.h>-#include <stdio.h>-#include <ctype.h>--struct smartypants_data {-	int in_squote;-	int in_dquote;-};--static size_t smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);-static size_t smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);--static size_t (*smartypants_cb_ptrs[])-	(struct buf *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) =-{-	NULL,					/* 0 */-	smartypants_cb__dash,	/* 1 */-	smartypants_cb__parens,	/* 2 */-	smartypants_cb__squote, /* 3 */-	smartypants_cb__dquote, /* 4 */-	smartypants_cb__amp,	/* 5 */-	smartypants_cb__period,	/* 6 */-	smartypants_cb__number,	/* 7 */-	smartypants_cb__ltag,	/* 8 */-	smartypants_cb__backtick, /* 9 */-	smartypants_cb__escape, /* 10 */-};--static const uint8_t smartypants_cb_chars[] = {-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0,-	0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0,-	9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-};--static inline int-word_boundary(uint8_t c)-{-	return c == 0 || isspace(c) || ispunct(c);-}--static int-smartypants_quotes(struct buf *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open)-{-	char ent[8];--	if (*is_open && !word_boundary(next_char))-		return 0;--	if (!(*is_open) && !word_boundary(previous_char))-		return 0;--	snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote);-	*is_open = !(*is_open);-	bufputs(ob, ent);-	return 1;-}--static size_t-smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (size >= 2) {-		uint8_t t1 = tolower(text[1]);--		if (t1 == '\'') {-			if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote))-				return 1;-		}--		if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') &&-			(size == 3 || word_boundary(text[2]))) {-			BUFPUTSL(ob, "&rsquo;");-			return 0;-		}--		if (size >= 3) {-			uint8_t t2 = tolower(text[2]);--			if (((t1 == 'r' && t2 == 'e') ||-				(t1 == 'l' && t2 == 'l') ||-				(t1 == 'v' && t2 == 'e')) &&-				(size == 4 || word_boundary(text[3]))) {-				BUFPUTSL(ob, "&rsquo;");-				return 0;-			}-		}-	}--	if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote))-		return 0;--	bufputc(ob, text[0]);-	return 0;-}--static size_t-smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (size >= 3) {-		uint8_t t1 = tolower(text[1]);-		uint8_t t2 = tolower(text[2]);--		if (t1 == 'c' && t2 == ')') {-			BUFPUTSL(ob, "&copy;");-			return 2;-		}--		if (t1 == 'r' && t2 == ')') {-			BUFPUTSL(ob, "&reg;");-			return 2;-		}--		if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') {-			BUFPUTSL(ob, "&trade;");-			return 3;-		}-	}--	bufputc(ob, text[0]);-	return 0;-}--static size_t-smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (size >= 3 && text[1] == '-' && text[2] == '-') {-		BUFPUTSL(ob, "&mdash;");-		return 2;-	}--	if (size >= 2 && text[1] == '-') {-		BUFPUTSL(ob, "&ndash;");-		return 1;-	}--	bufputc(ob, text[0]);-	return 0;-}--static size_t-smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (size >= 6 && memcmp(text, "&quot;", 6) == 0) {-		if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote))-			return 5;-	}--	if (size >= 4 && memcmp(text, "&#0;", 4) == 0)-		return 3;--	bufputc(ob, '&');-	return 0;-}--static size_t-smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (size >= 3 && text[1] == '.' && text[2] == '.') {-		BUFPUTSL(ob, "&hellip;");-		return 2;-	}--	if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') {-		BUFPUTSL(ob, "&hellip;");-		return 4;-	}--	bufputc(ob, text[0]);-	return 0;-}--static size_t-smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (size >= 2 && text[1] == '`') {-		if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote))-			return 1;-	}--	return 0;-}--static size_t-smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (word_boundary(previous_char) && size >= 3) {-		if (text[0] == '1' && text[1] == '/' && text[2] == '2') {-			if (size == 3 || word_boundary(text[3])) {-				BUFPUTSL(ob, "&frac12;");-				return 2;-			}-		}--		if (text[0] == '1' && text[1] == '/' && text[2] == '4') {-			if (size == 3 || word_boundary(text[3]) ||-				(size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) {-				BUFPUTSL(ob, "&frac14;");-				return 2;-			}-		}--		if (text[0] == '3' && text[1] == '/' && text[2] == '4') {-			if (size == 3 || word_boundary(text[3]) ||-				(size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) {-				BUFPUTSL(ob, "&frac34;");-				return 2;-			}-		}-	}--	bufputc(ob, text[0]);-	return 0;-}--static size_t-smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote))-		BUFPUTSL(ob, "&quot;");--	return 0;-}--static size_t-smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	static const char *skip_tags[] = {-	  "pre", "code", "var", "samp", "kbd", "math", "script", "style"-	};-	static const size_t skip_tags_count = 8;--	size_t tag, i = 0;--	while (i < size && text[i] != '>')-		i++;--	for (tag = 0; tag < skip_tags_count; ++tag) {-		if (sdhtml_is_tag(text, size, skip_tags[tag]) == HTML_TAG_OPEN)-			break;-	}--	if (tag < skip_tags_count) {-		for (;;) {-			while (i < size && text[i] != '<')-				i++;--			if (i == size)-				break;--			if (sdhtml_is_tag(text + i, size - i, skip_tags[tag]) == HTML_TAG_CLOSE)-				break;--			i++;-		}--		while (i < size && text[i] != '>')-			i++;-	}--	bufput(ob, text, i + 1);-	return i;-}--static size_t-smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)-{-	if (size < 2)-		return 0;--	switch (text[1]) {-	case '\\':-	case '"':-	case '\'':-	case '.':-	case '-':-	case '`':-		bufputc(ob, text[1]);-		return 1;--	default:-		bufputc(ob, '\\');-		return 0;-	}-}--#if 0-static struct {-    uint8_t c0;-    const uint8_t *pattern;-    const uint8_t *entity;-    int skip;-} smartypants_subs[] = {-    { '\'', "'s>",      "&rsquo;",  0 },-    { '\'', "'t>",      "&rsquo;",  0 },-    { '\'', "'re>",     "&rsquo;",  0 },-    { '\'', "'ll>",     "&rsquo;",  0 },-    { '\'', "'ve>",     "&rsquo;",  0 },-    { '\'', "'m>",      "&rsquo;",  0 },-    { '\'', "'d>",      "&rsquo;",  0 },-    { '-',  "--",       "&mdash;",  1 },-    { '-',  "<->",      "&ndash;",  0 },-    { '.',  "...",      "&hellip;", 2 },-    { '.',  ". . .",    "&hellip;", 4 },-    { '(',  "(c)",      "&copy;",   2 },-    { '(',  "(r)",      "&reg;",    2 },-    { '(',  "(tm)",     "&trade;",  3 },-    { '3',  "<3/4>",    "&frac34;", 2 },-    { '3',  "<3/4ths>", "&frac34;", 2 },-    { '1',  "<1/2>",    "&frac12;", 2 },-    { '1',  "<1/4>",    "&frac14;", 2 },-    { '1',  "<1/4th>",  "&frac14;", 2 },-    { '&',  "&#0;",      0,       3 },-};-#endif--void-sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size)-{-	size_t i;-	struct smartypants_data smrt = {0, 0};--	if (!text)-		return;--	bufgrow(ob, size);--	for (i = 0; i < size; ++i) {-		size_t org;-		uint8_t action = 0;--		org = i;-		while (i < size && (action = smartypants_cb_chars[text[i]]) == 0)-			i++;--		if (i > org)-			bufput(ob, text + org, i - org);--		if (i < size) {-			i += smartypants_cb_ptrs[(int)action]-				(ob, &smrt, i ? text[i - 1] : 0, text + i, size - i);-		}-	}-}--
− cbits/markdown.c
@@ -1,2551 +0,0 @@-/* markdown.c - generic markdown parser */--/*- * Copyright (c) 2009, Natacha Porté- * Copyright (c) 2011, Vicent Marti- *- * 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 "markdown.h"-#include "stack.h"--#include <assert.h>-#include <string.h>-#include <ctype.h>-#include <stdio.h>--#if defined(_WIN32)-#define strncasecmp	_strnicmp-#endif--#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 */-struct link_ref {-	unsigned int id;--	struct buf *link;-	struct buf *title;--	struct link_ref *next;-};--/* 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 sd_markdown;-typedef size_t-(*char_trigger)(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);--static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);-static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);--enum markdown_char_t {-	MD_CHAR_NONE = 0,-	MD_CHAR_EMPHASIS,-	MD_CHAR_CODESPAN,-	MD_CHAR_LINEBREAK,-	MD_CHAR_LINK,-	MD_CHAR_LANGLE,-	MD_CHAR_ESCAPE,-	MD_CHAR_ENTITITY,-	MD_CHAR_AUTOLINK_URL,-	MD_CHAR_AUTOLINK_EMAIL,-	MD_CHAR_AUTOLINK_WWW,-	MD_CHAR_SUPERSCRIPT,-};--static char_trigger markdown_char_ptrs[] = {-	NULL,-	&char_emphasis,-	&char_codespan,-	&char_linebreak,-	&char_link,-	&char_langle_tag,-	&char_escape,-	&char_entity,-	&char_autolink_url,-	&char_autolink_email,-	&char_autolink_www,-	&char_superscript,-};--/* render • structure containing one particular render */-struct sd_markdown {-	struct sd_callbacks	cb;-	void *opaque;--	struct link_ref *refs[REF_TABLE_SIZE];-	uint8_t active_char[256];-	struct stack work_bufs[2];-	unsigned int ext_flags;-	size_t max_nesting;-	int in_link_body;-};--/***************************- * HELPER FUNCTIONS *- ***************************/--static inline struct buf *-rndr_newbuf(struct sd_markdown *rndr, int type)-{-	static const size_t buf_size[2] = {256, 64};-	struct buf *work = NULL;-	struct stack *pool = &rndr->work_bufs[type];--	if (pool->size < pool->asize &&-		pool->item[pool->size] != NULL) {-		work = pool->item[pool->size++];-		work->size = 0;-	} else {-		work = bufnew(buf_size[type]);-		stack_push(pool, work);-	}--	return work;-}--static inline void-rndr_popbuf(struct sd_markdown *rndr, int type)-{-	rndr->work_bufs[type].size--;-}--static void-unscape_text(struct buf *ob, struct buf *src)-{-	size_t i = 0, org;-	while (i < src->size) {-		org = i;-		while (i < src->size && src->data[i] != '\\')-			i++;--		if (i > org)-			bufput(ob, src->data + org, i - org);--		if (i + 1 >= src->size)-			break;--		bufputc(ob, src->data[i + 1]);-		i += 2;-	}-}--static unsigned int-hash_link_ref(const uint8_t *link_ref, size_t length)-{-	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;-}--static struct link_ref *-add_link_ref(-	struct link_ref **references,-	const uint8_t *name, size_t name_size)-{-	struct link_ref *ref = calloc(1, sizeof(struct link_ref));--	if (!ref)-		return NULL;--	ref->id = hash_link_ref(name, name_size);-	ref->next = references[ref->id % REF_TABLE_SIZE];--	references[ref->id % REF_TABLE_SIZE] = ref;-	return ref;-}--static struct link_ref *-find_link_ref(struct link_ref **references, uint8_t *name, size_t length)-{-	unsigned int hash = hash_link_ref(name, length);-	struct link_ref *ref = NULL;--	ref = references[hash % REF_TABLE_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;--	for (i = 0; i < REF_TABLE_SIZE; ++i) {-		struct link_ref *r = references[i];-		struct link_ref *next;--		while (r) {-			next = r->next;-			bufrelease(r->link);-			bufrelease(r->title);-			free(r);-			r = next;-		}-	}-}--/*- * Check whether a char is a Markdown space.-- * Right now we only consider spaces the actual- * space and a newline: tabs and carriage returns- * are filtered out during the preprocessing phase.- *- * If we wanted to actually be UTF-8 compliant, we- * should instead extract an Unicode codepoint from- * this character and check for space properties.- */-static inline int-_isspace(int c)-{-	return c == ' ' || c == '\n';-}--/****************************- * INLINE PARSING FUNCTIONS *- ****************************/--/* is_mail_autolink • looks for the address part of a mail autolink and '>' */-/* this is less strict than the original markdown e-mail address matching */-static size_t-is_mail_autolink(uint8_t *data, size_t size)-{-	size_t i = 0, nb = 0;--	/* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */-	for (i = 0; i < size; ++i) {-		if (isalnum(data[i]))-			continue;--		switch (data[i]) {-			case '@':-				nb++;--			case '-':-			case '.':-			case '_':-				break;--			case '>':-				return (nb == 1) ? i + 1 : 0;--			default:-				return 0;-		}-	}--	return 0;-}--/* tag_length • returns the length of the given tag, or 0 is it's not valid */-static size_t-tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink)-{-	size_t i, j;--	/* a valid tag can't be shorter than 3 chars */-	if (size < 3) return 0;--	/* begins with a '<' optionally followed by '/', followed by letter or number */-	if (data[0] != '<') return 0;-	i = (data[1] == '/') ? 2 : 1;--	if (!isalnum(data[i]))-		return 0;--	/* scheme test */-	*autolink = MKDA_NOT_AUTOLINK;--	/* try to find the beginning of an URI */-	while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-'))-		i++;--	if (i > 1 && data[i] == '@') {-		if ((j = is_mail_autolink(data + i, size - i)) != 0) {-			*autolink = MKDA_EMAIL;-			return i + j;-		}-	}--	if (i > 2 && data[i] == ':') {-		*autolink = MKDA_NORMAL;-		i++;-	}--	/* completing autolink test: no whitespace or ' or " */-	if (i >= size)-		*autolink = MKDA_NOT_AUTOLINK;--	else if (*autolink) {-		j = i;--		while (i < size) {-			if (data[i] == '\\') i += 2;-			else if (data[i] == '>' || data[i] == '\'' ||-					data[i] == '"' || data[i] == ' ' || data[i] == '\n')-					break;-			else i++;-		}--		if (i >= size) return 0;-		if (i > j && data[i] == '>') return i + 1;-		/* one of the forbidden chars has been found */-		*autolink = MKDA_NOT_AUTOLINK;-	}--	/* looking for sometinhg looking like a tag end */-	while (i < size && data[i] != '>') i++;-	if (i >= size) return 0;-	return i + 1;-}--/* parse_inline • parses inline markdown elements */-static void-parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)-{-	size_t i = 0, end = 0;-	uint8_t action = 0;-	struct buf work = { 0, 0, 0, 0 };--	if (rndr->work_bufs[BUFFER_SPAN].size +-		rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)-		return;--	while (i < size) {-		/* copying inactive chars into the output */-		while (end < size && (action = rndr->active_char[data[end]]) == 0) {-			end++;-		}--		if (rndr->cb.normal_text) {-			work.data = data + i;-			work.size = end - i;-			rndr->cb.normal_text(ob, &work, rndr->opaque);-		}-		else-			bufput(ob, data + i, end - i);--		if (end >= size) break;-		i = end;--		end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i);-		if (!end) /* no action from the callback */-			end = i + 1;-		else {-			i += end;-			end = i;-		}-	}-}--/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */-static size_t-find_emph_char(uint8_t *data, size_t size, uint8_t c)-{-	size_t i = 1;--	while (i < size) {-		while (i < size && data[i] != c && data[i] != '`' && data[i] != '[')-			i++;--		if (i == size)-			return 0;--		if (data[i] == c)-			return i;--		/* not counting escaped chars */-		if (i && data[i - 1] == '\\') {-			i++; continue;-		}--		if (data[i] == '`') {-			size_t span_nb = 0, bt;-			size_t tmp_i = 0;--			/* counting the number of opening backticks */-			while (i < size && data[i] == '`') {-				i++; span_nb++;-			}--			if (i >= size) return 0;--			/* finding the matching closing sequence */-			bt = 0;-			while (i < size && bt < span_nb) {-				if (!tmp_i && data[i] == c) tmp_i = i;-				if (data[i] == '`') bt++;-				else bt = 0;-				i++;-			}--			if (i >= size) return tmp_i;-		}-		/* skipping a link */-		else if (data[i] == '[') {-			size_t tmp_i = 0;-			uint8_t cc;--			i++;-			while (i < size && data[i] != ']') {-				if (!tmp_i && data[i] == c) tmp_i = i;-				i++;-			}--			i++;-			while (i < size && (data[i] == ' ' || data[i] == '\n'))-				i++;--			if (i >= size)-				return tmp_i;--			switch (data[i]) {-			case '[':-				cc = ']'; break;--			case '(':-				cc = ')'; break;--			default:-				if (tmp_i)-					return tmp_i;-				else-					continue;-			}--			i++;-			while (i < size && data[i] != cc) {-				if (!tmp_i && data[i] == c) tmp_i = i;-				i++;-			}--			if (i >= size)-				return tmp_i;--			i++;-		}-	}--	return 0;-}--/* 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 sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)-{-	size_t i = 0, len;-	struct buf *work = 0;-	int r;--	if (!rndr->cb.emphasis) return 0;--	/* skipping one symbol if coming from emph3 */-	if (size > 1 && data[0] == c && data[1] == c) i = 1;--	while (i < size) {-		len = find_emph_char(data + i, size - i, c);-		if (!len) return 0;-		i += len;-		if (i >= size) return 0;--		if (data[i] == c && !_isspace(data[i - 1])) {--			if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {-				if (!(i + 1 == size || _isspace(data[i + 1]) || ispunct(data[i + 1])))-					continue;-			}--			work = rndr_newbuf(rndr, BUFFER_SPAN);-			parse_inline(work, rndr, data, i);-			r = rndr->cb.emphasis(ob, work, rndr->opaque);-			rndr_popbuf(rndr, BUFFER_SPAN);-			return r ? i + 1 : 0;-		}-	}--	return 0;-}--/* parse_emph2 • parsing single emphase */-static size_t-parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)-{-	int (*render_method)(struct buf *ob, const struct buf *text, void *opaque);-	size_t i = 0, len;-	struct buf *work = 0;-	int r;--	render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis;--	if (!render_method)-		return 0;--	while (i < size) {-		len = find_emph_char(data + i, size - i, c);-		if (!len) return 0;-		i += len;--		if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) {-			work = rndr_newbuf(rndr, BUFFER_SPAN);-			parse_inline(work, rndr, data, i);-			r = render_method(ob, work, rndr->opaque);-			rndr_popbuf(rndr, BUFFER_SPAN);-			return r ? i + 2 : 0;-		}-		i++;-	}-	return 0;-}--/* 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 sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)-{-	size_t i = 0, len;-	int r;--	while (i < size) {-		len = find_emph_char(data + i, size - i, c);-		if (!len) return 0;-		i += len;--		/* skip whitespace preceded symbols */-		if (data[i] != c || _isspace(data[i - 1]))-			continue;--		if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) {-			/* triple symbol found */-			struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);--			parse_inline(work, rndr, data, i);-			r = rndr->cb.triple_emphasis(ob, work, rndr->opaque);-			rndr_popbuf(rndr, BUFFER_SPAN);-			return r ? i + 3 : 0;--		} else if (i + 1 < size && data[i + 1] == c) {-			/* double symbol found, handing over to emph1 */-			len = parse_emph1(ob, rndr, data - 2, size + 2, c);-			if (!len) return 0;-			else return len - 2;--		} else {-			/* single symbol found, handing over to emph2 */-			len = parse_emph2(ob, rndr, data - 1, size + 1, c);-			if (!len) return 0;-			else return len - 1;-		}-	}-	return 0;-}--/* char_emphasis • single and double emphasis parsing */-static size_t-char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	uint8_t c = data[0];-	size_t ret;--	if (size > 2 && data[1] != c) {-		/* whitespace cannot follow an opening emphasis;-		 * strikethrough only takes two characters '~~' */-		if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0)-			return 0;--		return ret + 1;-	}--	if (size > 3 && data[1] == c && data[2] != c) {-		if (_isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0)-			return 0;--		return ret + 2;-	}--	if (size > 4 && data[1] == c && data[2] == c && data[3] != c) {-		if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0)-			return 0;--		return ret + 3;-	}--	return 0;-}---/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */-static size_t-char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	if (offset < 2 || data[-1] != ' ' || data[-2] != ' ')-		return 0;--	/* removing the last space from ob and rendering */-	while (ob->size && ob->data[ob->size - 1] == ' ')-		ob->size--;--	return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0;-}---/* char_codespan • '`' parsing a code span (assuming codespan != 0) */-static size_t-char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	size_t end, nb = 0, i, f_begin, f_end;--	/* counting the number of backticks in the delimiter */-	while (nb < size && data[nb] == '`')-		nb++;--	/* finding the next delimiter */-	i = 0;-	for (end = nb; end < size && i < nb; end++) {-		if (data[end] == '`') i++;-		else i = 0;-	}--	if (i < nb && end >= size)-		return 0; /* no matching delimiter */--	/* trimming outside whitespaces */-	f_begin = nb;-	while (f_begin < end && data[f_begin] == ' ')-		f_begin++;--	f_end = end - nb;-	while (f_end > nb && data[f_end-1] == ' ')-		f_end--;--	/* real code span */-	if (f_begin < f_end) {-		struct buf work = { data + f_begin, f_end - f_begin, 0, 0 };-		if (!rndr->cb.codespan(ob, &work, rndr->opaque))-			end = 0;-	} else {-		if (!rndr->cb.codespan(ob, 0, rndr->opaque))-			end = 0;-	}--	return end;-}---/* char_escape • '\\' backslash escape */-static size_t-char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~";-	struct buf work = { 0, 0, 0, 0 };--	if (size > 1) {-		if (strchr(escape_chars, data[1]) == NULL)-			return 0;--		if (rndr->cb.normal_text) {-			work.data = data + 1;-			work.size = 1;-			rndr->cb.normal_text(ob, &work, rndr->opaque);-		}-		else bufputc(ob, data[1]);-	} else if (size == 1) {-		bufputc(ob, data[0]);-	}--	return 2;-}--/* 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 sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	size_t end = 1;-	struct buf work = { 0, 0, 0, 0 };--	if (end < size && data[end] == '#')-		end++;--	while (end < size && isalnum(data[end]))-		end++;--	if (end < size && data[end] == ';')-		end++; /* real entity */-	else-		return 0; /* lone '&' */--	if (rndr->cb.entity) {-		work.data = data;-		work.size = end;-		rndr->cb.entity(ob, &work, rndr->opaque);-	}-	else bufput(ob, data, end);--	return end;-}--/* char_langle_tag • '<' when tags or autolinks are allowed */-static size_t-char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	enum mkd_autolink altype = MKDA_NOT_AUTOLINK;-	size_t end = tag_length(data, size, &altype);-	struct buf work = { data, end, 0, 0 };-	int ret = 0;--	if (end > 2) {-		if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) {-			struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN);-			work.data = data + 1;-			work.size = end - 2;-			unscape_text(u_link, &work);-			ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque);-			rndr_popbuf(rndr, BUFFER_SPAN);-		}-		else if (rndr->cb.raw_html_tag)-			ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque);-	}--	if (!ret) return 0;-	else return end;-}--static size_t-char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	struct buf *link, *link_url, *link_text;-	size_t link_len, rewind;--	if (!rndr->cb.link || rndr->in_link_body)-		return 0;--	link = rndr_newbuf(rndr, BUFFER_SPAN);--	if ((link_len = sd_autolink__www(&rewind, link, data, offset, size, 0)) > 0) {-		link_url = rndr_newbuf(rndr, BUFFER_SPAN);-		BUFPUTSL(link_url, "http://");-		bufput(link_url, link->data, link->size);--		ob->size -= rewind;-		if (rndr->cb.normal_text) {-			link_text = rndr_newbuf(rndr, BUFFER_SPAN);-			rndr->cb.normal_text(link_text, link, rndr->opaque);-			rndr->cb.link(ob, link_url, NULL, link_text, rndr->opaque);-			rndr_popbuf(rndr, BUFFER_SPAN);-		} else {-			rndr->cb.link(ob, link_url, NULL, link, rndr->opaque);-		}-		rndr_popbuf(rndr, BUFFER_SPAN);-	}--	rndr_popbuf(rndr, BUFFER_SPAN);-	return link_len;-}--static size_t-char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	struct buf *link;-	size_t link_len, rewind;--	if (!rndr->cb.autolink || rndr->in_link_body)-		return 0;--	link = rndr_newbuf(rndr, BUFFER_SPAN);--	if ((link_len = sd_autolink__email(&rewind, link, data, offset, size, 0)) > 0) {-		ob->size -= rewind;-		rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque);-	}--	rndr_popbuf(rndr, BUFFER_SPAN);-	return link_len;-}--static size_t-char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	struct buf *link;-	size_t link_len, rewind;--	if (!rndr->cb.autolink || rndr->in_link_body)-		return 0;--	link = rndr_newbuf(rndr, BUFFER_SPAN);--	if ((link_len = sd_autolink__url(&rewind, link, data, offset, size, 0)) > 0) {-		ob->size -= rewind;-		rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque);-	}--	rndr_popbuf(rndr, BUFFER_SPAN);-	return link_len;-}--/* char_link • '[': parsing a link or an image */-static size_t-char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *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;-	struct buf *content = 0;-	struct buf *link = 0;-	struct buf *title = 0;-	struct buf *u_link = 0;-	size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size;-	int text_has_nl = 0, ret = 0;-	int in_title = 0, qtype = 0;--	/* checking whether the correct renderer exists */-	if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link))-		goto cleanup;--	/* looking for the matching closing bracket */-	for (level = 1; i < size; i++) {-		if (data[i] == '\n')-			text_has_nl = 1;--		else if (data[i - 1] == '\\')-			continue;--		else if (data[i] == '[')-			level++;--		else if (data[i] == ']') {-			level--;-			if (level <= 0)-				break;-		}-	}--	if (i >= size)-		goto cleanup;--	txt_e = i;-	i++;--	/* skip any amount of whitespace or newline */-	/* (this is much more laxist than original markdown syntax) */-	while (i < size && _isspace(data[i]))-		i++;--	/* inline style link */-	if (i < size && data[i] == '(') {-		/* skipping initial whitespace */-		i++;--		while (i < size && _isspace(data[i]))-			i++;--		link_b = i;--		/* looking for link end: ' " ) */-		while (i < size) {-			if (data[i] == '\\') i += 2;-			else if (data[i] == ')') break;-			else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;-			else i++;-		}--		if (i >= size) goto cleanup;-		link_e = i;--		/* looking for title end if present */-		if (data[i] == '\'' || data[i] == '"') {-			qtype = data[i];-			in_title = 1;-			i++;-			title_b = i;--			while (i < size) {-				if (data[i] == '\\') i += 2;-				else if (data[i] == qtype) {in_title = 0; i++;}-				else if ((data[i] == ')') && !in_title) break;-				else i++;-			}--			if (i >= size) goto cleanup;--			/* skipping whitespaces after title */-			title_e = i - 1;-			while (title_e > title_b && _isspace(data[title_e]))-				title_e--;--			/* checking for closing quote presence */-			if (data[title_e] != '\'' &&  data[title_e] != '"') {-				title_b = title_e = 0;-				link_e = i;-			}-		}--		/* remove whitespace at the end of the link */-		while (link_e > link_b && _isspace(data[link_e - 1]))-			link_e--;--		/* remove optional angle brackets around the link */-		if (data[link_b] == '<') link_b++;-		if (data[link_e - 1] == '>') link_e--;--		/* building escaped link and title */-		if (link_e > link_b) {-			link = rndr_newbuf(rndr, BUFFER_SPAN);-			bufput(link, data + link_b, link_e - link_b);-		}--		if (title_e > title_b) {-			title = rndr_newbuf(rndr, BUFFER_SPAN);-			bufput(title, data + title_b, title_e - title_b);-		}--		i++;-	}--	/* reference style link */-	else if (i < size && data[i] == '[') {-		struct buf id = { 0, 0, 0, 0 };-		struct link_ref *lr;--		/* looking for the id */-		i++;-		link_b = i;-		while (i < size && data[i] != ']') i++;-		if (i >= size) goto cleanup;-		link_e = i;--		/* finding the link_ref */-		if (link_b == link_e) {-			if (text_has_nl) {-				struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN);-				size_t j;--				for (j = 1; j < txt_e; j++) {-					if (data[j] != '\n')-						bufputc(b, data[j]);-					else if (data[j - 1] != ' ')-						bufputc(b, ' ');-				}--				id.data = b->data;-				id.size = b->size;-			} else {-				id.data = data + 1;-				id.size = txt_e - 1;-			}-		} else {-			id.data = data + link_b;-			id.size = link_e - link_b;-		}--		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;-		i++;-	}--	/* shortcut reference style link */-	else {-		struct buf id = { 0, 0, 0, 0 };-		struct link_ref *lr;--		/* crafting the id */-		if (text_has_nl) {-			struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN);-			size_t j;--			for (j = 1; j < txt_e; j++) {-				if (data[j] != '\n')-					bufputc(b, data[j]);-				else if (data[j - 1] != ' ')-					bufputc(b, ' ');-			}--			id.data = b->data;-			id.size = b->size;-		} else {-			id.data = data + 1;-			id.size = txt_e - 1;-		}--		/* finding the link_ref */-		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;--		/* rewinding the whitespace */-		i = txt_e + 1;-	}--	/* building content: img alt is escaped, link content is parsed */-	if (txt_e > 1) {-		content = rndr_newbuf(rndr, BUFFER_SPAN);-		if (is_img) {-			bufput(content, data + 1, txt_e - 1);-		} else {-			/* disable autolinking when parsing inline the-			 * content of a link */-			rndr->in_link_body = 1;-			parse_inline(content, rndr, data + 1, txt_e - 1);-			rndr->in_link_body = 0;-		}-	}--	if (link) {-		u_link = rndr_newbuf(rndr, BUFFER_SPAN);-		unscape_text(u_link, link);-	}--	/* calling the relevant rendering function */-	if (is_img) {-		if (ob->size && ob->data[ob->size - 1] == '!')-			ob->size -= 1;--		ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque);-	} else {-		ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque);-	}--	/* cleanup */-cleanup:-	rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size;-	return ret ? i : 0;-}--static size_t-char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)-{-	size_t sup_start, sup_len;-	struct buf *sup;--	if (!rndr->cb.superscript)-		return 0;--	if (size < 2)-		return 0;--	if (data[1] == '(') {-		sup_start = sup_len = 2;--		while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\')-			sup_len++;--		if (sup_len == size)-			return 0;-	} else {-		sup_start = sup_len = 1;--		while (sup_len < size && !_isspace(data[sup_len]))-			sup_len++;-	}--	if (sup_len - sup_start == 0)-		return (sup_start == 2) ? 3 : 0;--	sup = rndr_newbuf(rndr, BUFFER_SPAN);-	parse_inline(sup, rndr, data + sup_start, sup_len - sup_start);-	rndr->cb.superscript(ob, sup, rndr->opaque);-	rndr_popbuf(rndr, BUFFER_SPAN);--	return (sup_start == 2) ? sup_len + 1 : sup_len;-}--/*********************************- * BLOCK-LEVEL PARSING FUNCTIONS *- *********************************/--/* is_empty • returns the line length when it is empty, 0 otherwise */-static size_t-is_empty(uint8_t *data, size_t size)-{-	size_t i;--	for (i = 0; i < size && data[i] != '\n'; i++)-		if (data[i] != ' ')-			return 0;--	return i + 1;-}--/* is_hrule • returns whether a line is a horizontal rule */-static int-is_hrule(uint8_t *data, size_t size)-{-	size_t i = 0, n = 0;-	uint8_t c;--	/* skipping initial spaces */-	if (size < 3) return 0;-	if (data[0] == ' ') { i++;-	if (data[1] == ' ') { i++;-	if (data[2] == ' ') { i++; } } }--	/* looking at the hrule uint8_t */-	if (i + 2 >= size-	|| (data[i] != '*' && data[i] != '-' && data[i] != '_'))-		return 0;-	c = data[i];--	/* the whole line must be the char or whitespace */-	while (i < size && data[i] != '\n') {-		if (data[i] == c) n++;-		else if (data[i] != ' ')-			return 0;--		i++;-	}--	return n >= 3;-}--/* check if a line begins with a code fence; return the- * width of the code fence */-static size_t-prefix_codefence(uint8_t *data, size_t size)-{-	size_t i = 0, n = 0;-	uint8_t c;--	/* skipping initial spaces */-	if (size < 3) return 0;-	if (data[0] == ' ') { i++;-	if (data[1] == ' ') { i++;-	if (data[2] == ' ') { i++; } } }--	/* looking at the hrule uint8_t */-	if (i + 2 >= size || !(data[i] == '~' || data[i] == '`'))-		return 0;--	c = data[i];--	/* the whole line must be the uint8_t or whitespace */-	while (i < size && data[i] == c) {-		n++; i++;-	}--	if (n < 3)-		return 0;--	return i;-}--/* check if a line is a code fence; return its size if it is */-static size_t-is_codefence(uint8_t *data, size_t size, struct buf *syntax)-{-	size_t i = 0, syn_len = 0;-	uint8_t *syn_start;--	i = prefix_codefence(data, size);-	if (i == 0)-		return 0;--	while (i < size && data[i] == ' ')-		i++;--	syn_start = data + i;--	if (i < size && data[i] == '{') {-		i++; syn_start++;--		while (i < size && data[i] != '}' && data[i] != '\n') {-			syn_len++; i++;-		}--		if (i == size || data[i] != '}')-			return 0;--		/* strip all whitespace at the beginning and the end-		 * of the {} block */-		while (syn_len > 0 && _isspace(syn_start[0])) {-			syn_start++; syn_len--;-		}--		while (syn_len > 0 && _isspace(syn_start[syn_len - 1]))-			syn_len--;--		i++;-	} else {-		while (i < size && !_isspace(data[i])) {-			syn_len++; i++;-		}-	}--	if (syntax) {-		syntax->data = syn_start;-		syntax->size = syn_len;-	}--	while (i < size && data[i] != '\n') {-		if (!_isspace(data[i]))-			return 0;--		i++;-	}--	return i + 1;-}--/* is_atxheader • returns whether the line is a hash-prefixed header */-static int-is_atxheader(struct sd_markdown *rndr, uint8_t *data, size_t size)-{-	if (data[0] != '#')-		return 0;--	if (rndr->ext_flags & MKDEXT_SPACE_HEADERS) {-		size_t level = 0;--		while (level < size && level < 6 && data[level] == '#')-			level++;--		if (level < size && data[level] != ' ')-			return 0;-	}--	return 1;-}--/* is_headerline • returns whether the line is a setext-style hdr underline */-static int-is_headerline(uint8_t *data, size_t size)-{-	size_t i = 0;--	/* test of level 1 header */-	if (data[i] == '=') {-		for (i = 1; i < size && data[i] == '='; i++);-		while (i < size && data[i] == ' ') i++;-		return (i >= size || data[i] == '\n') ? 1 : 0; }--	/* test of level 2 header */-	if (data[i] == '-') {-		for (i = 1; i < size && data[i] == '-'; i++);-		while (i < size && data[i] == ' ') i++;-		return (i >= size || data[i] == '\n') ? 2 : 0; }--	return 0;-}--static int-is_next_headerline(uint8_t *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(uint8_t *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] == '>') {-		if (i + 1 < size && data[i + 1] == ' ')-			return i + 2;--		return i + 1;-	}--	return 0;-}--/* prefix_code • returns prefix length for block code*/-static size_t-prefix_code(uint8_t *data, size_t size)-{-	if (size > 3 && data[0] == ' ' && data[1] == ' '-		&& data[2] == ' ' && data[3] == ' ') return 4;--	return 0;-}--/* prefix_oli • returns ordered list item prefix */-static size_t-prefix_oli(uint8_t *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] != ' ')-		return 0;--	if (is_next_headerline(data + i, size - i))-		return 0;--	return i + 2;-}--/* prefix_uli • returns ordered list item prefix */-static size_t-prefix_uli(uint8_t *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] != ' ')-		return 0;--	if (is_next_headerline(data + i, size - i))-		return 0;--	return i + 2;-}---/* parse_block • parsing of one block, returning next uint8_t to parse */-static void parse_block(struct buf *ob, struct sd_markdown *rndr,-			uint8_t *data, size_t size);---/* parse_blockquote • handles parsing of a blockquote fragment */-static size_t-parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)-{-	size_t beg, end = 0, pre, work_size = 0;-	uint8_t *work_data = 0;-	struct buf *out = 0;--	out = rndr_newbuf(rndr, BUFFER_BLOCK);-	beg = 0;-	while (beg < size) {-		for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);--		pre = prefix_quote(data + beg, end - beg);--		if (pre)-			beg += pre; /* skipping prefix */--		/* empty line followed by non-quote line */-		else if (is_empty(data + beg, end - beg) &&-				(end >= size || (prefix_quote(data + end, size - end) == 0 &&-				!is_empty(data + end, size - end))))-			break;--		if (beg < end) { /* copy into the in-place working buffer */-			/* bufput(work, data + beg, end - beg); */-			if (!work_data)-				work_data = data + beg;-			else if (data + beg != work_data + work_size)-				memmove(work_data + work_size, data + beg, end - beg);-			work_size += end - beg;-		}-		beg = end;-	}--	parse_block(out, rndr, work_data, work_size);-	if (rndr->cb.blockquote)-		rndr->cb.blockquote(ob, out, rndr->opaque);-	rndr_popbuf(rndr, BUFFER_BLOCK);-	return end;-}--static size_t-parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render);--/* parse_blockquote • handles parsing of a regular paragraph */-static size_t-parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)-{-	size_t i = 0, end = 0;-	int level = 0;-	struct buf work = { data, 0, 0, 0 };--	while (i < size) {-		for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */;--		if (is_empty(data + i, size - i))-			break;--		if ((level = is_headerline(data + i, size - i)) != 0)-			break;--		if (is_atxheader(rndr, data + i, size - i) ||-			is_hrule(data + i, size - i) ||-			prefix_quote(data + i, size - i)) {-			end = i;-			break;-		}--		/*-		 * Early termination of a paragraph with the same logic-		 * as Markdown 1.0.0. If this logic is applied, the-		 * Markdown 1.0.3 test suite won't pass cleanly-		 *-		 * :: If the first character in a new line is not a letter,-		 * let's check to see if there's some kind of block starting-		 * here-		 */-		if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !isalnum(data[i])) {-			if (prefix_oli(data + i, size - i) ||-				prefix_uli(data + i, size - i)) {-				end = i;-				break;-			}--			/* see if an html block starts here */-			if (data[i] == '<' && rndr->cb.blockhtml &&-				parse_htmlblock(ob, rndr, data + i, size - i, 0)) {-				end = i;-				break;-			}--			/* see if a code fence starts here */-			if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 &&-				is_codefence(data + i, size - i, NULL) != 0) {-				end = i;-				break;-			}-		}--		i = end;-	}--	work.size = i;-	while (work.size && data[work.size - 1] == '\n')-		work.size--;--	if (!level) {-		struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);-		parse_inline(tmp, rndr, work.data, work.size);-		if (rndr->cb.paragraph)-			rndr->cb.paragraph(ob, tmp, rndr->opaque);-		rndr_popbuf(rndr, BUFFER_BLOCK);-	} else {-		struct buf *header_work;--		if (work.size) {-			size_t beg;-			i = work.size;-			work.size -= 1;--			while (work.size && data[work.size] != '\n')-				work.size -= 1;--			beg = work.size + 1;-			while (work.size && data[work.size - 1] == '\n')-				work.size -= 1;--			if (work.size > 0) {-				struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);-				parse_inline(tmp, rndr, work.data, work.size);--				if (rndr->cb.paragraph)-					rndr->cb.paragraph(ob, tmp, rndr->opaque);--				rndr_popbuf(rndr, BUFFER_BLOCK);-				work.data += beg;-				work.size = i - beg;-			}-			else work.size = i;-		}--		header_work = rndr_newbuf(rndr, BUFFER_SPAN);-		parse_inline(header_work, rndr, work.data, work.size);--		if (rndr->cb.header)-			rndr->cb.header(ob, header_work, (int)level, rndr->opaque);--		rndr_popbuf(rndr, BUFFER_SPAN);-	}--	return end;-}--/* parse_fencedcode • handles parsing of a block-level code fragment */-static size_t-parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)-{-	size_t beg, end;-	struct buf *work = 0;-	struct buf lang = { 0, 0, 0, 0 };--	beg = is_codefence(data, size, &lang);-	if (beg == 0) return 0;--	work = rndr_newbuf(rndr, BUFFER_BLOCK);--	while (beg < size) {-		size_t fence_end;-		struct buf fence_trail = { 0, 0, 0, 0 };--		fence_end = is_codefence(data + beg, size - beg, &fence_trail);-		if (fence_end != 0 && fence_trail.size == 0) {-			beg += fence_end;-			break;-		}--		for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);--		if (beg < end) {-			/* verbatim copy to the working buffer,-				escaping entities */-			if (is_empty(data + beg, end - beg))-				bufputc(work, '\n');-			else bufput(work, data + beg, end - beg);-		}-		beg = end;-	}--	if (work->size && work->data[work->size - 1] != '\n')-		bufputc(work, '\n');--	if (rndr->cb.blockcode)-		rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque);--	rndr_popbuf(rndr, BUFFER_BLOCK);-	return beg;-}--static size_t-parse_blockcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)-{-	size_t beg, end, pre;-	struct buf *work = 0;--	work = rndr_newbuf(rndr, BUFFER_BLOCK);--	beg = 0;-	while (beg < size) {-		for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {};-		pre = prefix_code(data + beg, end - beg);--		if (pre)-			beg += pre; /* skipping prefix */-		else if (!is_empty(data + beg, end - beg))-			/* non-empty non-prefixed line breaks the pre */-			break;--		if (beg < end) {-			/* verbatim copy to the working buffer,-				escaping entities */-			if (is_empty(data + beg, end - beg))-				bufputc(work, '\n');-			else bufput(work, data + beg, end - beg);-		}-		beg = end;-	}--	while (work->size && work->data[work->size - 1] == '\n')-		work->size -= 1;--	bufputc(work, '\n');--	if (rndr->cb.blockcode)-		rndr->cb.blockcode(ob, work, NULL, rndr->opaque);--	rndr_popbuf(rndr, BUFFER_BLOCK);-	return beg;-}--/* parse_listitem • parsing of a single list item */-/*	assuming initial prefix is already removed */-static size_t-parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags)-{-	struct buf *work = 0, *inter = 0;-	size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;-	int in_empty = 0, has_inside_empty = 0, in_fence = 0;--	/* keeping track of the first indentation prefix */-	while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')-		orgpre++;--	beg = prefix_uli(data, size);-	if (!beg)-		beg = prefix_oli(data, size);--	if (!beg)-		return 0;--	/* skipping to the beginning of the following line */-	end = beg;-	while (end < size && data[end - 1] != '\n')-		end++;--	/* getting working buffers */-	work = rndr_newbuf(rndr, BUFFER_SPAN);-	inter = rndr_newbuf(rndr, BUFFER_SPAN);--	/* putting the first line into the working buffer */-	bufput(work, data + beg, end - beg);-	beg = end;--	/* process the following lines */-	while (beg < size) {-		size_t has_next_uli = 0, has_next_oli = 0;--		end++;--		while (end < size && data[end - 1] != '\n')-			end++;--		/* process an empty line */-		if (is_empty(data + beg, end - beg)) {-			in_empty = 1;-			beg = end;-			continue;-		}--		/* calculating the indentation */-		i = 0;-		while (i < 4 && beg + i < end && data[beg + i] == ' ')-			i++;--		pre = i;--		if (rndr->ext_flags & MKDEXT_FENCED_CODE) {-			if (is_codefence(data + beg + i, end - beg - i, NULL) != 0)-				in_fence = !in_fence;-		}--		/* Only check for new list items if we are **not** inside-		 * a fenced code block */-		if (!in_fence) {-			has_next_uli = prefix_uli(data + beg + i, end - beg - i);-			has_next_oli = prefix_oli(data + beg + i, end - beg - i);-		}--		/* checking for ul/ol switch */-		if (in_empty && (-			((*flags & MKD_LIST_ORDERED) && has_next_uli) ||-			(!(*flags & MKD_LIST_ORDERED) && has_next_oli))){-			*flags |= MKD_LI_END;-			break; /* the following item must have same list type */-		}--		/* checking for a new item */-		if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) {-			if (in_empty)-				has_inside_empty = 1;--			if (pre == orgpre) /* the following item must have */-				break;             /* the same indentation */--			if (!sublist)-				sublist = work->size;-		}-		/* joining only indented stuff after empty lines;-		 * note that now we only require 1 space of indentation-		 * to continue a list */-		else if (in_empty && pre == 0) {-			*flags |= MKD_LI_END;-			break;-		}-		else if (in_empty) {-			bufputc(work, '\n');-			has_inside_empty = 1;-		}--		in_empty = 0;--		/* adding the line without prefix into the working buffer */-		bufput(work, data + beg + i, end - beg - i);-		beg = end;-	}--	/* render of li contents */-	if (has_inside_empty)-		*flags |= MKD_LI_BLOCK;--	if (*flags & MKD_LI_BLOCK) {-		/* 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);-		}-		else-			parse_block(inter, rndr, work->data, work->size);-	} else {-		/* intermediate render of inline li */-		if (sublist && sublist < work->size) {-			parse_inline(inter, rndr, work->data, sublist);-			parse_block(inter, rndr, work->data + sublist, work->size - sublist);-		}-		else-			parse_inline(inter, rndr, work->data, work->size);-	}--	/* render of li itself */-	if (rndr->cb.listitem)-		rndr->cb.listitem(ob, inter, *flags, rndr->opaque);--	rndr_popbuf(rndr, BUFFER_SPAN);-	rndr_popbuf(rndr, BUFFER_SPAN);-	return beg;-}---/* parse_list • parsing ordered or unordered list block */-static size_t-parse_list(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int flags)-{-	struct buf *work = 0;-	size_t i = 0, j;--	work = rndr_newbuf(rndr, BUFFER_BLOCK);--	while (i < size) {-		j = parse_listitem(work, rndr, data + i, size - i, &flags);-		i += j;--		if (!j || (flags & MKD_LI_END))-			break;-	}--	if (rndr->cb.list)-		rndr->cb.list(ob, work, flags, rndr->opaque);-	rndr_popbuf(rndr, BUFFER_BLOCK);-	return i;-}--/* parse_atxheader • parsing of atx-style headers */-static size_t-parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)-{-	size_t level = 0;-	size_t i, end, skip;--	while (level < size && level < 6 && data[level] == '#')-		level++;--	for (i = level; i < size && data[i] == ' '; i++);--	for (end = i; end < size && data[end] != '\n'; end++);-	skip = end;--	while (end && data[end - 1] == '#')-		end--;--	while (end && data[end - 1] == ' ')-		end--;--	if (end > i) {-		struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);--		parse_inline(work, rndr, data + i, end - i);--		if (rndr->cb.header)-			rndr->cb.header(ob, work, (int)level, rndr->opaque);--		rndr_popbuf(rndr, BUFFER_SPAN);-	}--	return skip;-}---/* htmlblock_end • checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */-/*	returns the length on match, 0 otherwise */-static size_t-htmlblock_end_tag(-	const char *tag,-	size_t tag_len,-	struct sd_markdown *rndr,-	uint8_t *data,-	size_t size)-{-	size_t i, w;--	/* checking if tag is a match */-	if (tag_len + 3 >= size ||-		strncasecmp((char *)data + 2, tag, tag_len) != 0 ||-		data[tag_len + 2] != '>')-		return 0;--	/* checking white lines */-	i = tag_len + 3;-	w = 0;-	if (i < size && (w = is_empty(data + i, size - i)) == 0)-		return 0; /* non-blank after tag */-	i += w;-	w = 0;--	if (i < size)-		w = is_empty(data + i, size - i);--	return i + w;-}--static size_t-htmlblock_end(const char *curtag,-	struct sd_markdown *rndr,-	uint8_t *data,-	size_t size,-	int start_of_line)-{-	size_t tag_size = strlen(curtag);-	size_t i = 1, end_tag;-	int block_lines = 0;--	while (i < size) {-		i++;-		while (i < size && !(data[i - 1] == '<' && data[i] == '/')) {-			if (data[i] == '\n')-				block_lines++;--			i++;-		}--		/* If we are only looking for unindented tags, skip the tag-		 * if it doesn't follow a newline.-		 *-		 * The only exception to this is if the tag is still on the-		 * initial line; in that case it still counts as a closing-		 * tag-		 */-		if (start_of_line && block_lines > 0 && data[i - 2] != '\n')-			continue;--		if (i + 2 + tag_size >= size)-			break;--		end_tag = htmlblock_end_tag(curtag, tag_size, rndr, data + i - 1, size - i + 1);-		if (end_tag)-			return i + end_tag - 1;-	}--	return 0;-}---/* parse_htmlblock • parsing of inline HTML block */-static size_t-parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render)-{-	size_t i, j = 0, tag_end;-	const char *curtag = NULL;-	struct buf work = { data, 0, 0, 0 };--	/* identification of the opening tag */-	if (size < 2 || data[0] != '<')-		return 0;--	i = 1;-	while (i < size && data[i] != '>' && data[i] != ' ')-		i++;--	if (i < size)-		curtag = find_block_tag((char *)data + 1, (int)i - 1);--	/* handling of special cases */-	if (!curtag) {--		/* HTML comment, laxist form */-		if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') {-			i = 5;--			while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>'))-				i++;--			i++;--			if (i < size)-				j = is_empty(data + i, size - i);--			if (j) {-				work.size = i + j;-				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 */-		if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {-			i = 3;-			while (i < size && data[i] != '>')-				i++;--			if (i + 1 < size) {-				i++;-				j = is_empty(data + i, size - i);-				if (j) {-					work.size = i + j;-					if (do_render && rndr->cb.blockhtml)-						rndr->cb.blockhtml(ob, &work, rndr->opaque);-					return work.size;-				}-			}-		}--		/* no special case recognised */-		return 0;-	}--	/* looking for an unindented matching closing tag */-	/*	followed by a blank line */-	tag_end = htmlblock_end(curtag, rndr, data, size, 1);--	/* if not found, trying a second pass looking for indented match */-	/* but not if tag is "ins" or "del" (following original Markdown.pl) */-	if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) {-		tag_end = htmlblock_end(curtag, rndr, data, size, 0);-	}--	if (!tag_end)-		return 0;--	/* the end of the block has been found */-	work.size = tag_end;-	if (do_render && rndr->cb.blockhtml)-		rndr->cb.blockhtml(ob, &work, rndr->opaque);--	return tag_end;-}--static void-parse_table_row(-	struct buf *ob,-	struct sd_markdown *rndr,-	uint8_t *data,-	size_t size,-	size_t columns,-	int *col_data,-	int header_flag)-{-	size_t i = 0, col;-	struct buf *row_work = 0;--	if (!rndr->cb.table_cell || !rndr->cb.table_row)-		return;--	row_work = rndr_newbuf(rndr, BUFFER_SPAN);--	if (i < size && data[i] == '|')-		i++;--	for (col = 0; col < columns && i < size; ++col) {-		size_t cell_start, cell_end;-		struct buf *cell_work;--		cell_work = rndr_newbuf(rndr, BUFFER_SPAN);--		while (i < size && _isspace(data[i]))-			i++;--		cell_start = i;--		while (i < size && data[i] != '|')-			i++;--		cell_end = i - 1;--		while (cell_end > cell_start && _isspace(data[cell_end]))-			cell_end--;--		parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start);-		rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque);--		rndr_popbuf(rndr, BUFFER_SPAN);-		i++;-	}--	for (; col < columns; ++col) {-		struct buf empty_cell = { 0, 0, 0, 0 };-		rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque);-	}--	rndr->cb.table_row(ob, row_work, rndr->opaque);--	rndr_popbuf(rndr, BUFFER_SPAN);-}--static size_t-parse_table_header(-	struct buf *ob,-	struct sd_markdown *rndr,-	uint8_t *data,-	size_t size,-	size_t *columns,-	int **column_data)-{-	int pipes;-	size_t i = 0, col, header_end, under_end;--	pipes = 0;-	while (i < size && data[i] != '\n')-		if (data[i++] == '|')-			pipes++;--	if (i == size || pipes == 0)-		return 0;--	header_end = i;--	while (header_end > 0 && _isspace(data[header_end - 1]))-		header_end--;--	if (data[0] == '|')-		pipes--;--	if (header_end && data[header_end - 1] == '|')-		pipes--;--	*columns = pipes + 1;-	*column_data = calloc(*columns, sizeof(int));--	/* Parse the header underline */-	i++;-	if (i < size && data[i] == '|')-		i++;--	under_end = i;-	while (under_end < size && data[under_end] != '\n')-		under_end++;--	for (col = 0; col < *columns && i < under_end; ++col) {-		size_t dashes = 0;--		while (i < under_end && data[i] == ' ')-			i++;--		if (data[i] == ':') {-			i++; (*column_data)[col] |= MKD_TABLE_ALIGN_L;-			dashes++;-		}--		while (i < under_end && data[i] == '-') {-			i++; dashes++;-		}--		if (i < under_end && data[i] == ':') {-			i++; (*column_data)[col] |= MKD_TABLE_ALIGN_R;-			dashes++;-		}--		while (i < under_end && data[i] == ' ')-			i++;--		if (i < under_end && data[i] != '|')-			break;--		if (dashes < 3)-			break;--		i++;-	}--	if (col < *columns)-		return 0;--	parse_table_row(-		ob, rndr, data,-		header_end,-		*columns,-		*column_data,-		MKD_TABLE_HEADER-	);--	return under_end + 1;-}--static size_t-parse_table(-	struct buf *ob,-	struct sd_markdown *rndr,-	uint8_t *data,-	size_t size)-{-	size_t i;--	struct buf *header_work = 0;-	struct buf *body_work = 0;--	size_t columns;-	int *col_data = NULL;--	header_work = rndr_newbuf(rndr, BUFFER_SPAN);-	body_work = rndr_newbuf(rndr, BUFFER_BLOCK);--	i = parse_table_header(header_work, rndr, data, size, &columns, &col_data);-	if (i > 0) {--		while (i < size) {-			size_t row_start;-			int pipes = 0;--			row_start = i;--			while (i < size && data[i] != '\n')-				if (data[i++] == '|')-					pipes++;--			if (pipes == 0 || i == size) {-				i = row_start;-				break;-			}--			parse_table_row(-				body_work,-				rndr,-				data + row_start,-				i - row_start,-				columns,-				col_data, 0-			);--			i++;-		}--		if (rndr->cb.table)-			rndr->cb.table(ob, header_work, body_work, rndr->opaque);-	}--	free(col_data);-	rndr_popbuf(rndr, BUFFER_SPAN);-	rndr_popbuf(rndr, BUFFER_BLOCK);-	return i;-}--/* parse_block • parsing of one block, returning next uint8_t to parse */-static void-parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)-{-	size_t beg, end, i;-	uint8_t *txt_data;-	beg = 0;--	if (rndr->work_bufs[BUFFER_SPAN].size +-		rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)-		return;--	while (beg < size) {-		txt_data = data + beg;-		end = size - beg;--		if (is_atxheader(rndr, txt_data, end))-			beg += parse_atxheader(ob, rndr, txt_data, end);--		else if (data[beg] == '<' && rndr->cb.blockhtml &&-				(i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0)-			beg += i;--		else if ((i = is_empty(txt_data, end)) != 0)-			beg += i;--		else if (is_hrule(txt_data, end)) {-			if (rndr->cb.hrule)-				rndr->cb.hrule(ob, rndr->opaque);--			while (beg < size && data[beg] != '\n')-				beg++;--			beg++;-		}--		else if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 &&-			(i = parse_fencedcode(ob, rndr, txt_data, end)) != 0)-			beg += i;--		else if ((rndr->ext_flags & MKDEXT_TABLES) != 0 &&-			(i = parse_table(ob, rndr, txt_data, end)) != 0)-			beg += i;--		else if (prefix_quote(txt_data, end))-			beg += parse_blockquote(ob, rndr, txt_data, end);--		else if (prefix_code(txt_data, end))-			beg += parse_blockcode(ob, rndr, txt_data, end);--		else if (prefix_uli(txt_data, end))-			beg += parse_list(ob, rndr, txt_data, end, 0);--		else if (prefix_oli(txt_data, end))-			beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);--		else-			beg += parse_paragraph(ob, rndr, txt_data, end);-	}-}----/*********************- * REFERENCE PARSING *- *********************/--/* is_ref • returns whether a line is a reference or not */-static int-is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)-{-/*	int n; */-	size_t i = 0;-	size_t id_offset, id_end;-	size_t link_offset, link_end;-	size_t title_offset, title_end;-	size_t line_end;--	/* up to 3 optional leading spaces */-	if (beg + 3 >= end) return 0;-	if (data[beg] == ' ') { i = 1;-	if (data[beg + 1] == ' ') { i = 2;-	if (data[beg + 2] == ' ') { i = 3;-	if (data[beg + 3] == ' ') return 0; } } }-	i += beg;--	/* id part: anything but a newline between brackets */-	if (data[i] != '[') return 0;-	i++;-	id_offset = i;-	while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')-		i++;-	if (i >= end || data[i] != ']') return 0;-	id_end = i;--	/* spacer: colon (space | tab)* newline? (space | tab)* */-	i++;-	if (i >= end || data[i] != ':') return 0;-	i++;-	while (i < end && data[i] == ' ') i++;-	if (i < end && (data[i] == '\n' || data[i] == '\r')) {-		i++;-		if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }-	while (i < end && data[i] == ' ') i++;-	if (i >= end) return 0;--	/* link: whitespace-free sequence, optionally between angle brackets */-	if (data[i] == '<')-		i++;--	link_offset = i;--	while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r')-		i++;--	if (data[i - 1] == '>') link_end = i - 1;-	else link_end = i;--	/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */-	while (i < end && data[i] == ' ') i++;-	if (i < end && data[i] != '\n' && data[i] != '\r'-			&& data[i] != '\'' && data[i] != '"' && data[i] != '(')-		return 0;-	line_end = 0;-	/* computing end-of-line */-	if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i;-	if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')-		line_end = i + 1;--	/* optional (space|tab)* spacer after a newline */-	if (line_end) {-		i = line_end + 1;-		while (i < end && data[i] == ' ') i++; }--	/* optional title: any non-newline sequence enclosed in '"()-					alone on its line */-	title_offset = title_end = 0;-	if (i + 1 < end-	&& (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {-		i++;-		title_offset = i;-		/* looking for EOL */-		while (i < end && data[i] != '\n' && data[i] != '\r') i++;-		if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')-			title_end = i + 1;-		else	title_end = i;-		/* stepping back */-		i -= 1;-		while (i > title_offset && data[i] == ' ')-			i -= 1;-		if (i > title_offset-		&& (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {-			line_end = title_end;-			title_end = i; } }--	if (!line_end || link_end == link_offset)-		return 0; /* garbage after the link empty link */--	/* a valid ref has been found, filling-in return structures */-	if (last)-		*last = line_end;--	if (refs) {-		struct link_ref *ref;--		ref = add_link_ref(refs, data + id_offset, id_end - id_offset);-		if (!ref)-			return 0;--		ref->link = bufnew(link_end - link_offset);-		bufput(ref->link, data + link_offset, link_end - link_offset);--		if (title_end > title_offset) {-			ref->title = bufnew(title_end - title_offset);-			bufput(ref->title, data + title_offset, title_end - title_offset);-		}-	}--	return 1;-}--static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size)-{-	size_t  i = 0, tab = 0;--	while (i < size) {-		size_t org = i;--		while (i < size && line[i] != '\t') {-			i++; tab++;-		}--		if (i > org)-			bufput(ob, line + org, i - org);--		if (i >= size)-			break;--		do {-			bufputc(ob, ' '); tab++;-		} while (tab % 4);--		i++;-	}-}--/**********************- * EXPORTED FUNCTIONS *- **********************/--struct sd_markdown *-sd_markdown_new(-	unsigned int extensions,-	size_t max_nesting,-	const struct sd_callbacks *callbacks,-	void *opaque)-{-	struct sd_markdown *md = NULL;--	assert(max_nesting > 0 && callbacks);--	md = malloc(sizeof(struct sd_markdown));-	if (!md)-		return NULL;--	memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));--	stack_init(&md->work_bufs[BUFFER_BLOCK], 4);-	stack_init(&md->work_bufs[BUFFER_SPAN], 8);--	memset(md->active_char, 0x0, 256);--	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)-			md->active_char['~'] = MD_CHAR_EMPHASIS;-	}--	if (md->cb.codespan)-		md->active_char['`'] = MD_CHAR_CODESPAN;--	if (md->cb.linebreak)-		md->active_char['\n'] = MD_CHAR_LINEBREAK;--	if (md->cb.image || md->cb.link)-		md->active_char['['] = MD_CHAR_LINK;--	md->active_char['<'] = MD_CHAR_LANGLE;-	md->active_char['\\'] = MD_CHAR_ESCAPE;-	md->active_char['&'] = MD_CHAR_ENTITITY;--	if (extensions & MKDEXT_AUTOLINK) {-		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)-		md->active_char['^'] = MD_CHAR_SUPERSCRIPT;--	/* Extension data */-	md->ext_flags = extensions;-	md->opaque = opaque;-	md->max_nesting = max_nesting;-	md->in_link_body = 0;--	return md;-}--void-sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md)-{-#define MARKDOWN_GROW(x) ((x) + ((x) >> 1))-	static const char UTF8_BOM[] = {0xEF, 0xBB, 0xBF};--	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, doc_size);--	/* reset the references table */-	memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));--	/* first pass: looking for references, copying everything else */-	beg = 0;--	/* Skip a possible UTF-8 BOM, even though the Unicode standard-	 * discourages having these in UTF-8 documents */-	if (doc_size >= 3 && memcmp(document, UTF8_BOM, 3) == 0)-		beg += 3;--	while (beg < doc_size) /* iterating over lines */-		if (is_ref(document, beg, doc_size, &end, md->refs))-			beg = end;-		else { /* skipping to the next line */-			end = beg;-			while (end < doc_size && document[end] != '\n' && document[end] != '\r')-				end++;--			/* adding the line body if present */-			if (end > beg)-				expand_tabs(text, document + beg, end - beg);--			while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) {-				/* add one \n per newline */-				if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n'))-					bufputc(text, '\n');-				end++;-			}--			beg = end;-		}--	/* pre-grow the output buffer to minimize allocations */-	bufgrow(ob, MARKDOWN_GROW(text->size));--	/* second pass: actual rendering */-	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, md, text->data, text->size);-	}--	if (md->cb.doc_footer)-		md->cb.doc_footer(ob, md->opaque);--	/* clean-up */-	bufrelease(text);-	free_link_refs(md->refs);--	assert(md->work_bufs[BUFFER_SPAN].size == 0);-	assert(md->work_bufs[BUFFER_BLOCK].size == 0);-}--void-sd_markdown_free(struct sd_markdown *md)-{-	size_t 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)md->work_bufs[BUFFER_BLOCK].asize; ++i)-		bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]);--	stack_free(&md->work_bufs[BUFFER_SPAN]);-	stack_free(&md->work_bufs[BUFFER_BLOCK]);--	free(md);-}--void-sd_version(int *ver_major, int *ver_minor, int *ver_revision)-{-	*ver_major = SUNDOWN_VER_MAJOR;-	*ver_minor = SUNDOWN_VER_MINOR;-	*ver_revision = SUNDOWN_VER_REVISION;-}--/* vim: set filetype=c: */
− cbits/markdown.h
@@ -1,138 +0,0 @@-/* markdown.h - generic markdown parser */--/*- * Copyright (c) 2009, 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 UPSKIRT_MARKDOWN_H-#define UPSKIRT_MARKDOWN_H--#include "buffer.h"-#include "autolink.h"--#ifdef __cplusplus-extern "C" {-#endif--#define SUNDOWN_VERSION "1.16.0"-#define SUNDOWN_VER_MAJOR 1-#define SUNDOWN_VER_MINOR 16-#define SUNDOWN_VER_REVISION 0--/********************- * TYPE DEFINITIONS *- ********************/--/* mkd_autolink - type of autolink */-enum mkd_autolink {-	MKDA_NOT_AUTOLINK,	/* used internally when it is not an autolink*/-	MKDA_NORMAL,		/* normal http/http/ftp/mailto/etc link */-	MKDA_EMAIL,			/* e-mail link without explit mailto: */-};--enum mkd_tableflags {-	MKD_TABLE_ALIGN_L = 1,-	MKD_TABLE_ALIGN_R = 2,-	MKD_TABLE_ALIGN_CENTER = 3,-	MKD_TABLE_ALIGNMASK = 3,-	MKD_TABLE_HEADER = 4-};--enum mkd_extensions {-	MKDEXT_NO_INTRA_EMPHASIS = (1 << 0),-	MKDEXT_TABLES = (1 << 1),-	MKDEXT_FENCED_CODE = (1 << 2),-	MKDEXT_AUTOLINK = (1 << 3),-	MKDEXT_STRIKETHROUGH = (1 << 4),-	MKDEXT_SPACE_HEADERS = (1 << 6),-	MKDEXT_SUPERSCRIPT = (1 << 7),-	MKDEXT_LAX_SPACING = (1 << 8),-};--/* sd_callbacks - functions for rendering parsed data */-struct sd_callbacks {-	/* block level callbacks - NULL skips the block */-	void (*blockcode)(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque);-	void (*blockquote)(struct buf *ob, const struct buf *text, void *opaque);-	void (*blockhtml)(struct buf *ob,const  struct buf *text, void *opaque);-	void (*header)(struct buf *ob, const struct buf *text, int level, void *opaque);-	void (*hrule)(struct buf *ob, void *opaque);-	void (*list)(struct buf *ob, const struct buf *text, int flags, void *opaque);-	void (*listitem)(struct buf *ob, const struct buf *text, int flags, void *opaque);-	void (*paragraph)(struct buf *ob, const struct buf *text, void *opaque);-	void (*table)(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque);-	void (*table_row)(struct buf *ob, const struct buf *text, void *opaque);-	void (*table_cell)(struct buf *ob, const struct buf *text, int flags, void *opaque);---	/* span level callbacks - NULL or return 0 prints the span verbatim */-	int (*autolink)(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque);-	int (*codespan)(struct buf *ob, const struct buf *text, void *opaque);-	int (*double_emphasis)(struct buf *ob, const struct buf *text, void *opaque);-	int (*emphasis)(struct buf *ob, const struct buf *text, void *opaque);-	int (*image)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque);-	int (*linebreak)(struct buf *ob, void *opaque);-	int (*link)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque);-	int (*raw_html_tag)(struct buf *ob, const struct buf *tag, void *opaque);-	int (*triple_emphasis)(struct buf *ob, const struct buf *text, void *opaque);-	int (*strikethrough)(struct buf *ob, const struct buf *text, void *opaque);-	int (*superscript)(struct buf *ob, const struct buf *text, void *opaque);--	/* low level callbacks - NULL copies input directly into the output */-	void (*entity)(struct buf *ob, const struct buf *entity, void *opaque);-	void (*normal_text)(struct buf *ob, const struct buf *text, void *opaque);--	/* header and footer */-	void (*doc_header)(struct buf *ob, void *opaque);-	void (*doc_footer)(struct buf *ob, void *opaque);-};--struct sd_markdown;--/*********- * FLAGS *- *********/--/* list/listitem flags */-#define MKD_LIST_ORDERED	1-#define MKD_LI_BLOCK		2  /* <li> containing block data */--/**********************- * EXPORTED FUNCTIONS *- **********************/--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_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md);--extern void-sd_markdown_free(struct sd_markdown *md);--extern void-sd_version(int *major, int *minor, int *revision);--#ifdef __cplusplus-}-#endif--#endif--/* vim: set filetype=c: */
− cbits/stack.c
@@ -1,81 +0,0 @@-#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;--	memset(new_st + st->asize, 0x0,-		(new_size - st->asize) * sizeof(void *));--	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
@@ -1,29 +0,0 @@-#ifndef STACK_H__-#define STACK_H__--#include <stdlib.h>--#ifdef __cplusplus-extern "C" {-#endif--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 *);--#ifdef __cplusplus-}-#endif--#endif
src/Text/Sundown.hs view
@@ -1,29 +1,33 @@ {-| -Bindings to the github fork of the sundown library - previously known as upskirt:-<https://github.com/tanoku/sundown>+Bindings to the github fork of the sundown library - previously known as+upskirt: <https://github.com/tanoku/sundown> +To actually get output, you have to use one of the @Text.Sundown.Html@ modules:++    * "Text.Sundown.Html.String"++    * "Text.Sundown.Html.Text"++    * "Text.Sundown.Html.ByteString"+ Example usage: -> import Text.Sundown-> import Text.Sundown.Renderers.Html-> import qualified Data.ByteString as BS-> import qualified Data.ByteString.UTF8 as UTF8+> import Text.Sundown.Renderers.Html.String > import System (getArgs) > import Control.Monad (liftM) > > main :: IO () > main = do->   input <- liftM (!! 0) getArgs >>= BS.readFile->   putStrLn $ UTF8.toString $ renderHtml input allExtensions noHtmlModes Nothing+>   input <- liftM (!! 0) getArgs >>= readFile+>   putStrLn $ renderHtml input allExtensions noHtmlModes Nothing  -}- module Text.Sundown-       ( module Text.Sundown.Markdown-       ) where--import Text.Sundown.Markdown--+     ( -- * Markdown extensions+       Extensions (..)+     , noExtensions+     , allExtensions+     ) where +import Text.Sundown.Foreign
src/Text/Sundown/Buffer/Foreign.hsc view
@@ -1,15 +1,13 @@ {-# Language ForeignFunctionInterface #-}- module Text.Sundown.Buffer.Foreign-       ( Buffer (..)-       , getBufferData-       , c_bufnew-       , c_bufputs-       , c_bufgrow-       , c_bufrelease-       ) where+    ( Buffer (..)+    , getBufferData+    , bufnew+    , bufrelease+    ) where -import Data.ByteString         (ByteString)+import Control.Applicative+import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Foreign import Foreign.C.String@@ -17,37 +15,29 @@  #include "buffer.h" -data Buffer = Buffer { bufData  :: CString-                     , bufSize  :: CSize-                     , bufASize :: CSize-                     , bufUnit  :: CSize-                     }+data Buffer = Buffer+    { buf_data  :: CString+    , buf_size  :: CSize+    , buf_asize :: CSize+    , buf_unit  :: CSize+    }  instance Storable Buffer where-  sizeOf _ = (#size struct buf)-  alignment _ = alignment (undefined :: CInt)-  peek ptr = do-    d  <- (#peek struct buf, data) ptr-    s  <- (#peek struct buf, size) ptr-    as <- (#peek struct buf, asize) ptr-    u  <- (#peek struct buf, unit) ptr-    return $ Buffer d s as u-  poke _ _ = error "Buffer.poke not implemented."+    sizeOf _    = #{size struct buf}+    alignment _ = alignment (undefined :: CInt)+    peek ptr    = Buffer <$> #{peek struct buf, data} ptr+                         <*> #{peek struct buf, size} ptr+                         <*> #{peek struct buf, asize} ptr+                         <*> #{peek struct buf, unit} ptr+    poke _ _    = error "Buffer.poke not implemented."  getBufferData :: Buffer -> IO ByteString-getBufferData (Buffer d s _ _) | d == nullPtr = return $ BS.pack [0]-                               | otherwise = BS.packCStringLen (d, fromIntegral s)+getBufferData Buffer {buf_data = d, buf_size = s}+    | d == nullPtr = return $ BS.pack [0]+    | otherwise    = BS.packCStringLen (d, fromIntegral s)  foreign import ccall "buffer.h bufnew"-  c_bufnew :: CSize -> IO (Ptr Buffer)--c_bufputs :: Ptr Buffer -> ByteString -> IO ()-c_bufputs buf bs = BS.useAsCString bs $ c_bufputs' buf-foreign import ccall "buffer.h bufputs"-  c_bufputs' :: Ptr Buffer -> CString -> IO ()--foreign import ccall "buffer.h bufgrow"-  c_bufgrow :: Ptr Buffer -> CSize -> IO CInt+    bufnew :: CSize -> IO (Ptr Buffer)  foreign import ccall "buffer.h bufrelease"-  c_bufrelease :: Ptr Buffer -> IO ()+    bufrelease :: Ptr Buffer -> IO ()
src/Text/Sundown/Flag.hs view
@@ -1,14 +1,13 @@ module Text.Sundown.Flag-       ( Flag (..)-       , toCUInt-       ) where+    ( Flag (..)+    , toCUInt+    ) where -import Foreign.C.Types import Data.Bits+import Foreign.C.Types  class Flag a where-  flagIndexes :: a -> [(CUInt, Bool)]+   flagIndexes :: a -> [(CUInt, Bool)]  toCUInt :: Flag a => a -> CUInt-toCUInt flag = foldl (\uint (f, b) -> (if b then f else 0) .|. uint)-                     0 (flagIndexes flag)+toCUInt = foldr (\(f, b) -> ((if b then f else 0) .|.)) 0 . flagIndexes
+ src/Text/Sundown/Foreign.hsc view
@@ -0,0 +1,89 @@+{-# Language ForeignFunctionInterface #-}+module Text.Sundown.Foreign+    ( Extensions (..)+    , noExtensions+    , allExtensions+    , Callbacks+    , sd_markdown_new+    , sd_markdown_render+    , sd_markdown_free+    ) where++import Foreign+import Foreign.C.String+import Foreign.C.Types++import Text.Sundown.Buffer.Foreign+import Text.Sundown.Flag++#include "markdown.h"++-- | A set of switches to enable or disable markdown features.+data Extensions = Extensions+    { extNoIntraEmphasis :: Bool+      -- ^ Turn off underscores insode a word does designating emphasis.+    , extTables          :: Bool+    , extFencedCode      :: Bool+    -- ^ Turns on a non-indentation form of code-blocks, by blocking off a+    --   regionwith ~ or \`.+    , extAutolink        :: Bool+    -- ^ Turn things that look like URLs and email addresses into links+    , extStrikethrough   :: Bool+    -- ^ Surround text with `~` to designate it as struck through+    , extSpaceHeaders    :: Bool+    , extSuperscript     :: Bool+    , extLaxSpacing      :: Bool+    -- ^ Allow blocks inside of paragraphs, instead requireing tags to be on+    --   separate lines+    }++-- | All 'Extensions' disabled+noExtensions :: Extensions+noExtensions = Extensions False False False False False False False False++-- | All 'Extensions' enabled+allExtensions :: Extensions+allExtensions = Extensions True True True True True True True True++instance Flag Extensions where+    flagIndexes exts =+        [ (#{const MKDEXT_NO_INTRA_EMPHASIS}, extNoIntraEmphasis exts)+        , (#{const MKDEXT_TABLES},            extTables exts)+        , (#{const MKDEXT_FENCED_CODE},       extFencedCode exts)+        , (#{const MKDEXT_AUTOLINK},          extAutolink exts)+        , (#{const MKDEXT_STRIKETHROUGH},     extStrikethrough exts)+        , (#{const MKDEXT_SPACE_HEADERS},     extSpaceHeaders exts)+        , (#{const MKDEXT_SUPERSCRIPT},       extSuperscript exts)+        , (#{const MKDEXT_LAX_SPACING},       extLaxSpacing exts)+        ]++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"++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"++sd_markdown_new+    :: Extensions -> CSize -> Ptr Callbacks -> Ptr () -> IO (Ptr Markdown)+sd_markdown_new extensions max_nesting callbacks opaque =+    sd_markdown_new' (toCUInt extensions) max_nesting callbacks opaque+foreign import ccall "markdown.h sd_markdown_new"+    sd_markdown_new'+        :: CUInt -> CSize -> Ptr Callbacks -> Ptr () -> IO (Ptr Markdown)++foreign import ccall "markdown.h sd_markdown_render"+    sd_markdown_render+        :: Ptr Buffer -> CString -> CSize -> Ptr Markdown -> IO ()++foreign import ccall "markdown.h sd_markdown_free"+    sd_markdown_free :: Ptr Markdown -> IO ()
+ src/Text/Sundown/Html/ByteString.hs view
@@ -0,0 +1,92 @@+{-# Language ForeignFunctionInterface #-}+{-|++Warning: you should not need to use this module, if you're storing your text+sanely. Use "Text.Sundown.Html.String" or "Text.Sundown.Html.Text" so that you+won't have to worry about text encoding.++If you really want to use 'ByteString's directly, make sure that they are UTF-8.++-}+module Text.Sundown.Html.ByteString+    ( renderHtml+    , smartypants+      -- * Markdown extensions         +    , Extensions (..)+    , allExtensions+    , noExtensions+      -- * Html render modes         +    , HtmlRenderMode (..)+    , noHtmlModes+    , allHtmlModes+    ) where++import Data.Maybe (fromMaybe)+import Foreign.Marshal+import Foreign.Ptr+import Foreign.Storable+import System.IO.Unsafe++import Data.ByteString (ByteString)+import qualified Data.ByteString.Unsafe as BS++import Text.Sundown.Buffer.Foreign+import Text.Sundown.Foreign+import Text.Sundown.Html.Foreign++defaultMaxNesting :: Int+defaultMaxNesting = 16++-- | Parses a 'ByteString' containing the markdown, returns the Html code.+renderHtml :: ByteString+           -> Extensions+           -> HtmlRenderMode+           -> Bool              -- ^ If true, smartypant the output+           -> Maybe Int+           -- ^ The maximum nesting of the HTML. If Nothing, a default value+           -- (16) will be used.+           -> ByteString+{-# NOINLINE renderHtml #-}+renderHtml input exts mode sp maxNestingM =+    unsafePerformIO $+    alloca $ \callbacks ->+    alloca $ \options ->+    BS.unsafeUseAsCStringLen input $ \(ptr, len) -> do+        ob <- bufnew 64+        sdhtml_renderer callbacks options mode+        let maxNesting = fromIntegral $ fromMaybe defaultMaxNesting maxNestingM+        markdown <- sd_markdown_new exts maxNesting callbacks (castPtr options)+        sd_markdown_render ob ptr (fromIntegral len) markdown+        sd_markdown_free markdown+        res <- if sp then+                   do ob' <- bufnew 64+                      Buffer {buf_data = optr, buf_size = olen} <- peek ob+                      sdhtml_smartypants ob' optr olen+                      bufrelease ob+                      return ob'+               else return ob+        output <- peek res >>= getBufferData+        bufrelease res+        return output++-- | All the 'HtmlRenderMode' disabled+noHtmlModes :: HtmlRenderMode+noHtmlModes = HtmlRenderMode False False False False False False False False+                             False False++-- | All the 'HtmlRenderMode' enabled+allHtmlModes :: HtmlRenderMode+allHtmlModes = HtmlRenderMode True True True True True True True True True True++-- | Converts punctuation in Html entities,+-- <http://daringfireball.net/projects/smartypants/>+smartypants :: ByteString -> ByteString+{-# NOINLINE smartypants #-}+smartypants input =+    unsafePerformIO $+    BS.unsafeUseAsCStringLen input $ \(ptr, len) -> do+        ob <- bufnew 64+        sdhtml_smartypants ob ptr (fromIntegral len)+        output <- peek ob >>= getBufferData+        bufrelease ob+        return output
+ src/Text/Sundown/Html/Foreign.hsc view
@@ -0,0 +1,64 @@+{-# Language ForeignFunctionInterface #-}+module Text.Sundown.Html.Foreign+    ( HtmlRenderMode (..)+    , sdhtml_renderer+    , sdhtml_torenderer+    , sdhtml_smartypants+    ) where++import Foreign+import Foreign.C.String+import Foreign.C.Types++import Text.Sundown.Buffer.Foreign+import Text.Sundown.Flag+import Text.Sundown.Foreign++#include "html.h"++data HtmlRenderMode = HtmlRenderMode+    { htmlSkipHtml   :: Bool -- ^ Drop in-line HTML tags from the output+    , htmlSkipStyle  :: Bool -- ^ Don't add any style tags to the output+    , htmlSkipImages :: Bool -- ^ Don't include images in the output+    , htmlSkipLinks  :: Bool -- ^ Don't include links in the output+    , htmlExpandTabs :: Bool+    , htmlSafelink   :: Bool -- ^ Sanity check links for known URL schemes+    , htmlToc        :: Bool -- ^ Include a table of contents in the output+    , htmlHardWrap   :: Bool+    , htmlUseXhtml   :: Bool -- ^ Produce XHTML output instead of HTML+    , htmlEscape     :: Bool+    }+++instance Flag HtmlRenderMode where+    flagIndexes mode = [ (#{const HTML_SKIP_HTML},   htmlSkipHtml mode)+                       , (#{const HTML_SKIP_STYLE},  htmlSkipStyle mode)+                       , (#{const HTML_SKIP_IMAGES}, htmlSkipImages mode)+                       , (#{const HTML_SKIP_LINKS},  htmlSkipLinks mode)+                       , (#{const HTML_EXPAND_TABS}, htmlExpandTabs mode)+                       , (#{const HTML_SAFELINK},    htmlSafelink mode)+                       , (#{const HTML_TOC},         htmlToc mode)+                       , (#{const HTML_HARD_WRAP},   htmlHardWrap mode)+                       , (#{const HTML_USE_XHTML},   htmlUseXhtml mode)+                       , (#{const HTML_ESCAPE},      htmlEscape mode)+                       ]++data HtmlRenderOptions++instance Storable HtmlRenderOptions where+    sizeOf _    = (#size struct html_renderopt)+    alignment _ = alignment (undefined :: Ptr ())+    peek _      = error "HtmlRenderopt.peek is not implemented"+    poke _      = error "HtmlRenderopt.poke is not implemented"++sdhtml_renderer+    :: Ptr Callbacks -> Ptr HtmlRenderOptions -> HtmlRenderMode -> IO ()+sdhtml_renderer rndr options mode = sdhtml_renderer' rndr options (toCUInt mode)+foreign import ccall "html.h sdhtml_renderer"+    sdhtml_renderer' :: Ptr Callbacks -> Ptr HtmlRenderOptions -> CUInt -> IO ()++foreign import ccall "html.h sdhtml_toc_renderer"+    sdhtml_torenderer :: Ptr Callbacks -> Ptr HtmlRenderOptions -> IO ()++foreign import ccall "html.h sdhtml_smartypants"+    sdhtml_smartypants :: Ptr Buffer -> CString -> CSize -> IO ()
+ src/Text/Sundown/Html/String.hs view
@@ -0,0 +1,37 @@+module Text.Sundown.Html.String+    ( renderHtml+    , smartypants+      -- * Markdown extensions+    , Extensions (..)+    , allExtensions+    , noExtensions+      -- * Html render modes+    , HtmlRenderMode(..)+    , noHtmlModes+    , allHtmlModes+    ) where++import qualified Data.ByteString.UTF8 as BS++import Text.Sundown.Html.ByteString+    (noHtmlModes, allHtmlModes, HtmlRenderMode(..))+import qualified Text.Sundown.Html.ByteString as SundownBS+import Text.Sundown.Foreign++-- | Parses a 'ByteString' containing the markdown, returns the Html code.+renderHtml :: String+           -> Extensions+           -> HtmlRenderMode+           -> Bool              -- ^ If true, smartypant the output           +           -> Maybe Int+           -- ^ The maximum nesting of the HTML. If Nothing, a default value+           -- (16) will be used.+           -> String+renderHtml input exts mode sp maxNestingM = +    BS.toString $ SundownBS.renderHtml (BS.fromString input) exts mode+                                       sp maxNestingM++-- | Converts punctuation in Html entities,+-- <http://daringfireball.net/projects/smartypants/>+smartypants :: String -> String+smartypants = BS.toString . SundownBS.smartypants . BS.fromString
+ src/Text/Sundown/Html/Text.hs view
@@ -0,0 +1,38 @@+module Text.Sundown.Html.Text+    ( renderHtml+    , smartypants+      -- * Markdown extensions         +    , Extensions (..)+    , allExtensions+    , noExtensions+      -- * Html render modes         +    , HtmlRenderMode(..)+    , noHtmlModes+    , allHtmlModes+    ) where++import Data.Text (Text)+import Data.Text.Encoding++import Text.Sundown.Html.ByteString+    (noHtmlModes, allHtmlModes, HtmlRenderMode(..))+import qualified Text.Sundown.Html.ByteString as SundownBS+import Text.Sundown.Foreign++-- | Parses a 'ByteString' containing the markdown, returns the Html code.+renderHtml :: Text+           -> Extensions+           -> HtmlRenderMode+           -> Bool              -- ^ If true, smartypant the output           +           -> Maybe Int+           -- ^ The maximum nesting of the HTML. If Nothing, a default value+           -- (16) will be used.+           -> Text+renderHtml input exts mode sp maxNestingM = +    decodeUtf8 $ SundownBS.renderHtml (encodeUtf8 input) exts mode sp+                                      maxNestingM++-- | Converts punctuation in Html entities,+-- <http://daringfireball.net/projects/smartypants/>+smartypants :: Text -> Text+smartypants = decodeUtf8 . SundownBS.smartypants . encodeUtf8
− src/Text/Sundown/Markdown.hs
@@ -1,15 +0,0 @@-module Text.Sundown.Markdown-       ( Extensions (..)-       , allExtensions-       , noExtensions-       ) where--import Text.Sundown.Markdown.Foreign---- | All 'Extensions' disabled-noExtensions :: Extensions-noExtensions = Extensions False False False False False False False False---- | All 'Extensions' enabled-allExtensions :: Extensions-allExtensions = Extensions True True True True True True True True
− src/Text/Sundown/Markdown/Foreign.hsc
@@ -1,76 +0,0 @@-{-# Language ForeignFunctionInterface #-}--module Text.Sundown.Markdown.Foreign-       ( Callbacks-       , Extensions (..)-       , c_sd_markdown_new-       , c_sd_markdown_render-       , c_sd_markdown_free-       ) where--import Foreign-import Foreign.C.String-import Foreign.C.Types-import Text.Sundown.Buffer.Foreign-import Text.Sundown.Flag--#include "markdown.h"---- | A set of switches to enable or disable markdown features.-data Extensions = Extensions-  { extNoIntraEmphasis :: Bool-    -- ^ Turn off underscores insode a word does designating emphasis.-  , extTables :: Bool-  , extFencedCode :: Bool-    -- ^ Turns on a non-indentation form of code-blocks, by blocking off a-    --   regionwith ~ or \`.-  , extAutolink :: Bool-    -- ^ Turn things that look like URLs and email addresses into links-  , extStrikethrough :: Bool-    -- ^ Surround text with `~` to designate it as struck through-  , extSpaceHeaders :: Bool-  , extSuperscript :: Bool-  , extLaxSpacing :: Bool-    -- ^ Allow blocks inside of paragraphs, instead requireing tags to be on-    --   separate lines-  }--instance Flag Extensions where-  flagIndexes exts = [ (#{const MKDEXT_NO_INTRA_EMPHASIS}, extNoIntraEmphasis exts)-                     , (#{const MKDEXT_TABLES}, extTables exts)-                     , (#{const MKDEXT_FENCED_CODE}, extFencedCode exts)-                     , (#{const MKDEXT_AUTOLINK}, extAutolink exts)-                     , (#{const MKDEXT_STRIKETHROUGH}, extStrikethrough exts)-                     , (#{const MKDEXT_SPACE_HEADERS}, extSpaceHeaders exts)-                     , (#{const MKDEXT_SUPERSCRIPT}, extSuperscript exts)-                     , (#{const MKDEXT_LAX_SPACING}, extLaxSpacing exts)-                     ]---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"--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 -> CString -> CSize -> Ptr Markdown -> IO ()--foreign import ccall "markdown.h sd_markdown_free"-  c_sd_markdown_free :: Ptr Markdown -> IO ()
− src/Text/Sundown/Renderers/Html.hs
@@ -1,94 +0,0 @@-{-# Language ForeignFunctionInterface #-}--module Text.Sundown.Renderers.Html-       ( renderHtml-       , noHtmlModes-       , allHtmlModes-       , smartypants-       , HtmlRenderMode (..)-       ) where--import Foreign.Marshal-import Foreign.Ptr-import Foreign.Storable--import System.IO.Unsafe--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-           -> 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 $ \callbacks ->-  alloca $ \options -> do-    -- Allocate buffers-    ob <- c_bufnew 64-    ib <- c_bufnew . fromIntegral . BS.length $ input--    -- Put the input content into the buffer-    c_bufputs ib input--    -- Do the markdown-    c_sdhtml_renderer callbacks options mode--    let maxNesting = fromIntegral $ fromMaybe defaultMaxNesting maxNestingM-    markdown <- c_sd_markdown_new exts maxNesting callbacks (castPtr options)--    Buffer {bufData = cs, bufSize = size} <- peek ib-    c_sd_markdown_render ob cs size markdown--    c_sd_markdown_free markdown--    -- Get the result-    output <- peek ob >>= getBufferData--    c_bufrelease ib-    c_bufrelease ob--    return output---- | All the 'HtmlRenderMode' disabled-noHtmlModes :: HtmlRenderMode-noHtmlModes = HtmlRenderMode False False False False False False False False False False---- | All the 'HtmlRenderMode' enabled-allHtmlModes :: HtmlRenderMode-allHtmlModes = HtmlRenderMode True True True True True True True True True True---- | Converts punctuation in Html entities,--- <http://daringfireball.net/projects/smartypants/>-{-# NOINLINE smartypants #-}-smartypants :: ByteString -> ByteString-smartypants input =-  unsafePerformIO $ do-    ob <- c_bufnew 64-    ib <- c_bufnew $ fromInteger . toInteger $ BS.length input--    c_bufputs ib input--    Buffer {bufData = cs, bufSize = size} <- peek ib-    c_sdhtml_smartypants ob cs size--    output <- peek ob >>= getBufferData--    c_bufrelease ib-    c_bufrelease ob--    return output
− src/Text/Sundown/Renderers/Html/Foreign.hsc
@@ -1,65 +0,0 @@-{-# Language ForeignFunctionInterface #-}--module Text.Sundown.Renderers.Html.Foreign-       ( HtmlRenderMode (..)-       , c_sdhtml_renderer-       , c_sdhtml_toc_renderer-       , c_sdhtml_smartypants-       ) where--import Foreign-import Foreign.C.String-import Foreign.C.Types-import Text.Sundown.Buffer.Foreign-import Text.Sundown.Flag-import Text.Sundown.Markdown.Foreign--#include "html.h"--data HtmlRenderMode = HtmlRenderMode { htmlSkipHtml :: Bool -- ^ Drop in-line HTML tags from the-                                                           -- output-                                     , htmlSkipStyle :: Bool -- ^ Don't add any style tags to the-                                                            -- output-                                     , htmlSkipImages :: Bool -- ^ Don't include images in the output-                                     , htmlSkipLinks :: Bool -- ^ Don't include links in the output-                                     , htmlExpandTabs :: Bool-                                     , htmlSafelink :: Bool -- ^ Sanity check links for known URL-                                                           -- schemes-                                     , htmlToc :: Bool -- ^ Include a table of contents in the output-                                     , htmlHardWrap :: Bool-                                     , htmlUseXhtml :: Bool -- ^ Produce XHTML output instead of HTML-                                     , htmlEscape :: Bool-                                     }---instance Flag HtmlRenderMode where-  flagIndexes mode = [ (#{const HTML_SKIP_HTML}, htmlSkipHtml mode)-                     , (#{const HTML_SKIP_STYLE}, htmlSkipStyle mode)-                     , (#{const HTML_SKIP_IMAGES}, htmlSkipImages mode)-                     , (#{const HTML_SKIP_LINKS}, htmlSkipLinks mode)-                     , (#{const HTML_EXPAND_TABS}, htmlExpandTabs mode)-                     , (#{const HTML_SAFELINK}, htmlSafelink mode)-                     , (#{const HTML_TOC}, htmlToc mode)-                     , (#{const HTML_HARD_WRAP}, htmlHardWrap mode)-                     , (#{const HTML_USE_XHTML}, htmlUseXhtml mode)-                     , (#{const HTML_ESCAPE}, htmlEscape mode)-                     ]--data HtmlRenderOptions--instance Storable HtmlRenderOptions where-  sizeOf _ = (#size struct html_renderopt)-  alignment _ = alignment (undefined :: Ptr ())-  peek _ = error "HtmlRenderopt.peek is not implemented"-  poke _ = error "HtmlRenderopt.poke is not implemented"--c_sdhtml_renderer :: Ptr Callbacks -> Ptr HtmlRenderOptions -> HtmlRenderMode -> IO ()-c_sdhtml_renderer rndr options mode = c_sdhtml_renderer' rndr options (toCUInt mode)-foreign import ccall "html.h sdhtml_renderer"-  c_sdhtml_renderer' :: Ptr Callbacks -> Ptr HtmlRenderOptions -> CUInt -> IO ()--foreign import ccall "html.h sdhtml_toc_renderer"-  c_sdhtml_toc_renderer :: Ptr Callbacks -> Ptr HtmlRenderOptions -> IO ()--foreign import ccall "html.h sdhtml_smartypants"-  c_sdhtml_smartypants :: Ptr Buffer -> CString -> CSize -> IO ()
+ sundown-c/autolink.c view
@@ -0,0 +1,296 @@+/*+ * Copyright (c) 2011, Vicent Marti+ *+ * 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 "buffer.h"+#include "autolink.h"++#include <string.h>+#include <stdlib.h>+#include <stdio.h>+#include <ctype.h>++#if defined(_WIN32)+#define strncasecmp	_strnicmp+#endif++int+sd_autolink_issafe(const uint8_t *link, size_t link_len)+{+	static const size_t valid_uris_count = 5;+	static const char *valid_uris[] = {+		"/", "http://", "https://", "ftp://", "mailto:"+	};++	size_t i;++	for (i = 0; i < valid_uris_count; ++i) {+		size_t len = strlen(valid_uris[i]);++		if (link_len > len &&+			strncasecmp((char *)link, valid_uris[i], len) == 0 &&+			isalnum(link[len]))+			return 1;+	}++	return 0;+}++static size_t+autolink_delim(uint8_t *data, size_t link_end, size_t offset, size_t size)+{+	uint8_t cclose, copen = 0;+	size_t i;++	for (i = 0; i < link_end; ++i)+		if (data[i] == '<') {+			link_end = i;+			break;+		}++	while (link_end > 0) {+		if (strchr("?!.,", data[link_end - 1]) != NULL)+			link_end--;++		else if (data[link_end - 1] == ';') {+			size_t new_end = link_end - 2;++			while (new_end > 0 && isalpha(data[new_end]))+				new_end--;++			if (new_end < link_end - 2 && data[new_end] == '&')+				link_end = new_end;+			else+				link_end--;+		}+		else break;+	}++	if (link_end == 0)+		return 0;++	cclose = data[link_end - 1];++	switch (cclose) {+	case '"':	copen = '"'; break;+	case '\'':	copen = '\''; break;+	case ')':	copen = '('; break;+	case ']':	copen = '['; break;+	case '}':	copen = '{'; break;+	}++	if (copen != 0) {+		size_t closing = 0;+		size_t opening = 0;+		size_t i = 0;++		/* Try to close the final punctuation sign in this same line;+		 * if we managed to close it outside of the URL, that means that it's+		 * not part of the URL. If it closes inside the URL, that means it+		 * is part of the URL.+		 *+		 * Examples:+		 *+		 *	foo http://www.pokemon.com/Pikachu_(Electric) bar+		 *		=> http://www.pokemon.com/Pikachu_(Electric)+		 *+		 *	foo (http://www.pokemon.com/Pikachu_(Electric)) bar+		 *		=> http://www.pokemon.com/Pikachu_(Electric)+		 *+		 *	foo http://www.pokemon.com/Pikachu_(Electric)) bar+		 *		=> http://www.pokemon.com/Pikachu_(Electric))+		 *+		 *	(foo http://www.pokemon.com/Pikachu_(Electric)) bar+		 *		=> foo http://www.pokemon.com/Pikachu_(Electric)+		 */++		while (i < link_end) {+			if (data[i] == copen)+				opening++;+			else if (data[i] == cclose)+				closing++;++			i++;+		}++		if (closing != opening)+			link_end--;+	}++	return link_end;+}++static size_t+check_domain(uint8_t *data, size_t size, int allow_short)+{+	size_t i, np = 0;++	if (!isalnum(data[0]))+		return 0;++	for (i = 1; i < size - 1; ++i) {+		if (data[i] == '.') np++;+		else if (!isalnum(data[i]) && data[i] != '-') break;+	}++	if (allow_short) {+		/* We don't need a valid domain in the strict sense (with+		 * least one dot; so just make sure it's composed of valid+		 * domain characters and return the length of the the valid+		 * sequence. */+		return i;+	} else {+		/* a valid domain needs to have at least a dot.+		 * that's as far as we get */+		return np ? i : 0;+	}+}++size_t+sd_autolink__www(+	size_t *rewind_p,+	struct buf *link,+	uint8_t *data,+	size_t offset,+	size_t size,+	unsigned int flags)+{+	size_t link_end;++	if (offset > 0 && !ispunct(data[-1]) && !isspace(data[-1]))+		return 0;++	if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)+		return 0;++	link_end = check_domain(data, size, 0);++	if (link_end == 0)+		return 0;++	while (link_end < size && !isspace(data[link_end]))+		link_end++;++	link_end = autolink_delim(data, link_end, offset, size);++	if (link_end == 0)+		return 0;++	bufput(link, data, link_end);+	*rewind_p = 0;++	return (int)link_end;+}++size_t+sd_autolink__email(+	size_t *rewind_p,+	struct buf *link,+	uint8_t *data,+	size_t offset,+	size_t size,+	unsigned int flags)+{+	size_t link_end, rewind;+	int nb = 0, np = 0;++	for (rewind = 0; rewind < offset; ++rewind) {+		uint8_t c = data[-rewind - 1];++		if (isalnum(c))+			continue;++		if (strchr(".+-_", c) != NULL)+			continue;++		break;+	}++	if (rewind == 0)+		return 0;++	for (link_end = 0; link_end < size; ++link_end) {+		uint8_t c = data[link_end];++		if (isalnum(c))+			continue;++		if (c == '@')+			nb++;+		else if (c == '.' && link_end < size - 1)+			np++;+		else if (c != '-' && c != '_')+			break;+	}++	if (link_end < 2 || nb != 1 || np == 0)+		return 0;++	link_end = autolink_delim(data, link_end, offset, size);++	if (link_end == 0)+		return 0;++	bufput(link, data - rewind, link_end + rewind);+	*rewind_p = rewind;++	return link_end;+}++size_t+sd_autolink__url(+	size_t *rewind_p,+	struct buf *link,+	uint8_t *data,+	size_t offset,+	size_t size,+	unsigned int flags)+{+	size_t link_end, rewind = 0, domain_len;++	if (size < 4 || data[1] != '/' || data[2] != '/')+		return 0;++	while (rewind < offset && isalpha(data[-rewind - 1]))+		rewind++;++	if (!sd_autolink_issafe(data - rewind, size + rewind))+		return 0;++	link_end = strlen("://");++	domain_len = check_domain(+		data + link_end,+		size - link_end,+		flags & SD_AUTOLINK_SHORT_DOMAINS);++	if (domain_len == 0)+		return 0;++	link_end += domain_len;+	while (link_end < size && !isspace(data[link_end]))+		link_end++;++	link_end = autolink_delim(data, link_end, offset, size);++	if (link_end == 0)+		return 0;++	bufput(link, data - rewind, link_end + rewind);+	*rewind_p = rewind;++	return link_end;+}+
+ sundown-c/autolink.h view
@@ -0,0 +1,51 @@+/*+ * Copyright (c) 2011, Vicent Marti+ *+ * 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 UPSKIRT_AUTOLINK_H+#define UPSKIRT_AUTOLINK_H++#include "buffer.h"++#ifdef __cplusplus+extern "C" {+#endif++enum {+	SD_AUTOLINK_SHORT_DOMAINS = (1 << 0),+};++int+sd_autolink_issafe(const uint8_t *link, size_t link_len);++size_t+sd_autolink__www(size_t *rewind_p, struct buf *link,+	uint8_t *data, size_t offset, size_t size, unsigned int flags);++size_t+sd_autolink__email(size_t *rewind_p, struct buf *link,+	uint8_t *data, size_t offset, size_t size, unsigned int flags);++size_t+sd_autolink__url(size_t *rewind_p, struct buf *link,+	uint8_t *data, size_t offset, size_t size, unsigned int flags);++#ifdef __cplusplus+}+#endif++#endif++/* vim: set filetype=c: */
+ sundown-c/buffer.c view
@@ -0,0 +1,225 @@+/*+ * 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+ * 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.+ */++#define BUFFER_MAX_ALLOC_SIZE (1024 * 1024 * 16) //16mb++#include "buffer.h"++#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <assert.h>++/* MSVC compat */+#if defined(_MSC_VER)+#	define _buf_vsnprintf _vsnprintf+#else+#	define _buf_vsnprintf vsnprintf+#endif++int+bufprefix(const struct buf *buf, const char *prefix)+{+	size_t i;+	assert(buf && buf->unit);++	for (i = 0; i < buf->size; ++i) {+		if (prefix[i] == 0)+			return 0;++		if (buf->data[i] != prefix[i])+			return buf->data[i] - prefix[i];+	}++	return 0;+}++/* bufgrow: increasing the allocated size to the given value */+int+bufgrow(struct buf *buf, size_t neosz)+{+	size_t neoasz;+	void *neodata;++	assert(buf && buf->unit);++	if (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;++	neodata = realloc(buf->data, neoasz);+	if (!neodata)+		return BUF_ENOMEM;++	buf->data = neodata;+	buf->asize = neoasz;+	return BUF_OK;+}+++/* bufnew: allocation of a new buffer */+struct buf *+bufnew(size_t unit)+{+	struct buf *ret;+	ret = malloc(sizeof (struct buf));++	if (ret) {+		ret->data = 0;+		ret->size = ret->asize = 0;+		ret->unit = unit;+	}+	return ret;+}++/* bufnullterm: NULL-termination of the string array */+const char *+bufcstr(struct buf *buf)+{+	assert(buf && buf->unit);++	if (buf->size < buf->asize && buf->data[buf->size] == 0)+		return (char *)buf->data;++	if (buf->size + 1 <= buf->asize || bufgrow(buf, buf->size + 1) == 0) {+		buf->data[buf->size] = 0;+		return (char *)buf->data;+	}++	return NULL;+}++/* bufprintf: formatted printing to a buffer */+void+bufprintf(struct buf *buf, const char *fmt, ...)+{+	va_list ap;+	int n;++	assert(buf && buf->unit);++	if (buf->size >= buf->asize && bufgrow(buf, buf->size + 1) < 0)+		return;+	+	va_start(ap, fmt);+	n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);+	va_end(ap);++	if (n < 0) {+#ifdef _MSC_VER+		va_start(ap, fmt);+		n = _vscprintf(fmt, ap);+		va_end(ap);+#else+		return;+#endif+	}++	if ((size_t)n >= buf->asize - buf->size) {+		if (bufgrow(buf, buf->size + n + 1) < 0)+			return;++		va_start(ap, fmt);+		n = _buf_vsnprintf((char *)buf->data + buf->size, buf->asize - buf->size, fmt, ap);+		va_end(ap);+	}++	if (n < 0)+		return;++	buf->size += n;+}++/* bufput: appends raw data to a buffer */+void+bufput(struct buf *buf, const void *data, size_t len)+{+	assert(buf && buf->unit);++	if (buf->size + len > buf->asize && bufgrow(buf, buf->size + len) < 0)+		return;++	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));+}+++/* bufputc: appends a single uint8_t to a buffer */+void+bufputc(struct buf *buf, int c)+{+	assert(buf && buf->unit);++	if (buf->size + 1 > buf->asize && bufgrow(buf, buf->size + 1) < 0)+		return;++	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;++	free(buf->data);+	free(buf);+}+++/* bufreset: frees internal data of the buffer */+void+bufreset(struct buf *buf)+{+	if (!buf)+		return;++	free(buf->data);+	buf->data = NULL;+	buf->size = buf->asize = 0;+}++/* bufslurp: removes a given number of bytes from the head of the array */+void+bufslurp(struct buf *buf, size_t len)+{+	assert(buf && buf->unit);++	if (len >= buf->size) {+		buf->size = 0;+		return;+	}++	buf->size -= len;+	memmove(buf->data, buf->data + len, buf->size);+}+
+ sundown-c/buffer.h view
@@ -0,0 +1,96 @@+/*+ * 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+ * 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 BUFFER_H__+#define BUFFER_H__++#include <stddef.h>+#include <stdarg.h>+#include <stdint.h>++#ifdef __cplusplus+extern "C" {+#endif++#if defined(_MSC_VER)+#define __attribute__(x)+#define inline+#endif++typedef enum {+	BUF_OK = 0,+	BUF_ENOMEM = -1,+} buferror_t;++/* struct buf: character array buffer */+struct buf {+	uint8_t *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) */+};++/* CONST_BUF: global buffer from a string litteral */+#define BUF_STATIC(string) \+	{ (uint8_t *)string, sizeof string -1, sizeof string, 0, 0 }++/* VOLATILE_BUF: macro for creating a volatile buffer on the stack */+#define BUF_VOLATILE(strname) \+	{ (uint8_t *)strname, strlen(strname), 0, 0, 0 }++/* BUFPUTSL: optimized bufputs of a string litteral */+#define BUFPUTSL(output, literal) \+	bufput(output, literal, sizeof literal - 1)++/* 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) */+const char *bufcstr(struct buf *);++/* bufprefix: compare the beginning of a buffer with a string */+int bufprefix(const struct buf *buf, const char *prefix);++/* bufput: appends raw data to a buffer */+void bufput(struct buf *, const void *, 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 *, int);++/* bufrelease: decrease the reference count and free the buffer if needed */+void bufrelease(struct buf *);++/* bufreset: frees internal data of the buffer */+void bufreset(struct buf *);++/* bufslurp: removes a given number of bytes from the head of the array */+void bufslurp(struct buf *, size_t);++/* bufprintf: formatted printing to a buffer */+void bufprintf(struct buf *, const char *, ...) __attribute__ ((format (printf, 2, 3)));++#ifdef __cplusplus+}+#endif++#endif
+ sundown-c/houdini.h view
@@ -0,0 +1,29 @@+#ifndef HOUDINI_H__+#define HOUDINI_H__++#include "buffer.h"++#ifdef HOUDINI_USE_LOCALE+#	define _isxdigit(c) isxdigit(c)+#	define _isdigit(c) isdigit(c)+#else+/*+ * Helper _isdigit methods -- do not trust the current locale+ * */+#	define _isxdigit(c) (strchr("0123456789ABCDEFabcdef", (c)) != NULL)+#	define _isdigit(c) ((c) >= '0' && (c) <= '9')+#endif++extern void houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure);+extern void houdini_unescape_html(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_escape_xml(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_escape_uri(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_escape_url(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_unescape_uri(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_unescape_url(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_escape_js(struct buf *ob, const uint8_t *src, size_t size);+extern void houdini_unescape_js(struct buf *ob, const uint8_t *src, size_t size);++#endif
+ sundown-c/houdini_href_e.c view
@@ -0,0 +1,108 @@+#include <assert.h>+#include <stdio.h>+#include <string.h>++#include "houdini.h"++#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10)++/*+ * The following characters will not be escaped:+ *+ *		-_.+!*'(),%#@?=;:/,+&$ alphanum+ *+ * Note that this character set is the addition of:+ *+ *	- The characters which are safe to be in an URL+ *	- The characters which are *not* safe to be in+ *	an URL because they are RESERVED characters.+ *+ * We asume (lazily) that any RESERVED char that+ * appears inside an URL is actually meant to+ * have its native function (i.e. as an URL + * component/separator) and hence needs no escaping.+ *+ * There are two exceptions: the chacters & (amp)+ * and ' (single quote) do not appear in the table.+ * They are meant to appear in the URL as components,+ * yet they require special HTML-entity escaping+ * to generate valid HTML markup.+ *+ * All other characters will be escaped to %XX.+ *+ */+static const char HREF_SAFE[] = {+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, +	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, +	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, +	0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, +	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+};++void+houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size)+{+	static const char hex_chars[] = "0123456789ABCDEF";+	size_t  i = 0, org;+	char hex_str[3];++	bufgrow(ob, ESCAPE_GROW_FACTOR(size));+	hex_str[0] = '%';++	while (i < size) {+		org = i;+		while (i < size && HREF_SAFE[src[i]] != 0)+			i++;++		if (i > org)+			bufput(ob, src + org, i - org);++		/* escaping */+		if (i >= size)+			break;++		switch (src[i]) {+		/* amp appears all the time in URLs, but needs+		 * HTML-entity escaping to be inside an href */+		case '&': +			BUFPUTSL(ob, "&amp;");+			break;++		/* the single quote is a valid URL character+		 * according to the standard; it needs HTML+		 * entity escaping too */+		case '\'':+			BUFPUTSL(ob, "&#x27;");+			break;+		+		/* the space can be escaped to %20 or a plus+		 * sign. we're going with the generic escape+		 * for now. the plus thing is more commonly seen+		 * when building GET strings */+#if 0+		case ' ':+			bufputc(ob, '+');+			break;+#endif++		/* every other character goes with a %XX escaping */+		default:+			hex_str[1] = hex_chars[(src[i] >> 4) & 0xF];+			hex_str[2] = hex_chars[src[i] & 0xF];+			bufput(ob, hex_str, 3);+		}++		i++;+	}+}
+ sundown-c/houdini_html_e.c view
@@ -0,0 +1,84 @@+#include <assert.h>+#include <stdio.h>+#include <string.h>++#include "houdini.h"++#define ESCAPE_GROW_FACTOR(x) (((x) * 12) / 10) /* this is very scientific, yes */++/**+ * According to the OWASP rules:+ *+ * & --> &amp;+ * < --> &lt;+ * > --> &gt;+ * " --> &quot;+ * ' --> &#x27;     &apos; is not recommended+ * / --> &#x2F;     forward slash is included as it helps end an HTML entity+ *+ */+static const char HTML_ESCAPE_TABLE[] = {+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 1, 0, 0, 0, 2, 3, 0, 0, 0, 0, 0, 0, 0, 4, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 6, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+};++static const char *HTML_ESCAPES[] = {+        "",+        "&quot;",+        "&amp;",+        "&#39;",+        "&#47;",+        "&lt;",+        "&gt;"+};++void+houdini_escape_html0(struct buf *ob, const uint8_t *src, size_t size, int secure)+{+	size_t i = 0, org, esc = 0;++	bufgrow(ob, ESCAPE_GROW_FACTOR(size));++	while (i < size) {+		org = i;+		while (i < size && (esc = HTML_ESCAPE_TABLE[src[i]]) == 0)+			i++;++		if (i > org)+			bufput(ob, src + org, i - org);++		/* escaping */+		if (i >= size)+			break;++		/* The forward slash is only escaped in secure mode */+		if (src[i] == '/' && !secure) {+			bufputc(ob, '/');+		} else {+			bufputs(ob, HTML_ESCAPES[esc]);+		}++		i++;+	}+}++void+houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size)+{+	houdini_escape_html0(ob, src, size, 1);+}+
+ sundown-c/html.c view
@@ -0,0 +1,620 @@+/*+ * Copyright (c) 2009, Natacha Porté+ * Copyright (c) 2011, Vicent Marti+ *+ * 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 "markdown.h"+#include "html.h"++#include <string.h>+#include <stdlib.h>+#include <stdio.h>+#include <ctype.h>++#include "houdini.h"++#define USE_XHTML(opt) (opt->flags & HTML_USE_XHTML)++int+sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname)+{+	size_t i;+	int closed = 0;++	if (tag_size < 3 || tag_data[0] != '<')+		return HTML_TAG_NONE;++	i = 1;++	if (tag_data[i] == '/') {+		closed = 1;+		i++;+	}++	for (; i < tag_size; ++i, ++tagname) {+		if (*tagname == 0)+			break;++		if (tag_data[i] != *tagname)+			return HTML_TAG_NONE;+	}++	if (i == tag_size)+		return HTML_TAG_NONE;++	if (isspace(tag_data[i]) || tag_data[i] == '>')+		return closed ? HTML_TAG_CLOSE : HTML_TAG_OPEN;++	return HTML_TAG_NONE;+}++static inline void escape_html(struct buf *ob, const uint8_t *source, size_t length)+{+	houdini_escape_html0(ob, source, length, 0);+}++static inline void escape_href(struct buf *ob, const uint8_t *source, size_t length)+{+	houdini_escape_href(ob, source, length);+}++/********************+ * GENERIC RENDERER *+ ********************/+static int+rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque)+{+	struct html_renderopt *options = opaque;++	if (!link || !link->size)+		return 0;++	if ((options->flags & HTML_SAFELINK) != 0 &&+		!sd_autolink_issafe(link->data, link->size) &&+		type != MKDA_EMAIL)+		return 0;++	BUFPUTSL(ob, "<a href=\"");+	if (type == MKDA_EMAIL)+		BUFPUTSL(ob, "mailto:");+	escape_href(ob, link->data, link->size);++	if (options->link_attributes) {+		bufputc(ob, '\"');+		options->link_attributes(ob, link, opaque);+		bufputc(ob, '>');+	} else {+		BUFPUTSL(ob, "\">");+	}++	/*+	 * Pretty printing: if we get an email address as+	 * an actual URI, e.g. `mailto:foo@bar.com`, we don't+	 * want to print the `mailto:` prefix+	 */+	if (bufprefix(link, "mailto:") == 0) {+		escape_html(ob, link->data + 7, link->size - 7);+	} else {+		escape_html(ob, link->data, link->size);+	}++	BUFPUTSL(ob, "</a>");++	return 1;+}++static void+rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque)+{+	if (ob->size) bufputc(ob, '\n');++	if (lang && lang->size) {+		size_t i, cls;+		BUFPUTSL(ob, "<pre><code class=\"");++		for (i = 0, cls = 0; i < lang->size; ++i, ++cls) {+			while (i < lang->size && isspace(lang->data[i]))+				i++;++			if (i < lang->size) {+				size_t org = i;+				while (i < lang->size && !isspace(lang->data[i]))+					i++;++				if (lang->data[org] == '.')+					org++;++				if (cls) bufputc(ob, ' ');+				escape_html(ob, lang->data + org, i - org);+			}+		}++		BUFPUTSL(ob, "\">");+	} else+		BUFPUTSL(ob, "<pre><code>");++	if (text)+		escape_html(ob, text->data, text->size);++	BUFPUTSL(ob, "</code></pre>\n");+}++static void+rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque)+{+	if (ob->size) bufputc(ob, '\n');+	BUFPUTSL(ob, "<blockquote>\n");+	if (text) bufput(ob, text->data, text->size);+	BUFPUTSL(ob, "</blockquote>\n");+}++static int+rndr_codespan(struct buf *ob, const struct buf *text, void *opaque)+{+	BUFPUTSL(ob, "<code>");+	if (text) escape_html(ob, text->data, text->size);+	BUFPUTSL(ob, "</code>");+	return 1;+}++static int+rndr_strikethrough(struct buf *ob, const struct buf *text, void *opaque)+{+	if (!text || !text->size)+		return 0;++	BUFPUTSL(ob, "<del>");+	bufput(ob, text->data, text->size);+	BUFPUTSL(ob, "</del>");+	return 1;+}++static int+rndr_double_emphasis(struct buf *ob, const struct buf *text, void *opaque)+{+	if (!text || !text->size)+		return 0;++	BUFPUTSL(ob, "<strong>");+	bufput(ob, text->data, text->size);+	BUFPUTSL(ob, "</strong>");++	return 1;+}++static int+rndr_emphasis(struct buf *ob, const struct buf *text, void *opaque)+{+	if (!text || !text->size) return 0;+	BUFPUTSL(ob, "<em>");+	if (text) bufput(ob, text->data, text->size);+	BUFPUTSL(ob, "</em>");+	return 1;+}++static int+rndr_linebreak(struct buf *ob, void *opaque)+{+	struct html_renderopt *options = opaque;+	bufputs(ob, USE_XHTML(options) ? "<br/>\n" : "<br>\n");+	return 1;+}++static void+rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)+{+	struct html_renderopt *options = opaque;++	if (ob->size)+		bufputc(ob, '\n');++	if (options->flags & HTML_TOC)+		bufprintf(ob, "<h%d id=\"toc_%d\">", level, options->toc_data.header_count++);+	else+		bufprintf(ob, "<h%d>", level);++	if (text) bufput(ob, text->data, text->size);+	bufprintf(ob, "</h%d>\n", level);+}++static int+rndr_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)+{+	struct html_renderopt *options = opaque;++	if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))+		return 0;++	BUFPUTSL(ob, "<a href=\"");++	if (link && link->size)+		escape_href(ob, link->data, link->size);++	if (title && title->size) {+		BUFPUTSL(ob, "\" title=\"");+		escape_html(ob, title->data, title->size);+	}++	if (options->link_attributes) {+		bufputc(ob, '\"');+		options->link_attributes(ob, link, opaque);+		bufputc(ob, '>');+	} else {+		BUFPUTSL(ob, "\">");+	}++	if (content && content->size) bufput(ob, content->data, content->size);+	BUFPUTSL(ob, "</a>");+	return 1;+}++static void+rndr_list(struct buf *ob, const struct buf *text, int flags, void *opaque)+{+	if (ob->size) bufputc(ob, '\n');+	bufput(ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);+	if (text) bufput(ob, text->data, text->size);+	bufput(ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);+}++static void+rndr_listitem(struct buf *ob, const struct buf *text, int flags, void *opaque)+{+	BUFPUTSL(ob, "<li>");+	if (text) {+		size_t size = text->size;+		while (size && text->data[size - 1] == '\n')+			size--;++		bufput(ob, text->data, size);+	}+	BUFPUTSL(ob, "</li>\n");+}++static void+rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)+{+	struct html_renderopt *options = opaque;+	size_t i = 0;++	if (ob->size) bufputc(ob, '\n');++	if (!text || !text->size)+		return;++	while (i < text->size && isspace(text->data[i])) i++;++	if (i == text->size)+		return;++	BUFPUTSL(ob, "<p>");+	if (options->flags & HTML_HARD_WRAP) {+		size_t org;+		while (i < text->size) {+			org = i;+			while (i < text->size && text->data[i] != '\n')+				i++;++			if (i > org)+				bufput(ob, text->data + org, i - org);++			/*+			 * do not insert a line break if this newline+			 * is the last character on the paragraph+			 */+			if (i >= text->size - 1)+				break;++			rndr_linebreak(ob, opaque);+			i++;+		}+	} else {+		bufput(ob, &text->data[i], text->size - i);+	}+	BUFPUTSL(ob, "</p>\n");+}++static void+rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)+{+	size_t org, sz;+	if (!text) return;+	sz = text->size;+	while (sz > 0 && text->data[sz - 1] == '\n') sz--;+	org = 0;+	while (org < sz && text->data[org] == '\n') org++;+	if (org >= sz) return;+	if (ob->size) bufputc(ob, '\n');+	bufput(ob, text->data + org, sz - org);+	bufputc(ob, '\n');+}++static int+rndr_triple_emphasis(struct buf *ob, const struct buf *text, void *opaque)+{+	if (!text || !text->size) return 0;+	BUFPUTSL(ob, "<strong><em>");+	bufput(ob, text->data, text->size);+	BUFPUTSL(ob, "</em></strong>");+	return 1;+}++static void+rndr_hrule(struct buf *ob, void *opaque)+{+	struct html_renderopt *options = opaque;+	if (ob->size) bufputc(ob, '\n');+	bufputs(ob, USE_XHTML(options) ? "<hr/>\n" : "<hr>\n");+}++static int+rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)+{+	struct html_renderopt *options = opaque;+	if (!link || !link->size) return 0;++	BUFPUTSL(ob, "<img src=\"");+	escape_href(ob, link->data, link->size);+	BUFPUTSL(ob, "\" alt=\"");++	if (alt && alt->size)+		escape_html(ob, alt->data, alt->size);++	if (title && title->size) {+		BUFPUTSL(ob, "\" title=\"");+		escape_html(ob, title->data, title->size); }++	bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");+	return 1;+}++static int+rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)+{+	struct html_renderopt *options = opaque;++	/* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES+	* It doens't see if there are any valid tags, just escape all of them. */+	if((options->flags & HTML_ESCAPE) != 0) {+		escape_html(ob, text->data, text->size);+		return 1;+	}++	if ((options->flags & HTML_SKIP_HTML) != 0)+		return 1;++	if ((options->flags & HTML_SKIP_STYLE) != 0 &&+		sdhtml_is_tag(text->data, text->size, "style"))+		return 1;++	if ((options->flags & HTML_SKIP_LINKS) != 0 &&+		sdhtml_is_tag(text->data, text->size, "a"))+		return 1;++	if ((options->flags & HTML_SKIP_IMAGES) != 0 &&+		sdhtml_is_tag(text->data, text->size, "img"))+		return 1;++	bufput(ob, text->data, text->size);+	return 1;+}++static void+rndr_table(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque)+{+	if (ob->size) bufputc(ob, '\n');+	BUFPUTSL(ob, "<table><thead>\n");+	if (header)+		bufput(ob, header->data, header->size);+	BUFPUTSL(ob, "</thead><tbody>\n");+	if (body)+		bufput(ob, body->data, body->size);+	BUFPUTSL(ob, "</tbody></table>\n");+}++static void+rndr_tablerow(struct buf *ob, const struct buf *text, void *opaque)+{+	BUFPUTSL(ob, "<tr>\n");+	if (text)+		bufput(ob, text->data, text->size);+	BUFPUTSL(ob, "</tr>\n");+}++static void+rndr_tablecell(struct buf *ob, const struct buf *text, int flags, void *opaque)+{+	if (flags & MKD_TABLE_HEADER) {+		BUFPUTSL(ob, "<th");+	} else {+		BUFPUTSL(ob, "<td");+	}++	switch (flags & MKD_TABLE_ALIGNMASK) {+	case MKD_TABLE_ALIGN_CENTER:+		BUFPUTSL(ob, " align=\"center\">");+		break;++	case MKD_TABLE_ALIGN_L:+		BUFPUTSL(ob, " align=\"left\">");+		break;++	case MKD_TABLE_ALIGN_R:+		BUFPUTSL(ob, " align=\"right\">");+		break;++	default:+		BUFPUTSL(ob, ">");+	}++	if (text)+		bufput(ob, text->data, text->size);++	if (flags & MKD_TABLE_HEADER) {+		BUFPUTSL(ob, "</th>\n");+	} else {+		BUFPUTSL(ob, "</td>\n");+	}+}++static int+rndr_superscript(struct buf *ob, const struct buf *text, void *opaque)+{+	if (!text || !text->size) return 0;+	BUFPUTSL(ob, "<sup>");+	bufput(ob, text->data, text->size);+	BUFPUTSL(ob, "</sup>");+	return 1;+}++static void+rndr_normal_text(struct buf *ob, const struct buf *text, void *opaque)+{+	if (text)+		escape_html(ob, text->data, text->size);+}++static void+toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)+{+	struct html_renderopt *options = opaque;++	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, "<a href=\"#toc_%d\">", options->toc_data.header_count++);+	if (text)+		bufput(ob, text->data, text->size);+	BUFPUTSL(ob, "</a>\n");+}++static void+toc_finalize(struct buf *ob, void *opaque)+{+	struct html_renderopt *options = opaque;++	while (options->toc_data.current_level > 0) {+		BUFPUTSL(ob, "</li>\n</ul>\n");+		options->toc_data.current_level--;+	}+}++void+sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options)+{+	static const struct sd_callbacks cb_default = {+		NULL,+		NULL,+		NULL,+		toc_header,+		NULL,+		NULL,+		NULL,+		NULL,+		NULL,+		NULL,+		NULL,++		NULL,+		rndr_codespan,+		rndr_double_emphasis,+		rndr_emphasis,+		NULL,+		NULL,+		NULL,+		NULL,+		rndr_triple_emphasis,+		rndr_strikethrough,+		rndr_superscript,++		NULL,+		NULL,++		NULL,+		toc_finalize,+	};++	memset(options, 0x0, sizeof(struct html_renderopt));+	options->flags = HTML_TOC;++	memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));+}++void+sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)+{+	static const struct sd_callbacks cb_default = {+		rndr_blockcode,+		rndr_blockquote,+		rndr_raw_block,+		rndr_header,+		rndr_hrule,+		rndr_list,+		rndr_listitem,+		rndr_paragraph,+		rndr_table,+		rndr_tablerow,+		rndr_tablecell,++		rndr_autolink,+		rndr_codespan,+		rndr_double_emphasis,+		rndr_emphasis,+		rndr_image,+		rndr_linebreak,+		rndr_link,+		rndr_raw_html,+		rndr_triple_emphasis,+		rndr_strikethrough,+		rndr_superscript,++		NULL,+		rndr_normal_text,++		NULL,+		NULL,+	};++	/* Prepare the options pointer */+	memset(options, 0x0, sizeof(struct html_renderopt));+	options->flags = render_flags;++	/* Prepare the callbacks */+	memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));++	if (render_flags & HTML_SKIP_IMAGES)+		callbacks->image = NULL;++	if (render_flags & HTML_SKIP_LINKS) {+		callbacks->link = NULL;+		callbacks->autolink = NULL;+	}++	if (render_flags & HTML_SKIP_HTML || render_flags & HTML_ESCAPE)+		callbacks->blockhtml = NULL;+}
+ sundown-c/html.h view
@@ -0,0 +1,68 @@+/*+ * Copyright (c) 2011, Vicent Marti+ *+ * 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 UPSKIRT_HTML_H+#define UPSKIRT_HTML_H++#include "markdown.h"+#include "buffer.h"+#include <stdlib.h>++struct html_renderopt {+	struct {+		int header_count;+		int current_level;+	} toc_data;++	unsigned int flags;++	/* extra callbacks */+	void (*link_attributes)(struct buf *ob, const struct buf *url, void *self);+};++typedef enum {+	HTML_SKIP_HTML = (1 << 0),+	HTML_SKIP_STYLE = (1 << 1),+	HTML_SKIP_IMAGES = (1 << 2),+	HTML_SKIP_LINKS = (1 << 3),+	HTML_EXPAND_TABS = (1 << 4),+	HTML_SAFELINK = (1 << 5),+	HTML_TOC = (1 << 6),+	HTML_HARD_WRAP = (1 << 7),+	HTML_USE_XHTML = (1 << 8),+	HTML_ESCAPE = (1 << 9),+} html_render_mode;++typedef enum {+	HTML_TAG_NONE = 0,+	HTML_TAG_OPEN,+	HTML_TAG_CLOSE,+} html_tag;++int+sdhtml_is_tag(const uint8_t *tag_data, size_t tag_size, const char *tagname);++extern void+sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr, unsigned int render_flags);++extern void+sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options_ptr);++extern void+sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size);++#endif+
+ sundown-c/html_blocks.h view
@@ -0,0 +1,206 @@+/* 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 = 24,+      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",+      "",+      "style",+      "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;+}
+ sundown-c/html_smartypants.c view
@@ -0,0 +1,385 @@+/*+ * Copyright (c) 2011, Vicent Marti+ *+ * 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 "buffer.h"+#include "html.h"++#include <string.h>+#include <stdlib.h>+#include <stdio.h>+#include <ctype.h>++struct smartypants_data {+	int in_squote;+	int in_dquote;+};++static size_t smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);+static size_t smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size);++static size_t (*smartypants_cb_ptrs[])+	(struct buf *, struct smartypants_data *, uint8_t, const uint8_t *, size_t) =+{+	NULL,					/* 0 */+	smartypants_cb__dash,	/* 1 */+	smartypants_cb__parens,	/* 2 */+	smartypants_cb__squote, /* 3 */+	smartypants_cb__dquote, /* 4 */+	smartypants_cb__amp,	/* 5 */+	smartypants_cb__period,	/* 6 */+	smartypants_cb__number,	/* 7 */+	smartypants_cb__ltag,	/* 8 */+	smartypants_cb__backtick, /* 9 */+	smartypants_cb__escape, /* 10 */+};++static const uint8_t smartypants_cb_chars[] = {+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 4, 0, 0, 0, 5, 3, 2, 0, 0, 0, 0, 1, 6, 0,+	0, 7, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0,+	9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,+};++static inline int+word_boundary(uint8_t c)+{+	return c == 0 || isspace(c) || ispunct(c);+}++static int+smartypants_quotes(struct buf *ob, uint8_t previous_char, uint8_t next_char, uint8_t quote, int *is_open)+{+	char ent[8];++	if (*is_open && !word_boundary(next_char))+		return 0;++	if (!(*is_open) && !word_boundary(previous_char))+		return 0;++	snprintf(ent, sizeof(ent), "&%c%cquo;", (*is_open) ? 'r' : 'l', quote);+	*is_open = !(*is_open);+	bufputs(ob, ent);+	return 1;+}++static size_t+smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (size >= 2) {+		uint8_t t1 = tolower(text[1]);++		if (t1 == '\'') {+			if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote))+				return 1;+		}++		if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') &&+			(size == 3 || word_boundary(text[2]))) {+			BUFPUTSL(ob, "&rsquo;");+			return 0;+		}++		if (size >= 3) {+			uint8_t t2 = tolower(text[2]);++			if (((t1 == 'r' && t2 == 'e') ||+				(t1 == 'l' && t2 == 'l') ||+				(t1 == 'v' && t2 == 'e')) &&+				(size == 4 || word_boundary(text[3]))) {+				BUFPUTSL(ob, "&rsquo;");+				return 0;+			}+		}+	}++	if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote))+		return 0;++	bufputc(ob, text[0]);+	return 0;+}++static size_t+smartypants_cb__parens(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (size >= 3) {+		uint8_t t1 = tolower(text[1]);+		uint8_t t2 = tolower(text[2]);++		if (t1 == 'c' && t2 == ')') {+			BUFPUTSL(ob, "&copy;");+			return 2;+		}++		if (t1 == 'r' && t2 == ')') {+			BUFPUTSL(ob, "&reg;");+			return 2;+		}++		if (size >= 4 && t1 == 't' && t2 == 'm' && text[3] == ')') {+			BUFPUTSL(ob, "&trade;");+			return 3;+		}+	}++	bufputc(ob, text[0]);+	return 0;+}++static size_t+smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (size >= 3 && text[1] == '-' && text[2] == '-') {+		BUFPUTSL(ob, "&mdash;");+		return 2;+	}++	if (size >= 2 && text[1] == '-') {+		BUFPUTSL(ob, "&ndash;");+		return 1;+	}++	bufputc(ob, text[0]);+	return 0;+}++static size_t+smartypants_cb__amp(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (size >= 6 && memcmp(text, "&quot;", 6) == 0) {+		if (smartypants_quotes(ob, previous_char, size >= 7 ? text[6] : 0, 'd', &smrt->in_dquote))+			return 5;+	}++	if (size >= 4 && memcmp(text, "&#0;", 4) == 0)+		return 3;++	bufputc(ob, '&');+	return 0;+}++static size_t+smartypants_cb__period(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (size >= 3 && text[1] == '.' && text[2] == '.') {+		BUFPUTSL(ob, "&hellip;");+		return 2;+	}++	if (size >= 5 && text[1] == ' ' && text[2] == '.' && text[3] == ' ' && text[4] == '.') {+		BUFPUTSL(ob, "&hellip;");+		return 4;+	}++	bufputc(ob, text[0]);+	return 0;+}++static size_t+smartypants_cb__backtick(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (size >= 2 && text[1] == '`') {+		if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote))+			return 1;+	}++	return 0;+}++static size_t+smartypants_cb__number(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (word_boundary(previous_char) && size >= 3) {+		if (text[0] == '1' && text[1] == '/' && text[2] == '2') {+			if (size == 3 || word_boundary(text[3])) {+				BUFPUTSL(ob, "&frac12;");+				return 2;+			}+		}++		if (text[0] == '1' && text[1] == '/' && text[2] == '4') {+			if (size == 3 || word_boundary(text[3]) ||+				(size >= 5 && tolower(text[3]) == 't' && tolower(text[4]) == 'h')) {+				BUFPUTSL(ob, "&frac14;");+				return 2;+			}+		}++		if (text[0] == '3' && text[1] == '/' && text[2] == '4') {+			if (size == 3 || word_boundary(text[3]) ||+				(size >= 6 && tolower(text[3]) == 't' && tolower(text[4]) == 'h' && tolower(text[5]) == 's')) {+				BUFPUTSL(ob, "&frac34;");+				return 2;+			}+		}+	}++	bufputc(ob, text[0]);+	return 0;+}++static size_t+smartypants_cb__dquote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (!smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 'd', &smrt->in_dquote))+		BUFPUTSL(ob, "&quot;");++	return 0;+}++static size_t+smartypants_cb__ltag(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	static const char *skip_tags[] = {+	  "pre", "code", "var", "samp", "kbd", "math", "script", "style"+	};+	static const size_t skip_tags_count = 8;++	size_t tag, i = 0;++	while (i < size && text[i] != '>')+		i++;++	for (tag = 0; tag < skip_tags_count; ++tag) {+		if (sdhtml_is_tag(text, size, skip_tags[tag]) == HTML_TAG_OPEN)+			break;+	}++	if (tag < skip_tags_count) {+		for (;;) {+			while (i < size && text[i] != '<')+				i++;++			if (i == size)+				break;++			if (sdhtml_is_tag(text + i, size - i, skip_tags[tag]) == HTML_TAG_CLOSE)+				break;++			i++;+		}++		while (i < size && text[i] != '>')+			i++;+	}++	bufput(ob, text, i + 1);+	return i;+}++static size_t+smartypants_cb__escape(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size)+{+	if (size < 2)+		return 0;++	switch (text[1]) {+	case '\\':+	case '"':+	case '\'':+	case '.':+	case '-':+	case '`':+		bufputc(ob, text[1]);+		return 1;++	default:+		bufputc(ob, '\\');+		return 0;+	}+}++#if 0+static struct {+    uint8_t c0;+    const uint8_t *pattern;+    const uint8_t *entity;+    int skip;+} smartypants_subs[] = {+    { '\'', "'s>",      "&rsquo;",  0 },+    { '\'', "'t>",      "&rsquo;",  0 },+    { '\'', "'re>",     "&rsquo;",  0 },+    { '\'', "'ll>",     "&rsquo;",  0 },+    { '\'', "'ve>",     "&rsquo;",  0 },+    { '\'', "'m>",      "&rsquo;",  0 },+    { '\'', "'d>",      "&rsquo;",  0 },+    { '-',  "--",       "&mdash;",  1 },+    { '-',  "<->",      "&ndash;",  0 },+    { '.',  "...",      "&hellip;", 2 },+    { '.',  ". . .",    "&hellip;", 4 },+    { '(',  "(c)",      "&copy;",   2 },+    { '(',  "(r)",      "&reg;",    2 },+    { '(',  "(tm)",     "&trade;",  3 },+    { '3',  "<3/4>",    "&frac34;", 2 },+    { '3',  "<3/4ths>", "&frac34;", 2 },+    { '1',  "<1/2>",    "&frac12;", 2 },+    { '1',  "<1/4>",    "&frac14;", 2 },+    { '1',  "<1/4th>",  "&frac14;", 2 },+    { '&',  "&#0;",      0,       3 },+};+#endif++void+sdhtml_smartypants(struct buf *ob, const uint8_t *text, size_t size)+{+	size_t i;+	struct smartypants_data smrt = {0, 0};++	if (!text)+		return;++	bufgrow(ob, size);++	for (i = 0; i < size; ++i) {+		size_t org;+		uint8_t action = 0;++		org = i;+		while (i < size && (action = smartypants_cb_chars[text[i]]) == 0)+			i++;++		if (i > org)+			bufput(ob, text + org, i - org);++		if (i < size) {+			i += smartypants_cb_ptrs[(int)action]+				(ob, &smrt, i ? text[i - 1] : 0, text + i, size - i);+		}+	}+}++
+ sundown-c/markdown.c view
@@ -0,0 +1,2551 @@+/* markdown.c - generic markdown parser */++/*+ * Copyright (c) 2009, Natacha Porté+ * Copyright (c) 2011, Vicent Marti+ *+ * 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 "markdown.h"+#include "stack.h"++#include <assert.h>+#include <string.h>+#include <ctype.h>+#include <stdio.h>++#if defined(_WIN32)+#define strncasecmp	_strnicmp+#endif++#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 */+struct link_ref {+	unsigned int id;++	struct buf *link;+	struct buf *title;++	struct link_ref *next;+};++/* 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 sd_markdown;+typedef size_t+(*char_trigger)(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);++static size_t char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);+static size_t char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size);++enum markdown_char_t {+	MD_CHAR_NONE = 0,+	MD_CHAR_EMPHASIS,+	MD_CHAR_CODESPAN,+	MD_CHAR_LINEBREAK,+	MD_CHAR_LINK,+	MD_CHAR_LANGLE,+	MD_CHAR_ESCAPE,+	MD_CHAR_ENTITITY,+	MD_CHAR_AUTOLINK_URL,+	MD_CHAR_AUTOLINK_EMAIL,+	MD_CHAR_AUTOLINK_WWW,+	MD_CHAR_SUPERSCRIPT,+};++static char_trigger markdown_char_ptrs[] = {+	NULL,+	&char_emphasis,+	&char_codespan,+	&char_linebreak,+	&char_link,+	&char_langle_tag,+	&char_escape,+	&char_entity,+	&char_autolink_url,+	&char_autolink_email,+	&char_autolink_www,+	&char_superscript,+};++/* render • structure containing one particular render */+struct sd_markdown {+	struct sd_callbacks	cb;+	void *opaque;++	struct link_ref *refs[REF_TABLE_SIZE];+	uint8_t active_char[256];+	struct stack work_bufs[2];+	unsigned int ext_flags;+	size_t max_nesting;+	int in_link_body;+};++/***************************+ * HELPER FUNCTIONS *+ ***************************/++static inline struct buf *+rndr_newbuf(struct sd_markdown *rndr, int type)+{+	static const size_t buf_size[2] = {256, 64};+	struct buf *work = NULL;+	struct stack *pool = &rndr->work_bufs[type];++	if (pool->size < pool->asize &&+		pool->item[pool->size] != NULL) {+		work = pool->item[pool->size++];+		work->size = 0;+	} else {+		work = bufnew(buf_size[type]);+		stack_push(pool, work);+	}++	return work;+}++static inline void+rndr_popbuf(struct sd_markdown *rndr, int type)+{+	rndr->work_bufs[type].size--;+}++static void+unscape_text(struct buf *ob, struct buf *src)+{+	size_t i = 0, org;+	while (i < src->size) {+		org = i;+		while (i < src->size && src->data[i] != '\\')+			i++;++		if (i > org)+			bufput(ob, src->data + org, i - org);++		if (i + 1 >= src->size)+			break;++		bufputc(ob, src->data[i + 1]);+		i += 2;+	}+}++static unsigned int+hash_link_ref(const uint8_t *link_ref, size_t length)+{+	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;+}++static struct link_ref *+add_link_ref(+	struct link_ref **references,+	const uint8_t *name, size_t name_size)+{+	struct link_ref *ref = calloc(1, sizeof(struct link_ref));++	if (!ref)+		return NULL;++	ref->id = hash_link_ref(name, name_size);+	ref->next = references[ref->id % REF_TABLE_SIZE];++	references[ref->id % REF_TABLE_SIZE] = ref;+	return ref;+}++static struct link_ref *+find_link_ref(struct link_ref **references, uint8_t *name, size_t length)+{+	unsigned int hash = hash_link_ref(name, length);+	struct link_ref *ref = NULL;++	ref = references[hash % REF_TABLE_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;++	for (i = 0; i < REF_TABLE_SIZE; ++i) {+		struct link_ref *r = references[i];+		struct link_ref *next;++		while (r) {+			next = r->next;+			bufrelease(r->link);+			bufrelease(r->title);+			free(r);+			r = next;+		}+	}+}++/*+ * Check whether a char is a Markdown space.++ * Right now we only consider spaces the actual+ * space and a newline: tabs and carriage returns+ * are filtered out during the preprocessing phase.+ *+ * If we wanted to actually be UTF-8 compliant, we+ * should instead extract an Unicode codepoint from+ * this character and check for space properties.+ */+static inline int+_isspace(int c)+{+	return c == ' ' || c == '\n';+}++/****************************+ * INLINE PARSING FUNCTIONS *+ ****************************/++/* is_mail_autolink • looks for the address part of a mail autolink and '>' */+/* this is less strict than the original markdown e-mail address matching */+static size_t+is_mail_autolink(uint8_t *data, size_t size)+{+	size_t i = 0, nb = 0;++	/* address is assumed to be: [-@._a-zA-Z0-9]+ with exactly one '@' */+	for (i = 0; i < size; ++i) {+		if (isalnum(data[i]))+			continue;++		switch (data[i]) {+			case '@':+				nb++;++			case '-':+			case '.':+			case '_':+				break;++			case '>':+				return (nb == 1) ? i + 1 : 0;++			default:+				return 0;+		}+	}++	return 0;+}++/* tag_length • returns the length of the given tag, or 0 is it's not valid */+static size_t+tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink)+{+	size_t i, j;++	/* a valid tag can't be shorter than 3 chars */+	if (size < 3) return 0;++	/* begins with a '<' optionally followed by '/', followed by letter or number */+	if (data[0] != '<') return 0;+	i = (data[1] == '/') ? 2 : 1;++	if (!isalnum(data[i]))+		return 0;++	/* scheme test */+	*autolink = MKDA_NOT_AUTOLINK;++	/* try to find the beginning of an URI */+	while (i < size && (isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-'))+		i++;++	if (i > 1 && data[i] == '@') {+		if ((j = is_mail_autolink(data + i, size - i)) != 0) {+			*autolink = MKDA_EMAIL;+			return i + j;+		}+	}++	if (i > 2 && data[i] == ':') {+		*autolink = MKDA_NORMAL;+		i++;+	}++	/* completing autolink test: no whitespace or ' or " */+	if (i >= size)+		*autolink = MKDA_NOT_AUTOLINK;++	else if (*autolink) {+		j = i;++		while (i < size) {+			if (data[i] == '\\') i += 2;+			else if (data[i] == '>' || data[i] == '\'' ||+					data[i] == '"' || data[i] == ' ' || data[i] == '\n')+					break;+			else i++;+		}++		if (i >= size) return 0;+		if (i > j && data[i] == '>') return i + 1;+		/* one of the forbidden chars has been found */+		*autolink = MKDA_NOT_AUTOLINK;+	}++	/* looking for sometinhg looking like a tag end */+	while (i < size && data[i] != '>') i++;+	if (i >= size) return 0;+	return i + 1;+}++/* parse_inline • parses inline markdown elements */+static void+parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)+{+	size_t i = 0, end = 0;+	uint8_t action = 0;+	struct buf work = { 0, 0, 0, 0 };++	if (rndr->work_bufs[BUFFER_SPAN].size ++		rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)+		return;++	while (i < size) {+		/* copying inactive chars into the output */+		while (end < size && (action = rndr->active_char[data[end]]) == 0) {+			end++;+		}++		if (rndr->cb.normal_text) {+			work.data = data + i;+			work.size = end - i;+			rndr->cb.normal_text(ob, &work, rndr->opaque);+		}+		else+			bufput(ob, data + i, end - i);++		if (end >= size) break;+		i = end;++		end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i);+		if (!end) /* no action from the callback */+			end = i + 1;+		else {+			i += end;+			end = i;+		}+	}+}++/* find_emph_char • looks for the next emph uint8_t, skipping other constructs */+static size_t+find_emph_char(uint8_t *data, size_t size, uint8_t c)+{+	size_t i = 1;++	while (i < size) {+		while (i < size && data[i] != c && data[i] != '`' && data[i] != '[')+			i++;++		if (i == size)+			return 0;++		if (data[i] == c)+			return i;++		/* not counting escaped chars */+		if (i && data[i - 1] == '\\') {+			i++; continue;+		}++		if (data[i] == '`') {+			size_t span_nb = 0, bt;+			size_t tmp_i = 0;++			/* counting the number of opening backticks */+			while (i < size && data[i] == '`') {+				i++; span_nb++;+			}++			if (i >= size) return 0;++			/* finding the matching closing sequence */+			bt = 0;+			while (i < size && bt < span_nb) {+				if (!tmp_i && data[i] == c) tmp_i = i;+				if (data[i] == '`') bt++;+				else bt = 0;+				i++;+			}++			if (i >= size) return tmp_i;+		}+		/* skipping a link */+		else if (data[i] == '[') {+			size_t tmp_i = 0;+			uint8_t cc;++			i++;+			while (i < size && data[i] != ']') {+				if (!tmp_i && data[i] == c) tmp_i = i;+				i++;+			}++			i++;+			while (i < size && (data[i] == ' ' || data[i] == '\n'))+				i++;++			if (i >= size)+				return tmp_i;++			switch (data[i]) {+			case '[':+				cc = ']'; break;++			case '(':+				cc = ')'; break;++			default:+				if (tmp_i)+					return tmp_i;+				else+					continue;+			}++			i++;+			while (i < size && data[i] != cc) {+				if (!tmp_i && data[i] == c) tmp_i = i;+				i++;+			}++			if (i >= size)+				return tmp_i;++			i++;+		}+	}++	return 0;+}++/* 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 sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)+{+	size_t i = 0, len;+	struct buf *work = 0;+	int r;++	if (!rndr->cb.emphasis) return 0;++	/* skipping one symbol if coming from emph3 */+	if (size > 1 && data[0] == c && data[1] == c) i = 1;++	while (i < size) {+		len = find_emph_char(data + i, size - i, c);+		if (!len) return 0;+		i += len;+		if (i >= size) return 0;++		if (data[i] == c && !_isspace(data[i - 1])) {++			if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {+				if (!(i + 1 == size || _isspace(data[i + 1]) || ispunct(data[i + 1])))+					continue;+			}++			work = rndr_newbuf(rndr, BUFFER_SPAN);+			parse_inline(work, rndr, data, i);+			r = rndr->cb.emphasis(ob, work, rndr->opaque);+			rndr_popbuf(rndr, BUFFER_SPAN);+			return r ? i + 1 : 0;+		}+	}++	return 0;+}++/* parse_emph2 • parsing single emphase */+static size_t+parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)+{+	int (*render_method)(struct buf *ob, const struct buf *text, void *opaque);+	size_t i = 0, len;+	struct buf *work = 0;+	int r;++	render_method = (c == '~') ? rndr->cb.strikethrough : rndr->cb.double_emphasis;++	if (!render_method)+		return 0;++	while (i < size) {+		len = find_emph_char(data + i, size - i, c);+		if (!len) return 0;+		i += len;++		if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) {+			work = rndr_newbuf(rndr, BUFFER_SPAN);+			parse_inline(work, rndr, data, i);+			r = render_method(ob, work, rndr->opaque);+			rndr_popbuf(rndr, BUFFER_SPAN);+			return r ? i + 2 : 0;+		}+		i++;+	}+	return 0;+}++/* 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 sd_markdown *rndr, uint8_t *data, size_t size, uint8_t c)+{+	size_t i = 0, len;+	int r;++	while (i < size) {+		len = find_emph_char(data + i, size - i, c);+		if (!len) return 0;+		i += len;++		/* skip whitespace preceded symbols */+		if (data[i] != c || _isspace(data[i - 1]))+			continue;++		if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) {+			/* triple symbol found */+			struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);++			parse_inline(work, rndr, data, i);+			r = rndr->cb.triple_emphasis(ob, work, rndr->opaque);+			rndr_popbuf(rndr, BUFFER_SPAN);+			return r ? i + 3 : 0;++		} else if (i + 1 < size && data[i + 1] == c) {+			/* double symbol found, handing over to emph1 */+			len = parse_emph1(ob, rndr, data - 2, size + 2, c);+			if (!len) return 0;+			else return len - 2;++		} else {+			/* single symbol found, handing over to emph2 */+			len = parse_emph2(ob, rndr, data - 1, size + 1, c);+			if (!len) return 0;+			else return len - 1;+		}+	}+	return 0;+}++/* char_emphasis • single and double emphasis parsing */+static size_t+char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	uint8_t c = data[0];+	size_t ret;++	if (size > 2 && data[1] != c) {+		/* whitespace cannot follow an opening emphasis;+		 * strikethrough only takes two characters '~~' */+		if (c == '~' || _isspace(data[1]) || (ret = parse_emph1(ob, rndr, data + 1, size - 1, c)) == 0)+			return 0;++		return ret + 1;+	}++	if (size > 3 && data[1] == c && data[2] != c) {+		if (_isspace(data[2]) || (ret = parse_emph2(ob, rndr, data + 2, size - 2, c)) == 0)+			return 0;++		return ret + 2;+	}++	if (size > 4 && data[1] == c && data[2] == c && data[3] != c) {+		if (c == '~' || _isspace(data[3]) || (ret = parse_emph3(ob, rndr, data + 3, size - 3, c)) == 0)+			return 0;++		return ret + 3;+	}++	return 0;+}+++/* char_linebreak • '\n' preceded by two spaces (assuming linebreak != 0) */+static size_t+char_linebreak(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	if (offset < 2 || data[-1] != ' ' || data[-2] != ' ')+		return 0;++	/* removing the last space from ob and rendering */+	while (ob->size && ob->data[ob->size - 1] == ' ')+		ob->size--;++	return rndr->cb.linebreak(ob, rndr->opaque) ? 1 : 0;+}+++/* char_codespan • '`' parsing a code span (assuming codespan != 0) */+static size_t+char_codespan(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	size_t end, nb = 0, i, f_begin, f_end;++	/* counting the number of backticks in the delimiter */+	while (nb < size && data[nb] == '`')+		nb++;++	/* finding the next delimiter */+	i = 0;+	for (end = nb; end < size && i < nb; end++) {+		if (data[end] == '`') i++;+		else i = 0;+	}++	if (i < nb && end >= size)+		return 0; /* no matching delimiter */++	/* trimming outside whitespaces */+	f_begin = nb;+	while (f_begin < end && data[f_begin] == ' ')+		f_begin++;++	f_end = end - nb;+	while (f_end > nb && data[f_end-1] == ' ')+		f_end--;++	/* real code span */+	if (f_begin < f_end) {+		struct buf work = { data + f_begin, f_end - f_begin, 0, 0 };+		if (!rndr->cb.codespan(ob, &work, rndr->opaque))+			end = 0;+	} else {+		if (!rndr->cb.codespan(ob, 0, rndr->opaque))+			end = 0;+	}++	return end;+}+++/* char_escape • '\\' backslash escape */+static size_t+char_escape(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	static const char *escape_chars = "\\`*_{}[]()#+-.!:|&<>^~";+	struct buf work = { 0, 0, 0, 0 };++	if (size > 1) {+		if (strchr(escape_chars, data[1]) == NULL)+			return 0;++		if (rndr->cb.normal_text) {+			work.data = data + 1;+			work.size = 1;+			rndr->cb.normal_text(ob, &work, rndr->opaque);+		}+		else bufputc(ob, data[1]);+	} else if (size == 1) {+		bufputc(ob, data[0]);+	}++	return 2;+}++/* 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 sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	size_t end = 1;+	struct buf work = { 0, 0, 0, 0 };++	if (end < size && data[end] == '#')+		end++;++	while (end < size && isalnum(data[end]))+		end++;++	if (end < size && data[end] == ';')+		end++; /* real entity */+	else+		return 0; /* lone '&' */++	if (rndr->cb.entity) {+		work.data = data;+		work.size = end;+		rndr->cb.entity(ob, &work, rndr->opaque);+	}+	else bufput(ob, data, end);++	return end;+}++/* char_langle_tag • '<' when tags or autolinks are allowed */+static size_t+char_langle_tag(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	enum mkd_autolink altype = MKDA_NOT_AUTOLINK;+	size_t end = tag_length(data, size, &altype);+	struct buf work = { data, end, 0, 0 };+	int ret = 0;++	if (end > 2) {+		if (rndr->cb.autolink && altype != MKDA_NOT_AUTOLINK) {+			struct buf *u_link = rndr_newbuf(rndr, BUFFER_SPAN);+			work.data = data + 1;+			work.size = end - 2;+			unscape_text(u_link, &work);+			ret = rndr->cb.autolink(ob, u_link, altype, rndr->opaque);+			rndr_popbuf(rndr, BUFFER_SPAN);+		}+		else if (rndr->cb.raw_html_tag)+			ret = rndr->cb.raw_html_tag(ob, &work, rndr->opaque);+	}++	if (!ret) return 0;+	else return end;+}++static size_t+char_autolink_www(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	struct buf *link, *link_url, *link_text;+	size_t link_len, rewind;++	if (!rndr->cb.link || rndr->in_link_body)+		return 0;++	link = rndr_newbuf(rndr, BUFFER_SPAN);++	if ((link_len = sd_autolink__www(&rewind, link, data, offset, size, 0)) > 0) {+		link_url = rndr_newbuf(rndr, BUFFER_SPAN);+		BUFPUTSL(link_url, "http://");+		bufput(link_url, link->data, link->size);++		ob->size -= rewind;+		if (rndr->cb.normal_text) {+			link_text = rndr_newbuf(rndr, BUFFER_SPAN);+			rndr->cb.normal_text(link_text, link, rndr->opaque);+			rndr->cb.link(ob, link_url, NULL, link_text, rndr->opaque);+			rndr_popbuf(rndr, BUFFER_SPAN);+		} else {+			rndr->cb.link(ob, link_url, NULL, link, rndr->opaque);+		}+		rndr_popbuf(rndr, BUFFER_SPAN);+	}++	rndr_popbuf(rndr, BUFFER_SPAN);+	return link_len;+}++static size_t+char_autolink_email(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	struct buf *link;+	size_t link_len, rewind;++	if (!rndr->cb.autolink || rndr->in_link_body)+		return 0;++	link = rndr_newbuf(rndr, BUFFER_SPAN);++	if ((link_len = sd_autolink__email(&rewind, link, data, offset, size, 0)) > 0) {+		ob->size -= rewind;+		rndr->cb.autolink(ob, link, MKDA_EMAIL, rndr->opaque);+	}++	rndr_popbuf(rndr, BUFFER_SPAN);+	return link_len;+}++static size_t+char_autolink_url(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	struct buf *link;+	size_t link_len, rewind;++	if (!rndr->cb.autolink || rndr->in_link_body)+		return 0;++	link = rndr_newbuf(rndr, BUFFER_SPAN);++	if ((link_len = sd_autolink__url(&rewind, link, data, offset, size, 0)) > 0) {+		ob->size -= rewind;+		rndr->cb.autolink(ob, link, MKDA_NORMAL, rndr->opaque);+	}++	rndr_popbuf(rndr, BUFFER_SPAN);+	return link_len;+}++/* char_link • '[': parsing a link or an image */+static size_t+char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *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;+	struct buf *content = 0;+	struct buf *link = 0;+	struct buf *title = 0;+	struct buf *u_link = 0;+	size_t org_work_size = rndr->work_bufs[BUFFER_SPAN].size;+	int text_has_nl = 0, ret = 0;+	int in_title = 0, qtype = 0;++	/* checking whether the correct renderer exists */+	if ((is_img && !rndr->cb.image) || (!is_img && !rndr->cb.link))+		goto cleanup;++	/* looking for the matching closing bracket */+	for (level = 1; i < size; i++) {+		if (data[i] == '\n')+			text_has_nl = 1;++		else if (data[i - 1] == '\\')+			continue;++		else if (data[i] == '[')+			level++;++		else if (data[i] == ']') {+			level--;+			if (level <= 0)+				break;+		}+	}++	if (i >= size)+		goto cleanup;++	txt_e = i;+	i++;++	/* skip any amount of whitespace or newline */+	/* (this is much more laxist than original markdown syntax) */+	while (i < size && _isspace(data[i]))+		i++;++	/* inline style link */+	if (i < size && data[i] == '(') {+		/* skipping initial whitespace */+		i++;++		while (i < size && _isspace(data[i]))+			i++;++		link_b = i;++		/* looking for link end: ' " ) */+		while (i < size) {+			if (data[i] == '\\') i += 2;+			else if (data[i] == ')') break;+			else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;+			else i++;+		}++		if (i >= size) goto cleanup;+		link_e = i;++		/* looking for title end if present */+		if (data[i] == '\'' || data[i] == '"') {+			qtype = data[i];+			in_title = 1;+			i++;+			title_b = i;++			while (i < size) {+				if (data[i] == '\\') i += 2;+				else if (data[i] == qtype) {in_title = 0; i++;}+				else if ((data[i] == ')') && !in_title) break;+				else i++;+			}++			if (i >= size) goto cleanup;++			/* skipping whitespaces after title */+			title_e = i - 1;+			while (title_e > title_b && _isspace(data[title_e]))+				title_e--;++			/* checking for closing quote presence */+			if (data[title_e] != '\'' &&  data[title_e] != '"') {+				title_b = title_e = 0;+				link_e = i;+			}+		}++		/* remove whitespace at the end of the link */+		while (link_e > link_b && _isspace(data[link_e - 1]))+			link_e--;++		/* remove optional angle brackets around the link */+		if (data[link_b] == '<') link_b++;+		if (data[link_e - 1] == '>') link_e--;++		/* building escaped link and title */+		if (link_e > link_b) {+			link = rndr_newbuf(rndr, BUFFER_SPAN);+			bufput(link, data + link_b, link_e - link_b);+		}++		if (title_e > title_b) {+			title = rndr_newbuf(rndr, BUFFER_SPAN);+			bufput(title, data + title_b, title_e - title_b);+		}++		i++;+	}++	/* reference style link */+	else if (i < size && data[i] == '[') {+		struct buf id = { 0, 0, 0, 0 };+		struct link_ref *lr;++		/* looking for the id */+		i++;+		link_b = i;+		while (i < size && data[i] != ']') i++;+		if (i >= size) goto cleanup;+		link_e = i;++		/* finding the link_ref */+		if (link_b == link_e) {+			if (text_has_nl) {+				struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN);+				size_t j;++				for (j = 1; j < txt_e; j++) {+					if (data[j] != '\n')+						bufputc(b, data[j]);+					else if (data[j - 1] != ' ')+						bufputc(b, ' ');+				}++				id.data = b->data;+				id.size = b->size;+			} else {+				id.data = data + 1;+				id.size = txt_e - 1;+			}+		} else {+			id.data = data + link_b;+			id.size = link_e - link_b;+		}++		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;+		i++;+	}++	/* shortcut reference style link */+	else {+		struct buf id = { 0, 0, 0, 0 };+		struct link_ref *lr;++		/* crafting the id */+		if (text_has_nl) {+			struct buf *b = rndr_newbuf(rndr, BUFFER_SPAN);+			size_t j;++			for (j = 1; j < txt_e; j++) {+				if (data[j] != '\n')+					bufputc(b, data[j]);+				else if (data[j - 1] != ' ')+					bufputc(b, ' ');+			}++			id.data = b->data;+			id.size = b->size;+		} else {+			id.data = data + 1;+			id.size = txt_e - 1;+		}++		/* finding the link_ref */+		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;++		/* rewinding the whitespace */+		i = txt_e + 1;+	}++	/* building content: img alt is escaped, link content is parsed */+	if (txt_e > 1) {+		content = rndr_newbuf(rndr, BUFFER_SPAN);+		if (is_img) {+			bufput(content, data + 1, txt_e - 1);+		} else {+			/* disable autolinking when parsing inline the+			 * content of a link */+			rndr->in_link_body = 1;+			parse_inline(content, rndr, data + 1, txt_e - 1);+			rndr->in_link_body = 0;+		}+	}++	if (link) {+		u_link = rndr_newbuf(rndr, BUFFER_SPAN);+		unscape_text(u_link, link);+	}++	/* calling the relevant rendering function */+	if (is_img) {+		if (ob->size && ob->data[ob->size - 1] == '!')+			ob->size -= 1;++		ret = rndr->cb.image(ob, u_link, title, content, rndr->opaque);+	} else {+		ret = rndr->cb.link(ob, u_link, title, content, rndr->opaque);+	}++	/* cleanup */+cleanup:+	rndr->work_bufs[BUFFER_SPAN].size = (int)org_work_size;+	return ret ? i : 0;+}++static size_t+char_superscript(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset, size_t size)+{+	size_t sup_start, sup_len;+	struct buf *sup;++	if (!rndr->cb.superscript)+		return 0;++	if (size < 2)+		return 0;++	if (data[1] == '(') {+		sup_start = sup_len = 2;++		while (sup_len < size && data[sup_len] != ')' && data[sup_len - 1] != '\\')+			sup_len++;++		if (sup_len == size)+			return 0;+	} else {+		sup_start = sup_len = 1;++		while (sup_len < size && !_isspace(data[sup_len]))+			sup_len++;+	}++	if (sup_len - sup_start == 0)+		return (sup_start == 2) ? 3 : 0;++	sup = rndr_newbuf(rndr, BUFFER_SPAN);+	parse_inline(sup, rndr, data + sup_start, sup_len - sup_start);+	rndr->cb.superscript(ob, sup, rndr->opaque);+	rndr_popbuf(rndr, BUFFER_SPAN);++	return (sup_start == 2) ? sup_len + 1 : sup_len;+}++/*********************************+ * BLOCK-LEVEL PARSING FUNCTIONS *+ *********************************/++/* is_empty • returns the line length when it is empty, 0 otherwise */+static size_t+is_empty(uint8_t *data, size_t size)+{+	size_t i;++	for (i = 0; i < size && data[i] != '\n'; i++)+		if (data[i] != ' ')+			return 0;++	return i + 1;+}++/* is_hrule • returns whether a line is a horizontal rule */+static int+is_hrule(uint8_t *data, size_t size)+{+	size_t i = 0, n = 0;+	uint8_t c;++	/* skipping initial spaces */+	if (size < 3) return 0;+	if (data[0] == ' ') { i++;+	if (data[1] == ' ') { i++;+	if (data[2] == ' ') { i++; } } }++	/* looking at the hrule uint8_t */+	if (i + 2 >= size+	|| (data[i] != '*' && data[i] != '-' && data[i] != '_'))+		return 0;+	c = data[i];++	/* the whole line must be the char or whitespace */+	while (i < size && data[i] != '\n') {+		if (data[i] == c) n++;+		else if (data[i] != ' ')+			return 0;++		i++;+	}++	return n >= 3;+}++/* check if a line begins with a code fence; return the+ * width of the code fence */+static size_t+prefix_codefence(uint8_t *data, size_t size)+{+	size_t i = 0, n = 0;+	uint8_t c;++	/* skipping initial spaces */+	if (size < 3) return 0;+	if (data[0] == ' ') { i++;+	if (data[1] == ' ') { i++;+	if (data[2] == ' ') { i++; } } }++	/* looking at the hrule uint8_t */+	if (i + 2 >= size || !(data[i] == '~' || data[i] == '`'))+		return 0;++	c = data[i];++	/* the whole line must be the uint8_t or whitespace */+	while (i < size && data[i] == c) {+		n++; i++;+	}++	if (n < 3)+		return 0;++	return i;+}++/* check if a line is a code fence; return its size if it is */+static size_t+is_codefence(uint8_t *data, size_t size, struct buf *syntax)+{+	size_t i = 0, syn_len = 0;+	uint8_t *syn_start;++	i = prefix_codefence(data, size);+	if (i == 0)+		return 0;++	while (i < size && data[i] == ' ')+		i++;++	syn_start = data + i;++	if (i < size && data[i] == '{') {+		i++; syn_start++;++		while (i < size && data[i] != '}' && data[i] != '\n') {+			syn_len++; i++;+		}++		if (i == size || data[i] != '}')+			return 0;++		/* strip all whitespace at the beginning and the end+		 * of the {} block */+		while (syn_len > 0 && _isspace(syn_start[0])) {+			syn_start++; syn_len--;+		}++		while (syn_len > 0 && _isspace(syn_start[syn_len - 1]))+			syn_len--;++		i++;+	} else {+		while (i < size && !_isspace(data[i])) {+			syn_len++; i++;+		}+	}++	if (syntax) {+		syntax->data = syn_start;+		syntax->size = syn_len;+	}++	while (i < size && data[i] != '\n') {+		if (!_isspace(data[i]))+			return 0;++		i++;+	}++	return i + 1;+}++/* is_atxheader • returns whether the line is a hash-prefixed header */+static int+is_atxheader(struct sd_markdown *rndr, uint8_t *data, size_t size)+{+	if (data[0] != '#')+		return 0;++	if (rndr->ext_flags & MKDEXT_SPACE_HEADERS) {+		size_t level = 0;++		while (level < size && level < 6 && data[level] == '#')+			level++;++		if (level < size && data[level] != ' ')+			return 0;+	}++	return 1;+}++/* is_headerline • returns whether the line is a setext-style hdr underline */+static int+is_headerline(uint8_t *data, size_t size)+{+	size_t i = 0;++	/* test of level 1 header */+	if (data[i] == '=') {+		for (i = 1; i < size && data[i] == '='; i++);+		while (i < size && data[i] == ' ') i++;+		return (i >= size || data[i] == '\n') ? 1 : 0; }++	/* test of level 2 header */+	if (data[i] == '-') {+		for (i = 1; i < size && data[i] == '-'; i++);+		while (i < size && data[i] == ' ') i++;+		return (i >= size || data[i] == '\n') ? 2 : 0; }++	return 0;+}++static int+is_next_headerline(uint8_t *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(uint8_t *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] == '>') {+		if (i + 1 < size && data[i + 1] == ' ')+			return i + 2;++		return i + 1;+	}++	return 0;+}++/* prefix_code • returns prefix length for block code*/+static size_t+prefix_code(uint8_t *data, size_t size)+{+	if (size > 3 && data[0] == ' ' && data[1] == ' '+		&& data[2] == ' ' && data[3] == ' ') return 4;++	return 0;+}++/* prefix_oli • returns ordered list item prefix */+static size_t+prefix_oli(uint8_t *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] != ' ')+		return 0;++	if (is_next_headerline(data + i, size - i))+		return 0;++	return i + 2;+}++/* prefix_uli • returns ordered list item prefix */+static size_t+prefix_uli(uint8_t *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] != ' ')+		return 0;++	if (is_next_headerline(data + i, size - i))+		return 0;++	return i + 2;+}+++/* parse_block • parsing of one block, returning next uint8_t to parse */+static void parse_block(struct buf *ob, struct sd_markdown *rndr,+			uint8_t *data, size_t size);+++/* parse_blockquote • handles parsing of a blockquote fragment */+static size_t+parse_blockquote(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)+{+	size_t beg, end = 0, pre, work_size = 0;+	uint8_t *work_data = 0;+	struct buf *out = 0;++	out = rndr_newbuf(rndr, BUFFER_BLOCK);+	beg = 0;+	while (beg < size) {+		for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);++		pre = prefix_quote(data + beg, end - beg);++		if (pre)+			beg += pre; /* skipping prefix */++		/* empty line followed by non-quote line */+		else if (is_empty(data + beg, end - beg) &&+				(end >= size || (prefix_quote(data + end, size - end) == 0 &&+				!is_empty(data + end, size - end))))+			break;++		if (beg < end) { /* copy into the in-place working buffer */+			/* bufput(work, data + beg, end - beg); */+			if (!work_data)+				work_data = data + beg;+			else if (data + beg != work_data + work_size)+				memmove(work_data + work_size, data + beg, end - beg);+			work_size += end - beg;+		}+		beg = end;+	}++	parse_block(out, rndr, work_data, work_size);+	if (rndr->cb.blockquote)+		rndr->cb.blockquote(ob, out, rndr->opaque);+	rndr_popbuf(rndr, BUFFER_BLOCK);+	return end;+}++static size_t+parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render);++/* parse_blockquote • handles parsing of a regular paragraph */+static size_t+parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)+{+	size_t i = 0, end = 0;+	int level = 0;+	struct buf work = { data, 0, 0, 0 };++	while (i < size) {+		for (end = i + 1; end < size && data[end - 1] != '\n'; end++) /* empty */;++		if (is_empty(data + i, size - i))+			break;++		if ((level = is_headerline(data + i, size - i)) != 0)+			break;++		if (is_atxheader(rndr, data + i, size - i) ||+			is_hrule(data + i, size - i) ||+			prefix_quote(data + i, size - i)) {+			end = i;+			break;+		}++		/*+		 * Early termination of a paragraph with the same logic+		 * as Markdown 1.0.0. If this logic is applied, the+		 * Markdown 1.0.3 test suite won't pass cleanly+		 *+		 * :: If the first character in a new line is not a letter,+		 * let's check to see if there's some kind of block starting+		 * here+		 */+		if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !isalnum(data[i])) {+			if (prefix_oli(data + i, size - i) ||+				prefix_uli(data + i, size - i)) {+				end = i;+				break;+			}++			/* see if an html block starts here */+			if (data[i] == '<' && rndr->cb.blockhtml &&+				parse_htmlblock(ob, rndr, data + i, size - i, 0)) {+				end = i;+				break;+			}++			/* see if a code fence starts here */+			if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 &&+				is_codefence(data + i, size - i, NULL) != 0) {+				end = i;+				break;+			}+		}++		i = end;+	}++	work.size = i;+	while (work.size && data[work.size - 1] == '\n')+		work.size--;++	if (!level) {+		struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);+		parse_inline(tmp, rndr, work.data, work.size);+		if (rndr->cb.paragraph)+			rndr->cb.paragraph(ob, tmp, rndr->opaque);+		rndr_popbuf(rndr, BUFFER_BLOCK);+	} else {+		struct buf *header_work;++		if (work.size) {+			size_t beg;+			i = work.size;+			work.size -= 1;++			while (work.size && data[work.size] != '\n')+				work.size -= 1;++			beg = work.size + 1;+			while (work.size && data[work.size - 1] == '\n')+				work.size -= 1;++			if (work.size > 0) {+				struct buf *tmp = rndr_newbuf(rndr, BUFFER_BLOCK);+				parse_inline(tmp, rndr, work.data, work.size);++				if (rndr->cb.paragraph)+					rndr->cb.paragraph(ob, tmp, rndr->opaque);++				rndr_popbuf(rndr, BUFFER_BLOCK);+				work.data += beg;+				work.size = i - beg;+			}+			else work.size = i;+		}++		header_work = rndr_newbuf(rndr, BUFFER_SPAN);+		parse_inline(header_work, rndr, work.data, work.size);++		if (rndr->cb.header)+			rndr->cb.header(ob, header_work, (int)level, rndr->opaque);++		rndr_popbuf(rndr, BUFFER_SPAN);+	}++	return end;+}++/* parse_fencedcode • handles parsing of a block-level code fragment */+static size_t+parse_fencedcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)+{+	size_t beg, end;+	struct buf *work = 0;+	struct buf lang = { 0, 0, 0, 0 };++	beg = is_codefence(data, size, &lang);+	if (beg == 0) return 0;++	work = rndr_newbuf(rndr, BUFFER_BLOCK);++	while (beg < size) {+		size_t fence_end;+		struct buf fence_trail = { 0, 0, 0, 0 };++		fence_end = is_codefence(data + beg, size - beg, &fence_trail);+		if (fence_end != 0 && fence_trail.size == 0) {+			beg += fence_end;+			break;+		}++		for (end = beg + 1; end < size && data[end - 1] != '\n'; end++);++		if (beg < end) {+			/* verbatim copy to the working buffer,+				escaping entities */+			if (is_empty(data + beg, end - beg))+				bufputc(work, '\n');+			else bufput(work, data + beg, end - beg);+		}+		beg = end;+	}++	if (work->size && work->data[work->size - 1] != '\n')+		bufputc(work, '\n');++	if (rndr->cb.blockcode)+		rndr->cb.blockcode(ob, work, lang.size ? &lang : NULL, rndr->opaque);++	rndr_popbuf(rndr, BUFFER_BLOCK);+	return beg;+}++static size_t+parse_blockcode(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)+{+	size_t beg, end, pre;+	struct buf *work = 0;++	work = rndr_newbuf(rndr, BUFFER_BLOCK);++	beg = 0;+	while (beg < size) {+		for (end = beg + 1; end < size && data[end - 1] != '\n'; end++) {};+		pre = prefix_code(data + beg, end - beg);++		if (pre)+			beg += pre; /* skipping prefix */+		else if (!is_empty(data + beg, end - beg))+			/* non-empty non-prefixed line breaks the pre */+			break;++		if (beg < end) {+			/* verbatim copy to the working buffer,+				escaping entities */+			if (is_empty(data + beg, end - beg))+				bufputc(work, '\n');+			else bufput(work, data + beg, end - beg);+		}+		beg = end;+	}++	while (work->size && work->data[work->size - 1] == '\n')+		work->size -= 1;++	bufputc(work, '\n');++	if (rndr->cb.blockcode)+		rndr->cb.blockcode(ob, work, NULL, rndr->opaque);++	rndr_popbuf(rndr, BUFFER_BLOCK);+	return beg;+}++/* parse_listitem • parsing of a single list item */+/*	assuming initial prefix is already removed */+static size_t+parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags)+{+	struct buf *work = 0, *inter = 0;+	size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;+	int in_empty = 0, has_inside_empty = 0, in_fence = 0;++	/* keeping track of the first indentation prefix */+	while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')+		orgpre++;++	beg = prefix_uli(data, size);+	if (!beg)+		beg = prefix_oli(data, size);++	if (!beg)+		return 0;++	/* skipping to the beginning of the following line */+	end = beg;+	while (end < size && data[end - 1] != '\n')+		end++;++	/* getting working buffers */+	work = rndr_newbuf(rndr, BUFFER_SPAN);+	inter = rndr_newbuf(rndr, BUFFER_SPAN);++	/* putting the first line into the working buffer */+	bufput(work, data + beg, end - beg);+	beg = end;++	/* process the following lines */+	while (beg < size) {+		size_t has_next_uli = 0, has_next_oli = 0;++		end++;++		while (end < size && data[end - 1] != '\n')+			end++;++		/* process an empty line */+		if (is_empty(data + beg, end - beg)) {+			in_empty = 1;+			beg = end;+			continue;+		}++		/* calculating the indentation */+		i = 0;+		while (i < 4 && beg + i < end && data[beg + i] == ' ')+			i++;++		pre = i;++		if (rndr->ext_flags & MKDEXT_FENCED_CODE) {+			if (is_codefence(data + beg + i, end - beg - i, NULL) != 0)+				in_fence = !in_fence;+		}++		/* Only check for new list items if we are **not** inside+		 * a fenced code block */+		if (!in_fence) {+			has_next_uli = prefix_uli(data + beg + i, end - beg - i);+			has_next_oli = prefix_oli(data + beg + i, end - beg - i);+		}++		/* checking for ul/ol switch */+		if (in_empty && (+			((*flags & MKD_LIST_ORDERED) && has_next_uli) ||+			(!(*flags & MKD_LIST_ORDERED) && has_next_oli))){+			*flags |= MKD_LI_END;+			break; /* the following item must have same list type */+		}++		/* checking for a new item */+		if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) {+			if (in_empty)+				has_inside_empty = 1;++			if (pre == orgpre) /* the following item must have */+				break;             /* the same indentation */++			if (!sublist)+				sublist = work->size;+		}+		/* joining only indented stuff after empty lines;+		 * note that now we only require 1 space of indentation+		 * to continue a list */+		else if (in_empty && pre == 0) {+			*flags |= MKD_LI_END;+			break;+		}+		else if (in_empty) {+			bufputc(work, '\n');+			has_inside_empty = 1;+		}++		in_empty = 0;++		/* adding the line without prefix into the working buffer */+		bufput(work, data + beg + i, end - beg - i);+		beg = end;+	}++	/* render of li contents */+	if (has_inside_empty)+		*flags |= MKD_LI_BLOCK;++	if (*flags & MKD_LI_BLOCK) {+		/* 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);+		}+		else+			parse_block(inter, rndr, work->data, work->size);+	} else {+		/* intermediate render of inline li */+		if (sublist && sublist < work->size) {+			parse_inline(inter, rndr, work->data, sublist);+			parse_block(inter, rndr, work->data + sublist, work->size - sublist);+		}+		else+			parse_inline(inter, rndr, work->data, work->size);+	}++	/* render of li itself */+	if (rndr->cb.listitem)+		rndr->cb.listitem(ob, inter, *flags, rndr->opaque);++	rndr_popbuf(rndr, BUFFER_SPAN);+	rndr_popbuf(rndr, BUFFER_SPAN);+	return beg;+}+++/* parse_list • parsing ordered or unordered list block */+static size_t+parse_list(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int flags)+{+	struct buf *work = 0;+	size_t i = 0, j;++	work = rndr_newbuf(rndr, BUFFER_BLOCK);++	while (i < size) {+		j = parse_listitem(work, rndr, data + i, size - i, &flags);+		i += j;++		if (!j || (flags & MKD_LI_END))+			break;+	}++	if (rndr->cb.list)+		rndr->cb.list(ob, work, flags, rndr->opaque);+	rndr_popbuf(rndr, BUFFER_BLOCK);+	return i;+}++/* parse_atxheader • parsing of atx-style headers */+static size_t+parse_atxheader(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)+{+	size_t level = 0;+	size_t i, end, skip;++	while (level < size && level < 6 && data[level] == '#')+		level++;++	for (i = level; i < size && data[i] == ' '; i++);++	for (end = i; end < size && data[end] != '\n'; end++);+	skip = end;++	while (end && data[end - 1] == '#')+		end--;++	while (end && data[end - 1] == ' ')+		end--;++	if (end > i) {+		struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);++		parse_inline(work, rndr, data + i, end - i);++		if (rndr->cb.header)+			rndr->cb.header(ob, work, (int)level, rndr->opaque);++		rndr_popbuf(rndr, BUFFER_SPAN);+	}++	return skip;+}+++/* htmlblock_end • checking end of HTML block : </tag>[ \t]*\n[ \t*]\n */+/*	returns the length on match, 0 otherwise */+static size_t+htmlblock_end_tag(+	const char *tag,+	size_t tag_len,+	struct sd_markdown *rndr,+	uint8_t *data,+	size_t size)+{+	size_t i, w;++	/* checking if tag is a match */+	if (tag_len + 3 >= size ||+		strncasecmp((char *)data + 2, tag, tag_len) != 0 ||+		data[tag_len + 2] != '>')+		return 0;++	/* checking white lines */+	i = tag_len + 3;+	w = 0;+	if (i < size && (w = is_empty(data + i, size - i)) == 0)+		return 0; /* non-blank after tag */+	i += w;+	w = 0;++	if (i < size)+		w = is_empty(data + i, size - i);++	return i + w;+}++static size_t+htmlblock_end(const char *curtag,+	struct sd_markdown *rndr,+	uint8_t *data,+	size_t size,+	int start_of_line)+{+	size_t tag_size = strlen(curtag);+	size_t i = 1, end_tag;+	int block_lines = 0;++	while (i < size) {+		i++;+		while (i < size && !(data[i - 1] == '<' && data[i] == '/')) {+			if (data[i] == '\n')+				block_lines++;++			i++;+		}++		/* If we are only looking for unindented tags, skip the tag+		 * if it doesn't follow a newline.+		 *+		 * The only exception to this is if the tag is still on the+		 * initial line; in that case it still counts as a closing+		 * tag+		 */+		if (start_of_line && block_lines > 0 && data[i - 2] != '\n')+			continue;++		if (i + 2 + tag_size >= size)+			break;++		end_tag = htmlblock_end_tag(curtag, tag_size, rndr, data + i - 1, size - i + 1);+		if (end_tag)+			return i + end_tag - 1;+	}++	return 0;+}+++/* parse_htmlblock • parsing of inline HTML block */+static size_t+parse_htmlblock(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int do_render)+{+	size_t i, j = 0, tag_end;+	const char *curtag = NULL;+	struct buf work = { data, 0, 0, 0 };++	/* identification of the opening tag */+	if (size < 2 || data[0] != '<')+		return 0;++	i = 1;+	while (i < size && data[i] != '>' && data[i] != ' ')+		i++;++	if (i < size)+		curtag = find_block_tag((char *)data + 1, (int)i - 1);++	/* handling of special cases */+	if (!curtag) {++		/* HTML comment, laxist form */+		if (size > 5 && data[1] == '!' && data[2] == '-' && data[3] == '-') {+			i = 5;++			while (i < size && !(data[i - 2] == '-' && data[i - 1] == '-' && data[i] == '>'))+				i++;++			i++;++			if (i < size)+				j = is_empty(data + i, size - i);++			if (j) {+				work.size = i + j;+				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 */+		if (size > 4 && (data[1] == 'h' || data[1] == 'H') && (data[2] == 'r' || data[2] == 'R')) {+			i = 3;+			while (i < size && data[i] != '>')+				i++;++			if (i + 1 < size) {+				i++;+				j = is_empty(data + i, size - i);+				if (j) {+					work.size = i + j;+					if (do_render && rndr->cb.blockhtml)+						rndr->cb.blockhtml(ob, &work, rndr->opaque);+					return work.size;+				}+			}+		}++		/* no special case recognised */+		return 0;+	}++	/* looking for an unindented matching closing tag */+	/*	followed by a blank line */+	tag_end = htmlblock_end(curtag, rndr, data, size, 1);++	/* if not found, trying a second pass looking for indented match */+	/* but not if tag is "ins" or "del" (following original Markdown.pl) */+	if (!tag_end && strcmp(curtag, "ins") != 0 && strcmp(curtag, "del") != 0) {+		tag_end = htmlblock_end(curtag, rndr, data, size, 0);+	}++	if (!tag_end)+		return 0;++	/* the end of the block has been found */+	work.size = tag_end;+	if (do_render && rndr->cb.blockhtml)+		rndr->cb.blockhtml(ob, &work, rndr->opaque);++	return tag_end;+}++static void+parse_table_row(+	struct buf *ob,+	struct sd_markdown *rndr,+	uint8_t *data,+	size_t size,+	size_t columns,+	int *col_data,+	int header_flag)+{+	size_t i = 0, col;+	struct buf *row_work = 0;++	if (!rndr->cb.table_cell || !rndr->cb.table_row)+		return;++	row_work = rndr_newbuf(rndr, BUFFER_SPAN);++	if (i < size && data[i] == '|')+		i++;++	for (col = 0; col < columns && i < size; ++col) {+		size_t cell_start, cell_end;+		struct buf *cell_work;++		cell_work = rndr_newbuf(rndr, BUFFER_SPAN);++		while (i < size && _isspace(data[i]))+			i++;++		cell_start = i;++		while (i < size && data[i] != '|')+			i++;++		cell_end = i - 1;++		while (cell_end > cell_start && _isspace(data[cell_end]))+			cell_end--;++		parse_inline(cell_work, rndr, data + cell_start, 1 + cell_end - cell_start);+		rndr->cb.table_cell(row_work, cell_work, col_data[col] | header_flag, rndr->opaque);++		rndr_popbuf(rndr, BUFFER_SPAN);+		i++;+	}++	for (; col < columns; ++col) {+		struct buf empty_cell = { 0, 0, 0, 0 };+		rndr->cb.table_cell(row_work, &empty_cell, col_data[col] | header_flag, rndr->opaque);+	}++	rndr->cb.table_row(ob, row_work, rndr->opaque);++	rndr_popbuf(rndr, BUFFER_SPAN);+}++static size_t+parse_table_header(+	struct buf *ob,+	struct sd_markdown *rndr,+	uint8_t *data,+	size_t size,+	size_t *columns,+	int **column_data)+{+	int pipes;+	size_t i = 0, col, header_end, under_end;++	pipes = 0;+	while (i < size && data[i] != '\n')+		if (data[i++] == '|')+			pipes++;++	if (i == size || pipes == 0)+		return 0;++	header_end = i;++	while (header_end > 0 && _isspace(data[header_end - 1]))+		header_end--;++	if (data[0] == '|')+		pipes--;++	if (header_end && data[header_end - 1] == '|')+		pipes--;++	*columns = pipes + 1;+	*column_data = calloc(*columns, sizeof(int));++	/* Parse the header underline */+	i++;+	if (i < size && data[i] == '|')+		i++;++	under_end = i;+	while (under_end < size && data[under_end] != '\n')+		under_end++;++	for (col = 0; col < *columns && i < under_end; ++col) {+		size_t dashes = 0;++		while (i < under_end && data[i] == ' ')+			i++;++		if (data[i] == ':') {+			i++; (*column_data)[col] |= MKD_TABLE_ALIGN_L;+			dashes++;+		}++		while (i < under_end && data[i] == '-') {+			i++; dashes++;+		}++		if (i < under_end && data[i] == ':') {+			i++; (*column_data)[col] |= MKD_TABLE_ALIGN_R;+			dashes++;+		}++		while (i < under_end && data[i] == ' ')+			i++;++		if (i < under_end && data[i] != '|')+			break;++		if (dashes < 3)+			break;++		i++;+	}++	if (col < *columns)+		return 0;++	parse_table_row(+		ob, rndr, data,+		header_end,+		*columns,+		*column_data,+		MKD_TABLE_HEADER+	);++	return under_end + 1;+}++static size_t+parse_table(+	struct buf *ob,+	struct sd_markdown *rndr,+	uint8_t *data,+	size_t size)+{+	size_t i;++	struct buf *header_work = 0;+	struct buf *body_work = 0;++	size_t columns;+	int *col_data = NULL;++	header_work = rndr_newbuf(rndr, BUFFER_SPAN);+	body_work = rndr_newbuf(rndr, BUFFER_BLOCK);++	i = parse_table_header(header_work, rndr, data, size, &columns, &col_data);+	if (i > 0) {++		while (i < size) {+			size_t row_start;+			int pipes = 0;++			row_start = i;++			while (i < size && data[i] != '\n')+				if (data[i++] == '|')+					pipes++;++			if (pipes == 0 || i == size) {+				i = row_start;+				break;+			}++			parse_table_row(+				body_work,+				rndr,+				data + row_start,+				i - row_start,+				columns,+				col_data, 0+			);++			i++;+		}++		if (rndr->cb.table)+			rndr->cb.table(ob, header_work, body_work, rndr->opaque);+	}++	free(col_data);+	rndr_popbuf(rndr, BUFFER_SPAN);+	rndr_popbuf(rndr, BUFFER_BLOCK);+	return i;+}++/* parse_block • parsing of one block, returning next uint8_t to parse */+static void+parse_block(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)+{+	size_t beg, end, i;+	uint8_t *txt_data;+	beg = 0;++	if (rndr->work_bufs[BUFFER_SPAN].size ++		rndr->work_bufs[BUFFER_BLOCK].size > rndr->max_nesting)+		return;++	while (beg < size) {+		txt_data = data + beg;+		end = size - beg;++		if (is_atxheader(rndr, txt_data, end))+			beg += parse_atxheader(ob, rndr, txt_data, end);++		else if (data[beg] == '<' && rndr->cb.blockhtml &&+				(i = parse_htmlblock(ob, rndr, txt_data, end, 1)) != 0)+			beg += i;++		else if ((i = is_empty(txt_data, end)) != 0)+			beg += i;++		else if (is_hrule(txt_data, end)) {+			if (rndr->cb.hrule)+				rndr->cb.hrule(ob, rndr->opaque);++			while (beg < size && data[beg] != '\n')+				beg++;++			beg++;+		}++		else if ((rndr->ext_flags & MKDEXT_FENCED_CODE) != 0 &&+			(i = parse_fencedcode(ob, rndr, txt_data, end)) != 0)+			beg += i;++		else if ((rndr->ext_flags & MKDEXT_TABLES) != 0 &&+			(i = parse_table(ob, rndr, txt_data, end)) != 0)+			beg += i;++		else if (prefix_quote(txt_data, end))+			beg += parse_blockquote(ob, rndr, txt_data, end);++		else if (prefix_code(txt_data, end))+			beg += parse_blockcode(ob, rndr, txt_data, end);++		else if (prefix_uli(txt_data, end))+			beg += parse_list(ob, rndr, txt_data, end, 0);++		else if (prefix_oli(txt_data, end))+			beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);++		else+			beg += parse_paragraph(ob, rndr, txt_data, end);+	}+}++++/*********************+ * REFERENCE PARSING *+ *********************/++/* is_ref • returns whether a line is a reference or not */+static int+is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_ref **refs)+{+/*	int n; */+	size_t i = 0;+	size_t id_offset, id_end;+	size_t link_offset, link_end;+	size_t title_offset, title_end;+	size_t line_end;++	/* up to 3 optional leading spaces */+	if (beg + 3 >= end) return 0;+	if (data[beg] == ' ') { i = 1;+	if (data[beg + 1] == ' ') { i = 2;+	if (data[beg + 2] == ' ') { i = 3;+	if (data[beg + 3] == ' ') return 0; } } }+	i += beg;++	/* id part: anything but a newline between brackets */+	if (data[i] != '[') return 0;+	i++;+	id_offset = i;+	while (i < end && data[i] != '\n' && data[i] != '\r' && data[i] != ']')+		i++;+	if (i >= end || data[i] != ']') return 0;+	id_end = i;++	/* spacer: colon (space | tab)* newline? (space | tab)* */+	i++;+	if (i >= end || data[i] != ':') return 0;+	i++;+	while (i < end && data[i] == ' ') i++;+	if (i < end && (data[i] == '\n' || data[i] == '\r')) {+		i++;+		if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }+	while (i < end && data[i] == ' ') i++;+	if (i >= end) return 0;++	/* link: whitespace-free sequence, optionally between angle brackets */+	if (data[i] == '<')+		i++;++	link_offset = i;++	while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r')+		i++;++	if (data[i - 1] == '>') link_end = i - 1;+	else link_end = i;++	/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */+	while (i < end && data[i] == ' ') i++;+	if (i < end && data[i] != '\n' && data[i] != '\r'+			&& data[i] != '\'' && data[i] != '"' && data[i] != '(')+		return 0;+	line_end = 0;+	/* computing end-of-line */+	if (i >= end || data[i] == '\r' || data[i] == '\n') line_end = i;+	if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')+		line_end = i + 1;++	/* optional (space|tab)* spacer after a newline */+	if (line_end) {+		i = line_end + 1;+		while (i < end && data[i] == ' ') i++; }++	/* optional title: any non-newline sequence enclosed in '"()+					alone on its line */+	title_offset = title_end = 0;+	if (i + 1 < end+	&& (data[i] == '\'' || data[i] == '"' || data[i] == '(')) {+		i++;+		title_offset = i;+		/* looking for EOL */+		while (i < end && data[i] != '\n' && data[i] != '\r') i++;+		if (i + 1 < end && data[i] == '\n' && data[i + 1] == '\r')+			title_end = i + 1;+		else	title_end = i;+		/* stepping back */+		i -= 1;+		while (i > title_offset && data[i] == ' ')+			i -= 1;+		if (i > title_offset+		&& (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {+			line_end = title_end;+			title_end = i; } }++	if (!line_end || link_end == link_offset)+		return 0; /* garbage after the link empty link */++	/* a valid ref has been found, filling-in return structures */+	if (last)+		*last = line_end;++	if (refs) {+		struct link_ref *ref;++		ref = add_link_ref(refs, data + id_offset, id_end - id_offset);+		if (!ref)+			return 0;++		ref->link = bufnew(link_end - link_offset);+		bufput(ref->link, data + link_offset, link_end - link_offset);++		if (title_end > title_offset) {+			ref->title = bufnew(title_end - title_offset);+			bufput(ref->title, data + title_offset, title_end - title_offset);+		}+	}++	return 1;+}++static void expand_tabs(struct buf *ob, const uint8_t *line, size_t size)+{+	size_t  i = 0, tab = 0;++	while (i < size) {+		size_t org = i;++		while (i < size && line[i] != '\t') {+			i++; tab++;+		}++		if (i > org)+			bufput(ob, line + org, i - org);++		if (i >= size)+			break;++		do {+			bufputc(ob, ' '); tab++;+		} while (tab % 4);++		i++;+	}+}++/**********************+ * EXPORTED FUNCTIONS *+ **********************/++struct sd_markdown *+sd_markdown_new(+	unsigned int extensions,+	size_t max_nesting,+	const struct sd_callbacks *callbacks,+	void *opaque)+{+	struct sd_markdown *md = NULL;++	assert(max_nesting > 0 && callbacks);++	md = malloc(sizeof(struct sd_markdown));+	if (!md)+		return NULL;++	memcpy(&md->cb, callbacks, sizeof(struct sd_callbacks));++	stack_init(&md->work_bufs[BUFFER_BLOCK], 4);+	stack_init(&md->work_bufs[BUFFER_SPAN], 8);++	memset(md->active_char, 0x0, 256);++	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)+			md->active_char['~'] = MD_CHAR_EMPHASIS;+	}++	if (md->cb.codespan)+		md->active_char['`'] = MD_CHAR_CODESPAN;++	if (md->cb.linebreak)+		md->active_char['\n'] = MD_CHAR_LINEBREAK;++	if (md->cb.image || md->cb.link)+		md->active_char['['] = MD_CHAR_LINK;++	md->active_char['<'] = MD_CHAR_LANGLE;+	md->active_char['\\'] = MD_CHAR_ESCAPE;+	md->active_char['&'] = MD_CHAR_ENTITITY;++	if (extensions & MKDEXT_AUTOLINK) {+		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)+		md->active_char['^'] = MD_CHAR_SUPERSCRIPT;++	/* Extension data */+	md->ext_flags = extensions;+	md->opaque = opaque;+	md->max_nesting = max_nesting;+	md->in_link_body = 0;++	return md;+}++void+sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md)+{+#define MARKDOWN_GROW(x) ((x) + ((x) >> 1))+	static const char UTF8_BOM[] = {0xEF, 0xBB, 0xBF};++	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, doc_size);++	/* reset the references table */+	memset(&md->refs, 0x0, REF_TABLE_SIZE * sizeof(void *));++	/* first pass: looking for references, copying everything else */+	beg = 0;++	/* Skip a possible UTF-8 BOM, even though the Unicode standard+	 * discourages having these in UTF-8 documents */+	if (doc_size >= 3 && memcmp(document, UTF8_BOM, 3) == 0)+		beg += 3;++	while (beg < doc_size) /* iterating over lines */+		if (is_ref(document, beg, doc_size, &end, md->refs))+			beg = end;+		else { /* skipping to the next line */+			end = beg;+			while (end < doc_size && document[end] != '\n' && document[end] != '\r')+				end++;++			/* adding the line body if present */+			if (end > beg)+				expand_tabs(text, document + beg, end - beg);++			while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) {+				/* add one \n per newline */+				if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n'))+					bufputc(text, '\n');+				end++;+			}++			beg = end;+		}++	/* pre-grow the output buffer to minimize allocations */+	bufgrow(ob, MARKDOWN_GROW(text->size));++	/* second pass: actual rendering */+	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, md, text->data, text->size);+	}++	if (md->cb.doc_footer)+		md->cb.doc_footer(ob, md->opaque);++	/* clean-up */+	bufrelease(text);+	free_link_refs(md->refs);++	assert(md->work_bufs[BUFFER_SPAN].size == 0);+	assert(md->work_bufs[BUFFER_BLOCK].size == 0);+}++void+sd_markdown_free(struct sd_markdown *md)+{+	size_t 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)md->work_bufs[BUFFER_BLOCK].asize; ++i)+		bufrelease(md->work_bufs[BUFFER_BLOCK].item[i]);++	stack_free(&md->work_bufs[BUFFER_SPAN]);+	stack_free(&md->work_bufs[BUFFER_BLOCK]);++	free(md);+}++void+sd_version(int *ver_major, int *ver_minor, int *ver_revision)+{+	*ver_major = SUNDOWN_VER_MAJOR;+	*ver_minor = SUNDOWN_VER_MINOR;+	*ver_revision = SUNDOWN_VER_REVISION;+}++/* vim: set filetype=c: */
+ sundown-c/markdown.h view
@@ -0,0 +1,138 @@+/* markdown.h - generic markdown parser */++/*+ * Copyright (c) 2009, 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 UPSKIRT_MARKDOWN_H+#define UPSKIRT_MARKDOWN_H++#include "buffer.h"+#include "autolink.h"++#ifdef __cplusplus+extern "C" {+#endif++#define SUNDOWN_VERSION "1.16.0"+#define SUNDOWN_VER_MAJOR 1+#define SUNDOWN_VER_MINOR 16+#define SUNDOWN_VER_REVISION 0++/********************+ * TYPE DEFINITIONS *+ ********************/++/* mkd_autolink - type of autolink */+enum mkd_autolink {+	MKDA_NOT_AUTOLINK,	/* used internally when it is not an autolink*/+	MKDA_NORMAL,		/* normal http/http/ftp/mailto/etc link */+	MKDA_EMAIL,			/* e-mail link without explit mailto: */+};++enum mkd_tableflags {+	MKD_TABLE_ALIGN_L = 1,+	MKD_TABLE_ALIGN_R = 2,+	MKD_TABLE_ALIGN_CENTER = 3,+	MKD_TABLE_ALIGNMASK = 3,+	MKD_TABLE_HEADER = 4+};++enum mkd_extensions {+	MKDEXT_NO_INTRA_EMPHASIS = (1 << 0),+	MKDEXT_TABLES = (1 << 1),+	MKDEXT_FENCED_CODE = (1 << 2),+	MKDEXT_AUTOLINK = (1 << 3),+	MKDEXT_STRIKETHROUGH = (1 << 4),+	MKDEXT_SPACE_HEADERS = (1 << 6),+	MKDEXT_SUPERSCRIPT = (1 << 7),+	MKDEXT_LAX_SPACING = (1 << 8),+};++/* sd_callbacks - functions for rendering parsed data */+struct sd_callbacks {+	/* block level callbacks - NULL skips the block */+	void (*blockcode)(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque);+	void (*blockquote)(struct buf *ob, const struct buf *text, void *opaque);+	void (*blockhtml)(struct buf *ob,const  struct buf *text, void *opaque);+	void (*header)(struct buf *ob, const struct buf *text, int level, void *opaque);+	void (*hrule)(struct buf *ob, void *opaque);+	void (*list)(struct buf *ob, const struct buf *text, int flags, void *opaque);+	void (*listitem)(struct buf *ob, const struct buf *text, int flags, void *opaque);+	void (*paragraph)(struct buf *ob, const struct buf *text, void *opaque);+	void (*table)(struct buf *ob, const struct buf *header, const struct buf *body, void *opaque);+	void (*table_row)(struct buf *ob, const struct buf *text, void *opaque);+	void (*table_cell)(struct buf *ob, const struct buf *text, int flags, void *opaque);+++	/* span level callbacks - NULL or return 0 prints the span verbatim */+	int (*autolink)(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque);+	int (*codespan)(struct buf *ob, const struct buf *text, void *opaque);+	int (*double_emphasis)(struct buf *ob, const struct buf *text, void *opaque);+	int (*emphasis)(struct buf *ob, const struct buf *text, void *opaque);+	int (*image)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque);+	int (*linebreak)(struct buf *ob, void *opaque);+	int (*link)(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque);+	int (*raw_html_tag)(struct buf *ob, const struct buf *tag, void *opaque);+	int (*triple_emphasis)(struct buf *ob, const struct buf *text, void *opaque);+	int (*strikethrough)(struct buf *ob, const struct buf *text, void *opaque);+	int (*superscript)(struct buf *ob, const struct buf *text, void *opaque);++	/* low level callbacks - NULL copies input directly into the output */+	void (*entity)(struct buf *ob, const struct buf *entity, void *opaque);+	void (*normal_text)(struct buf *ob, const struct buf *text, void *opaque);++	/* header and footer */+	void (*doc_header)(struct buf *ob, void *opaque);+	void (*doc_footer)(struct buf *ob, void *opaque);+};++struct sd_markdown;++/*********+ * FLAGS *+ *********/++/* list/listitem flags */+#define MKD_LIST_ORDERED	1+#define MKD_LI_BLOCK		2  /* <li> containing block data */++/**********************+ * EXPORTED FUNCTIONS *+ **********************/++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_render(struct buf *ob, const uint8_t *document, size_t doc_size, struct sd_markdown *md);++extern void+sd_markdown_free(struct sd_markdown *md);++extern void+sd_version(int *major, int *minor, int *revision);++#ifdef __cplusplus+}+#endif++#endif++/* vim: set filetype=c: */
+ sundown-c/stack.c view
@@ -0,0 +1,81 @@+#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;++	memset(new_st + st->asize, 0x0,+		(new_size - st->asize) * sizeof(void *));++	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];+}+
+ sundown-c/stack.h view
@@ -0,0 +1,29 @@+#ifndef STACK_H__+#define STACK_H__++#include <stdlib.h>++#ifdef __cplusplus+extern "C" {+#endif++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 *);++#ifdef __cplusplus+}+#endif++#endif
sundown.cabal view
@@ -1,6 +1,6 @@ Cabal-version:      >= 1.6 Name:               sundown-Version:            0.4.2+Version:            0.5 Author:             Francesco Mazzoli (f@mazzo.li) Maintainer:         Francesco Mazzoli (f@mazzo.li) Build-Type:         Simple@@ -12,36 +12,40 @@ Homepage:           https://github.com/bitonic/sundown Bug-Reports:        https://github.com/bitonic/sundown/issues Description:-  Bindings to GitHub's C markdown library: <https://github.com/tanoku/sundown>+    Bindings to GitHub's C markdown library: <https://github.com/tanoku/sundown>.+    . +    The Haskell code is in the public domain. For sundown's license, refer to+    its GitHub page.  source-repository head-  type:     git-  location: git://github.com/bitonic/sundown.git+    type:     git+    location: git://github.com/bitonic/sundown.git  Library-  Hs-Source-Dirs:   src-  Build-Depends:    base >= 3 && < 5, bytestring-  GHC-Options:      -Wall -O2 -fno-cse-  Extensions:       ForeignFunctionInterface, EmptyDataDecls+    Hs-Source-Dirs:   src+    Build-Depends:    base >= 3 && < 5, bytestring, utf8-string, text+    GHC-Options:      -Wall -O2 -fno-cse+    Extensions:       ForeignFunctionInterface, EmptyDataDecls -  Exposed-Modules:  Text.Sundown,-                    Text.Sundown.Markdown,-                    Text.Sundown.Renderers.Html+    Exposed-Modules:  Text.Sundown,+                      Text.Sundown.Html.ByteString,+                      Text.Sundown.Html.String,+                      Text.Sundown.Html.Text -  Other-Modules:    Text.Sundown.Buffer.Foreign,-                    Text.Sundown.Renderers.Html.Foreign,-                    Text.Sundown.Flag,-                    Text.Sundown.Markdown.Foreign+    Other-Modules:    Text.Sundown.Buffer.Foreign,+                      Text.Sundown.Html.Foreign,+                      Text.Sundown.Flag,+                      Text.Sundown.Foreign -  C-Sources:        cbits/autolink.c, cbits/buffer.c,-                    cbits/houdini_href_e.c, cbits/houdini_html_e.c,-                    cbits/html.c, cbits/html_smartypants.c,-                    cbits/markdown.c, cbits/stack.c+    C-Sources:        sundown-c/autolink.c, sundown-c/buffer.c,+                      sundown-c/houdini_href_e.c, sundown-c/houdini_html_e.c,+                      sundown-c/html.c, sundown-c/html_smartypants.c,+                      sundown-c/markdown.c, sundown-c/stack.c -  Include-Dirs:     cbits+    Include-Dirs:     sundown-c -  Includes:         autolink.h, buffer.h, html.h, html_blocks.h, markdown.h,-                    stack.h+    Includes:         autolink.h, buffer.h, html.h, html_blocks.h, markdown.h,+                      stack.h -  Install-includes: autolink.h, buffer.h, houdini.h, html.h, html_blocks.h,-                    markdown.h, stack.h+    Install-includes: autolink.h, buffer.h, houdini.h, html.h, html_blocks.h,+                      markdown.h, stack.h