else/SnudownTest/snudown-validator.c

227 lines
5.9 KiB
C

#include "markdown.h"
#include "html.h"
#include "buffer.h"
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <gumbo.h>
#define READ_UNIT 1024
#define OUTPUT_UNIT 64
#include "autolink.h"
#define SNUDOWN_VERSION "1.3.2"
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;
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() {
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() {
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;
}
void
snudown_md(struct buf *ob, const uint8_t *document, size_t doc_size, int wiki_mode)
{
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;
if (wiki_mode)
renderer = RENDERER_WIKI;
_snudown = sundown[renderer];
struct snudown_renderopt *options = &(_snudown.state->options);
options->nofollow = nofollow;
options->target = target;
flags = options->html.flags;
if (enable_toc) {
_snudown.toc_state->options.html.toc_id_prefix = toc_id_prefix;
sd_markdown_render(ob, document, doc_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, document, doc_size, _snudown.main_renderer);
options->html.toc_id_prefix = NULL;
options->html.flags = flags;
}
int
main(int argc, char **argv)
{
init_default_renderer();
init_wiki_renderer();
struct buf *ib, *ob;
int size_read = 0, wiki_mode = 0, i = 0, have_errors = 0;
/* reading everything */
ib = bufnew(READ_UNIT);
bufgrow(ib, READ_UNIT);
while ((size_read = fread(ib->data + ib->size, 1, ib->asize - ib->size, stdin)) > 0) {
ib->size += size_read;
bufgrow(ib, ib->size + READ_UNIT);
}
/* Render to a buffer, then print that out */
ob = bufnew(OUTPUT_UNIT);
bufputs(ob, "<!DOCTYPE html><html><body>\n");
snudown_md(ob, ib->data, ib->size, wiki_mode);
bufputs(ob, "</body></html>\n");
// Wiki mode explicitly allows unbalanced tags, need some way to exclude those
if (!wiki_mode) {
GumboOutput* output = gumbo_parse_with_options(&kGumboDefaultOptions, bufcstr(ob), ob->size);
for (i=0; i < output->errors.length; ++i) {
// stupid "public" API I hacked in.
void* thing = output->errors.data[i];
GumboErrorType type = gumbo_get_error_type(thing);
switch(type) {
case GUMBO_ERR_UTF8_INVALID:
case GUMBO_ERR_UTF8_NULL:
// Making sure the user gave us valid
// utf-8 or transforming it to valid
// utf-8 is outside the scope of snudown
continue;
default:
have_errors = 1;
printf("%s\n", GUMBO_ERROR_NAMES[type]);
printf("%s\n",gumbo_get_error_text(thing));
printf("===============\n");
break;
}
}
if (have_errors) {
// gotta trigger a crash for AFL to catch it
assert(0);
}
gumbo_destroy_output(&kGumboDefaultOptions, output);
}
bufrelease(ob);
bufrelease(ib);
return 0;
}