Add sundown

main
cel 5 years ago
parent 8d4ea17bf6
commit c9fad8dcbb

@ -0,0 +1,297 @@
/*
* 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 max_rewind, 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 max_rewind,
size_t size,
unsigned int flags)
{
size_t link_end;
if (max_rewind > 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, max_rewind, 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 max_rewind,
size_t size,
unsigned int flags)
{
size_t link_end, rewind;
int nb = 0, np = 0;
for (rewind = 0; rewind < max_rewind; ++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 ||
!isalpha(data[link_end - 1]))
return 0;
link_end = autolink_delim(data, link_end, max_rewind, 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 max_rewind,
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 < max_rewind && 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, max_rewind, size);
if (link_end == 0)
return 0;
bufput(link, data - rewind, link_end + rewind);
*rewind_p = rewind;
return link_end;
}

@ -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: */

@ -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);
}

@ -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

@ -0,0 +1,37 @@
#ifndef HOUDINI_H__
#define HOUDINI_H__
#include "buffer.h"
#ifdef __cplusplus
extern "C" {
#endif
#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);
#ifdef __cplusplus
}
#endif
#endif

@ -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++;
}
}

@ -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);
}

@ -0,0 +1,635 @@
/*
* 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;
/* set the level offset if this is the first header
* we're parsing for the document */
if (options->toc_data.current_level == 0) {
options->toc_data.level_offset = level - 1;
}
level -= options->toc_data.level_offset;
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)
escape_html(ob, text->data, text->size);
BUFPUTSL(ob, "</a>\n");
}
static int
toc_link(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *content, void *opaque)
{
if (content && content->size)
bufput(ob, content->data, content->size);
return 1;
}
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,
toc_link,
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;
}

@ -0,0 +1,77 @@
/*
* 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>
#ifdef __cplusplus
extern "C" {
#endif
struct html_renderopt {
struct {
int header_count;
int current_level;
int level_offset;
} 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);
#ifdef __cplusplus
}
#endif
#endif

@ -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;
}

@ -0,0 +1,389 @@
/*
* 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>
#if defined(_WIN32)
#define snprintf _snprintf
#endif
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);
}
}
}

File diff suppressed because it is too large Load Diff

@ -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: */

@ -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];
}

@ -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
Loading…
Cancel
Save