diff --git a/html/html.c b/html/html.c
index 51dbae1..e94e8b2 100755
--- a/html/html.c
+++ b/html/html.c
@@ -222,10 +222,15 @@ rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)
if (ob->size)
bufputc(ob, '\n');
- if (options->flags & HTML_TOC)
- bufprintf(ob, "", level, options->toc_data.header_count++);
- else
+ if (options->flags & HTML_TOC) {
+ bufprintf(ob, "toc_id_prefix) {
+ bufprintf(ob, options->toc_id_prefix);
+ }
+ bufprintf(ob, "toc_%d\">", options->toc_data.header_count++);
+ } else {
bufprintf(ob, "", level);
+ }
if (text) bufput(ob, text->data, text->size);
bufprintf(ob, "\n", level);
@@ -382,10 +387,121 @@ rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, cons
return 1;
}
- int
+static void
+rndr_html_tag(struct buf *ob, const struct buf *text, void *opaque,
+ char* tagname, char** whitelist, int tagtype)
+{
+ size_t i, x, z, in_str = 0, seen_equals = 0, done, reset;
+ struct buf *attr = bufnew(16);
+ char c;
+
+ bufputc(ob, '<');
+
+ i = 1 + strlen(tagname);
+
+ if(tagtype == HTML_TAG_CLOSE) {
+ bufputc(ob, '/');
+ i += 1;
+ }
+
+ bufputs(ob, tagname);
+
+ if(tagtype != HTML_TAG_CLOSE) {
+ for(;i < text->size;i++) {
+ c = text->data[i];
+ done = 0;
+ reset = 0;
+
+ switch(c) {
+ case '>':
+ if(seen_equals && !in_str) {
+ done = 1;
+ reset = 1;
+ } else {
+ reset = 1;
+ }
+ break;
+ case '\'':
+ case '"':
+ if(!in_str)
+ in_str = c;
+ else if(in_str == c)
+ in_str = !in_str;
+ break;
+ default:
+ if(!in_str) {
+ switch(c) {
+ case ' ':
+ if(seen_equals) {
+ done = 1;
+ reset = 1;
+ } else
+ reset = 1;
+ break;
+ case '=':
+ if(seen_equals) {
+ reset = 1;
+ } else {
+ for(z=0; whitelist[z]; z++) {
+ if(strlen(whitelist[z]) != attr->size)
+ continue;
+ for(x=0;x < attr->size; x++) {
+ if(tolower(whitelist[z][x]) != tolower(attr->data[x]))
+ break;
+ }
+ if(x == attr->size)
+ seen_equals = 1;
+ }
+ if(!seen_equals)
+ reset = 1;
+ }
+ break;
+ }
+ }
+ }
+
+ if(done) {
+ bufputc(ob, ' ');
+ bufput(ob, attr->data, attr->size);
+ }
+
+ if(reset) {
+ seen_equals = 0;
+ in_str = 0;
+ bufreset(attr);
+ } else {
+ bufputc(attr, c);
+ }
+ }
+ }
+
+ bufrelease(attr);
+
+ bufputc(ob, '>');
+
+}
+
+static int
rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)
{
struct html_renderopt *options = opaque;
+ char** whitelist = options->html_element_whitelist;
+ int i, tagtype;
+
+
+ /* Items on the whitelist ignore all other flags and just output */
+ if (((options->flags & HTML_ALLOW_ELEMENT_WHITELIST) != 0) && whitelist) {
+ for(i = 0; whitelist[i]; i++) {
+ tagtype = sdhtml_is_tag(text->data, text->size, whitelist[i]);
+ if(tagtype != HTML_TAG_NONE) {
+ rndr_html_tag(ob, text, opaque,
+ whitelist[i],
+ options->html_attr_whitelist,
+ tagtype);
+ return 1;
+ }
+ }
+ }
/* 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. */
@@ -516,7 +632,13 @@ toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
BUFPUTSL(ob,"\n\n");
}
- bufprintf(ob, "", options->toc_data.header_count++);
+ bufprintf(ob, "toc_id_prefix) {
+ bufprintf(ob, options->toc_id_prefix);
+ }
+
+ bufprintf(ob, "toc_%d\">", options->toc_data.header_count++);
if (text)
escape_html(ob, text->data, text->size);
BUFPUTSL(ob, "\n");
@@ -530,6 +652,14 @@ toc_link(struct buf *ob, const struct buf *link, const struct buf *title, const
return 1;
}
+static void
+reset_toc(struct buf *ob, void *opaque)
+{
+ struct html_renderopt *options = opaque;
+
+ memset(&(options->toc_data), 0, sizeof(options->toc_data));
+}
+
static void
toc_finalize(struct buf *ob, void *opaque)
{
@@ -539,6 +669,8 @@ toc_finalize(struct buf *ob, void *opaque)
BUFPUTSL(ob, "\n\n");
options->toc_data.current_level--;
}
+
+ reset_toc(ob, opaque);
}
void
@@ -577,7 +709,7 @@ sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *optio
};
memset(options, 0x0, sizeof(struct html_renderopt));
- options->flags = HTML_TOC;
+ options->flags = HTML_TOC | HTML_SKIP_HTML;
memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
}
@@ -614,7 +746,7 @@ sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options,
rndr_normal_text,
NULL,
- NULL,
+ reset_toc,
};
/* Prepare the options pointer */
diff --git a/html/html.h b/html/html.h
index 4c8810d..105a8d5 100644
--- a/html/html.h
+++ b/html/html.h
@@ -32,7 +32,12 @@ struct html_renderopt {
int level_offset;
} toc_data;
+ char* toc_id_prefix;
+
unsigned int flags;
+
+ char** html_element_whitelist;
+ char** html_attr_whitelist;
/* extra callbacks */
void (*link_attributes)(struct buf *ob, const struct buf *url, void *self);
@@ -49,6 +54,7 @@ typedef enum {
HTML_HARD_WRAP = (1 << 7),
HTML_USE_XHTML = (1 << 8),
HTML_ESCAPE = (1 << 9),
+ HTML_ALLOW_ELEMENT_WHITELIST = (1 << 10),
} html_render_mode;
typedef enum {
diff --git a/snudown.c b/snudown.c
index b19cf76..38e249e 100644
--- a/snudown.c
+++ b/snudown.c
@@ -7,36 +7,64 @@
#define SNUDOWN_VERSION "1.0.6"
+enum snudown_renderer_mode {
+ RENDERER_USERTEXT = 0,
+ RENDERER_WIKI,
+ RENDERER_COUNT
+};
+
struct snudown_renderopt {
struct html_renderopt html;
int nofollow;
const char *target;
};
-static struct module_state {
+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;
-} _state;
+};
+
+static struct snudown_renderer sundown[RENDERER_COUNT];
-static struct sd_markdown* sundown = NULL;
+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 int snudown_md_flags =
+static const unsigned int snudown_default_md_flags =
MKDEXT_NO_INTRA_EMPHASIS |
MKDEXT_SUPERSCRIPT |
MKDEXT_AUTOLINK |
MKDEXT_STRIKETHROUGH |
MKDEXT_TABLES;
-static const int snudown_render_flags =
+static const unsigned int snudown_default_render_flags =
HTML_SKIP_HTML |
HTML_SKIP_IMAGES |
HTML_SAFELINK |
- HTML_ESCAPE |
- HTML_USE_XHTML;
+ 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)
@@ -53,29 +81,104 @@ snudown_link_attr(struct buf *ob, const struct buf *link, void *opaque)
}
}
+static struct sd_markdown* custom_render(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,
+ &state->callbacks,
+ &state->options
+ );
+}
+
+void init_default_render(PyObject *module) {
+ PyModule_AddIntConstant(module, "RENDERER_USERTEXT", RENDERER_USERTEXT);
+ sundown[RENDERER_USERTEXT].main_renderer = custom_render(&usertext_state, snudown_default_render_flags, snudown_default_md_flags, 0);
+ sundown[RENDERER_USERTEXT].toc_renderer = custom_render(&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_render(PyObject *module) {
+ PyModule_AddIntConstant(module, "RENDERER_WIKI", RENDERER_WIKI);
+ sundown[RENDERER_WIKI].main_renderer = custom_render(&wiki_state, snudown_wiki_render_flags, snudown_default_md_flags, 0);
+ sundown[RENDERER_WIKI].toc_renderer = custom_render(&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", NULL};
+ 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#|iz", kwlist,
- &ib.data, &ib.size, &_state.options.nofollow, &_state.options.target)) {
+ 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, sundown);
-
+ 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)
@@ -99,22 +202,10 @@ PyMODINIT_FUNC initsnudown(void)
module = Py_InitModule3("snudown", snudown_methods, snudown_module__doc__);
if (module == NULL)
return;
-
- /* initialize the html renderer */
- sdhtml_renderer(&_state.callbacks,
- (struct html_renderopt *)&_state.options,
- snudown_render_flags);
-
- _state.options.html.link_attributes = &snudown_link_attr;
-
- /* initialize the markdown parser */
- sundown = sd_markdown_new(
- snudown_md_flags,
- 16,
- &_state.callbacks,
- &_state.options
- );
-
+
+ init_default_render(module);
+ init_wiki_render(module);
+
/* Version */
PyModule_AddStringConstant(module, "__version__", SNUDOWN_VERSION);
}