213 lines
5.8 KiB
C
213 lines
5.8 KiB
C
|
#define PY_SSIZE_T_CLEAN
|
||
|
#include <Python.h>
|
||
|
|
||
|
#include "markdown.h"
|
||
|
#include "html.h"
|
||
|
#include "autolink.h"
|
||
|
|
||
|
#define SNUDOWN_VERSION "1.4.0"
|
||
|
|
||
|
enum snudown_renderer_mode {
|
||
|
RENDERER_USERTEXT = 0,
|
||
|
RENDERER_WIKI,
|
||
|
RENDERER_COUNT
|
||
|
};
|
||
|
|
||
|
struct snudown_renderopt {
|
||
|
struct html_renderopt html;
|
||
|
int nofollow;
|
||
|
const char *target;
|
||
|
};
|
||
|
|
||
|
struct snudown_renderer {
|
||
|
struct sd_markdown* main_renderer;
|
||
|
struct sd_markdown* toc_renderer;
|
||
|
struct module_state* state;
|
||
|
struct module_state* toc_state;
|
||
|
};
|
||
|
|
||
|
struct module_state {
|
||
|
struct sd_callbacks callbacks;
|
||
|
struct snudown_renderopt options;
|
||
|
};
|
||
|
|
||
|
static struct snudown_renderer sundown[RENDERER_COUNT];
|
||
|
|
||
|
static char* html_element_whitelist[] = {"tr", "th", "td", "table", "tbody", "thead", "tfoot", "caption", NULL};
|
||
|
static char* html_attr_whitelist[] = {"colspan", "rowspan", "cellspacing", "cellpadding", "scope", NULL};
|
||
|
|
||
|
static struct module_state usertext_toc_state;
|
||
|
static struct module_state wiki_toc_state;
|
||
|
static struct module_state usertext_state;
|
||
|
static struct module_state wiki_state;
|
||
|
|
||
|
/* The module doc strings */
|
||
|
PyDoc_STRVAR(snudown_module__doc__, "When does the narwhal bacon? At Sundown.");
|
||
|
PyDoc_STRVAR(snudown_md__doc__, "Render a Markdown document");
|
||
|
|
||
|
static const unsigned int snudown_default_md_flags =
|
||
|
MKDEXT_NO_INTRA_EMPHASIS |
|
||
|
MKDEXT_SUPERSCRIPT |
|
||
|
MKDEXT_AUTOLINK |
|
||
|
MKDEXT_STRIKETHROUGH |
|
||
|
MKDEXT_TABLES;
|
||
|
|
||
|
static const unsigned int snudown_default_render_flags =
|
||
|
HTML_SKIP_HTML |
|
||
|
HTML_SKIP_IMAGES |
|
||
|
HTML_SAFELINK |
|
||
|
HTML_ESCAPE |
|
||
|
HTML_USE_XHTML;
|
||
|
|
||
|
static const unsigned int snudown_wiki_render_flags =
|
||
|
HTML_SKIP_HTML |
|
||
|
HTML_SAFELINK |
|
||
|
HTML_ALLOW_ELEMENT_WHITELIST |
|
||
|
HTML_ESCAPE |
|
||
|
HTML_USE_XHTML;
|
||
|
|
||
|
static void
|
||
|
snudown_link_attr(struct buf *ob, const struct buf *link, void *opaque)
|
||
|
{
|
||
|
struct snudown_renderopt *options = opaque;
|
||
|
|
||
|
if (options->nofollow)
|
||
|
BUFPUTSL(ob, " rel=\"nofollow\"");
|
||
|
|
||
|
if (options->target != NULL) {
|
||
|
BUFPUTSL(ob, " target=\"");
|
||
|
bufputs(ob, options->target);
|
||
|
bufputc(ob, '\"');
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static struct sd_markdown* make_custom_renderer(struct module_state* state,
|
||
|
const unsigned int renderflags,
|
||
|
const unsigned int markdownflags,
|
||
|
int toc_renderer) {
|
||
|
if(toc_renderer) {
|
||
|
sdhtml_toc_renderer(&state->callbacks,
|
||
|
(struct html_renderopt *)&state->options);
|
||
|
} else {
|
||
|
sdhtml_renderer(&state->callbacks,
|
||
|
(struct html_renderopt *)&state->options,
|
||
|
renderflags);
|
||
|
}
|
||
|
|
||
|
state->options.html.link_attributes = &snudown_link_attr;
|
||
|
state->options.html.html_element_whitelist = html_element_whitelist;
|
||
|
state->options.html.html_attr_whitelist = html_attr_whitelist;
|
||
|
|
||
|
return sd_markdown_new(
|
||
|
markdownflags,
|
||
|
16,
|
||
|
64,
|
||
|
&state->callbacks,
|
||
|
&state->options
|
||
|
);
|
||
|
}
|
||
|
|
||
|
void init_default_renderer(PyObject *module) {
|
||
|
PyModule_AddIntConstant(module, "RENDERER_USERTEXT", RENDERER_USERTEXT);
|
||
|
sundown[RENDERER_USERTEXT].main_renderer = make_custom_renderer(&usertext_state, snudown_default_render_flags, snudown_default_md_flags, 0);
|
||
|
sundown[RENDERER_USERTEXT].toc_renderer = make_custom_renderer(&usertext_toc_state, snudown_default_render_flags, snudown_default_md_flags, 1);
|
||
|
sundown[RENDERER_USERTEXT].state = &usertext_state;
|
||
|
sundown[RENDERER_USERTEXT].toc_state = &usertext_toc_state;
|
||
|
}
|
||
|
|
||
|
void init_wiki_renderer(PyObject *module) {
|
||
|
PyModule_AddIntConstant(module, "RENDERER_WIKI", RENDERER_WIKI);
|
||
|
sundown[RENDERER_WIKI].main_renderer = make_custom_renderer(&wiki_state, snudown_wiki_render_flags, snudown_default_md_flags, 0);
|
||
|
sundown[RENDERER_WIKI].toc_renderer = make_custom_renderer(&wiki_toc_state, snudown_wiki_render_flags, snudown_default_md_flags, 1);
|
||
|
sundown[RENDERER_WIKI].state = &wiki_state;
|
||
|
sundown[RENDERER_WIKI].toc_state = &wiki_toc_state;
|
||
|
}
|
||
|
|
||
|
static PyObject *
|
||
|
snudown_md(PyObject *self, PyObject *args, PyObject *kwargs)
|
||
|
{
|
||
|
static char *kwlist[] = {"text", "nofollow", "target", "toc_id_prefix", "renderer", "enable_toc", NULL};
|
||
|
|
||
|
struct buf ib, *ob;
|
||
|
PyObject *py_result;
|
||
|
const char* result_text;
|
||
|
int renderer = RENDERER_USERTEXT;
|
||
|
int enable_toc = 0;
|
||
|
struct snudown_renderer _snudown;
|
||
|
int nofollow = 0;
|
||
|
char* target = NULL;
|
||
|
char* toc_id_prefix = NULL;
|
||
|
unsigned int flags;
|
||
|
|
||
|
memset(&ib, 0x0, sizeof(struct buf));
|
||
|
|
||
|
/* Parse arguments */
|
||
|
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "s#|izzii", kwlist,
|
||
|
&ib.data, &ib.size, &nofollow,
|
||
|
&target, &toc_id_prefix, &renderer, &enable_toc)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
if (renderer < 0 || renderer >= RENDERER_COUNT) {
|
||
|
PyErr_SetString(PyExc_ValueError, "Invalid renderer");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
_snudown = sundown[renderer];
|
||
|
|
||
|
struct snudown_renderopt *options = &(_snudown.state->options);
|
||
|
options->nofollow = nofollow;
|
||
|
options->target = target;
|
||
|
|
||
|
/* Output buffer */
|
||
|
ob = bufnew(128);
|
||
|
|
||
|
flags = options->html.flags;
|
||
|
|
||
|
if (enable_toc) {
|
||
|
_snudown.toc_state->options.html.toc_id_prefix = toc_id_prefix;
|
||
|
sd_markdown_render(ob, ib.data, ib.size, _snudown.toc_renderer);
|
||
|
_snudown.toc_state->options.html.toc_id_prefix = NULL;
|
||
|
|
||
|
options->html.flags |= HTML_TOC;
|
||
|
}
|
||
|
|
||
|
options->html.toc_id_prefix = toc_id_prefix;
|
||
|
|
||
|
/* do the magic */
|
||
|
sd_markdown_render(ob, ib.data, ib.size, _snudown.main_renderer);
|
||
|
|
||
|
options->html.toc_id_prefix = NULL;
|
||
|
options->html.flags = flags;
|
||
|
|
||
|
/* make a Python string */
|
||
|
result_text = "";
|
||
|
if (ob->data)
|
||
|
result_text = (const char*)ob->data;
|
||
|
py_result = Py_BuildValue("s#", result_text, (int)ob->size);
|
||
|
|
||
|
/* Cleanup */
|
||
|
bufrelease(ob);
|
||
|
return py_result;
|
||
|
}
|
||
|
|
||
|
static PyMethodDef snudown_methods[] = {
|
||
|
{"markdown", (PyCFunction) snudown_md, METH_VARARGS | METH_KEYWORDS, snudown_md__doc__},
|
||
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||
|
};
|
||
|
|
||
|
PyMODINIT_FUNC initsnudown(void)
|
||
|
{
|
||
|
PyObject *module;
|
||
|
|
||
|
module = Py_InitModule3("snudown", snudown_methods, snudown_module__doc__);
|
||
|
if (module == NULL)
|
||
|
return;
|
||
|
|
||
|
init_default_renderer(module);
|
||
|
init_wiki_renderer(module);
|
||
|
|
||
|
/* Version */
|
||
|
PyModule_AddStringConstant(module, "__version__", SNUDOWN_VERSION);
|
||
|
}
|