/* * 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 "xhtml.h" #include #include #include struct toc_data { int header_count; int current_level; }; static int is_safe_link(const char *link, size_t link_len) { static const size_t valid_uris_count = 4; 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 && memcmp(link, valid_uris[i], len) == 0) return 1; } return 0; } static inline int put_scaped_char(struct buf *ob, char c) { switch (c) { case '<': BUFPUTSL(ob, "<"); return 1; case '>': BUFPUTSL(ob, ">"); return 1; case '&': BUFPUTSL(ob, "&"); return 1; case '"': BUFPUTSL(ob, """); return 1; default: return 0; } } /* lus_attr_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */ static void lus_attr_escape(struct buf *ob, const char *src, size_t size) { size_t i = 0, org; while (i < size) { /* copying directly unescaped characters */ org = i; while (i < size && src[i] != '<' && src[i] != '>' && src[i] != '&' && src[i] != '"') i += 1; if (i > org) bufput(ob, src + org, i - org); /* escaping */ if (i >= size) break; put_scaped_char(ob, src[i]); i++; } } static int is_html_tag(struct buf *tag, const char *tagname) { size_t i; for (i = 0; i < tag->size; ++i) { if (tagname[i] == '>') break; if (tag->data[i] != tagname[i]) return 0; } return (i == tag->size || isspace(tag->data[i]) || tag->data[i] == '>'); } /******************** * GENERIC RENDERER * ********************/ static void rndr_autolink2(struct buf *ob, const char *link, size_t link_size, enum mkd_autolink type) { BUFPUTSL(ob, ""); if (type == MKDA_EXPLICIT_EMAIL && link_size > 7) lus_attr_escape(ob, link + 7, link_size - 7); else lus_attr_escape(ob, link, link_size); BUFPUTSL(ob, ""); } static int rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, struct mkd_renderopt *options) { if (!link || !link->size) return 0; if ((options->flags & RENDER_SAFELINK) != 0 && !is_safe_link(link->data, link->size)) return 0; rndr_autolink2(ob, link->data, link->size, type); return 1; } static void rndr_blockcode(struct buf *ob, struct buf *text, struct mkd_renderopt *options) { if (ob->size) bufputc(ob, '\n'); BUFPUTSL(ob, "
");
	if (text) lus_attr_escape(ob, text->data, text->size);
	BUFPUTSL(ob, "
\n"); } static void rndr_blockquote(struct buf *ob, struct buf *text, struct mkd_renderopt *options) { BUFPUTSL(ob, "
\n"); if (text) bufput(ob, text->data, text->size); BUFPUTSL(ob, "
"); } static int rndr_codespan(struct buf *ob, struct buf *text, struct mkd_renderopt *options) { BUFPUTSL(ob, ""); if (text) lus_attr_escape(ob, text->data, text->size); BUFPUTSL(ob, ""); return 1; } static int rndr_double_emphasis(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *options) { if (!text || !text->size) return 0; BUFPUTSL(ob, ""); bufput(ob, text->data, text->size); BUFPUTSL(ob, ""); return 1; } static int rndr_emphasis(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *options) { if (!text || !text->size) return 0; BUFPUTSL(ob, ""); if (text) bufput(ob, text->data, text->size); BUFPUTSL(ob, ""); return 1; } static void rndr_header(struct buf *ob, struct buf *text, int level, struct mkd_renderopt *options) { if (ob->size) bufputc(ob, '\n'); if (options->flags & RENDER_TOC) { struct toc_data *data = options->opaque; bufprintf(ob, "", data->header_count++); } bufprintf(ob, "", level); if (text) bufput(ob, text->data, text->size); bufprintf(ob, "\n", level); } static int rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, struct mkd_renderopt *options) { if ((options->flags & RENDER_SAFELINK) != 0 && !is_safe_link(link->data, link->size)) return 0; BUFPUTSL(ob, "size) lus_attr_escape(ob, link->data, link->size); if (title && title->size) { BUFPUTSL(ob, "\" title=\""); lus_attr_escape(ob, title->data, title->size); } BUFPUTSL(ob, "\">"); if (content && content->size) bufput(ob, content->data, content->size); BUFPUTSL(ob, ""); return 1; } static void rndr_list(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *options) { if (ob->size) bufputc(ob, '\n'); bufput(ob, flags & MKD_LIST_ORDERED ? "
    \n" : "
      \n", 5); if (text) bufput(ob, text->data, text->size); bufput(ob, flags & MKD_LIST_ORDERED ? "
\n" : "\n", 6); } static void rndr_listitem(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *options) { BUFPUTSL(ob, "
  • "); if (text) { while (text->size && text->data[text->size - 1] == '\n') text->size -= 1; bufput(ob, text->data, text->size); } BUFPUTSL(ob, "
  • \n"); } static void rndr_paragraph(struct buf *ob, struct buf *text, struct mkd_renderopt *options) { 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) { BUFPUTSL(ob, "

    "); bufput(ob, &text->data[i], text->size - i); BUFPUTSL(ob, "

    \n"); } } static void rndr_raw_block(struct buf *ob, struct buf *text, struct mkd_renderopt *options) { size_t org, sz; if (!text) return; sz = text->size; while (sz > 0 && text->data[sz - 1] == '\n') sz -= 1; org = 0; while (org < sz && text->data[org] == '\n') org += 1; 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, struct buf *text, char c, struct mkd_renderopt *options) { if (!text || !text->size) return 0; BUFPUTSL(ob, ""); bufput(ob, text->data, text->size); BUFPUTSL(ob, ""); return 1; } /********************** * XHTML 1.0 RENDERER * **********************/ static void rndr_hrule(struct buf *ob, struct mkd_renderopt *options) { if (ob->size) bufputc(ob, '\n'); BUFPUTSL(ob, "
    \n"); } static int rndr_image(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, struct mkd_renderopt *options) { if (!link || !link->size) return 0; BUFPUTSL(ob, "data, link->size); BUFPUTSL(ob, "\" alt=\""); if (alt && alt->size) lus_attr_escape(ob, alt->data, alt->size); if (title && title->size) { BUFPUTSL(ob, "\" title=\""); lus_attr_escape(ob, title->data, title->size); } BUFPUTSL(ob, "\" />"); return 1; } static int rndr_linebreak(struct buf *ob, struct mkd_renderopt *options) { BUFPUTSL(ob, "
    \n"); return 1; } static int rndr_raw_html(struct buf *ob, struct buf *text, struct mkd_renderopt *options) { int escape_html = 0; if (options->flags & RENDER_SKIP_HTML) escape_html = 1; else if ((options->flags & RENDER_SKIP_STYLE) != 0 && is_html_tag(text, "