#define PY_SSIZE_T_CLEAN #include #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) { struct snudown_renderopt *options; 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]; _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 PyInit_snudown(void) { PyObject *module; struct PyModuleDef wtf = { PyModuleDef_HEAD_INIT, "snudown", /* m_name */ "This is snudown", /* m_doc */ -1, /* m_size */ snudown_methods, /* m_methods */ NULL, /* m_reload */ NULL, /* m_traverse */ NULL, /* m_clear */ NULL, /* m_free */ }; //module = Py_InitModule3("snudown", snudown_methods, snudown_module__doc__); module = PyModule_Create(&wtf); if (module == NULL) return Py_BuildValue(""); init_default_renderer(module); init_wiki_renderer(module); /* Version */ PyModule_AddStringConstant(module, "__version__", SNUDOWN_VERSION); }; void initsnudown(void) { (void) PyInit_snudown("snudown", snudown_methods); };