/* * 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 xhtml_renderopt { struct { int header_count; int current_level; } toc_data; unsigned int flags; }; 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 int rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *opaque) { struct xhtml_renderopt *options = opaque; if (!link || !link->size) return 0; if ((options->flags & XHTML_SAFELINK) != 0 && !is_safe_link(link->data, link->size)) return 0; BUFPUTSL(ob, "data, link->size); BUFPUTSL(ob, "\">"); if (type == MKDA_EXPLICIT_EMAIL && link->size > 7) lus_attr_escape(ob, link->data + 7, link->size - 7); else lus_attr_escape(ob, link->data, link->size); BUFPUTSL(ob, ""); return 1; } static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque) { 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, void *opaque) { BUFPUTSL(ob, "
\n"); if (text) bufput(ob, text->data, text->size); BUFPUTSL(ob, "
"); } static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque) { 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, void *opaque) { struct xhtml_renderopt *options = opaque; if (!text || !text->size) return 0; if (c == '~') { if ((options->flags & XHTML_STRIKETHROUGH) == 0) return 0; BUFPUTSL(ob, ""); bufput(ob, text->data, text->size); BUFPUTSL(ob, ""); } else { BUFPUTSL(ob, ""); bufput(ob, text->data, text->size); BUFPUTSL(ob, ""); } return 1; } static int rndr_emphasis(struct buf *ob, struct buf *text, char c, void *opaque) { if (!text || !text->size || c == '~') 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, void *opaque) { struct xhtml_renderopt *options = opaque; if (ob->size) bufputc(ob, '\n'); if (options->flags & XHTML_TOC) { bufprintf(ob, "", options->toc_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, void *opaque) { struct xhtml_renderopt *options = opaque; if ((options->flags & XHTML_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, void *opaque) { 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, void *opaque) { 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, void *opaque) { 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, void *opaque) { 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, void *opaque) { 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, void *opaque) { 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, void *opaque) { 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, void *opaque) { BUFPUTSL(ob, "
    \n"); return 1; } static int rndr_raw_html(struct buf *ob, struct buf *text, void *opaque) { struct xhtml_renderopt *options = opaque; int escape_html = 0; if (options->flags & XHTML_SKIP_HTML) escape_html = 1; else if ((options->flags & XHTML_SKIP_STYLE) != 0 && is_html_tag(text, "