static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) { struct xhtml_renderopt *options = 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) return; BUFPUTSL(ob, "<p>"); if (options->flags & XHTML_HARD_WRAP) { size_t org; while (i < text->size) { org = i; while (i < text->size && text->data[i] != '\n') i++; if (i > org) bufput(ob, text->data + org, i - org); if (i >= text->size) break; BUFPUTSL(ob, "<br/>\n"); i++; } } else { bufput(ob, &text->data[i], text->size - i); } BUFPUTSL(ob, "</p>\n"); /* Close any open quotes at the end of the paragraph */ options->quotes.in_squote = 0; options->quotes.in_dquote = 0; }
static void rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque) { struct html_renderopt *options = 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) return; BUFPUTSL(ob, "<p>"); if (options->flags & HTML_HARD_WRAP) { size_t org; while (i < text->size) { org = i; while (i < text->size && text->data[i] != '\n') i++; if (i > org) bufput(ob, text->data + org, i - org); /* * do not insert a line break if this newline * is the last character on the paragraph */ if (i >= text->size - 1) break; rndr_linebreak(ob, opaque); i++; } } else { bufput(ob, &text->data[i], text->size - i); } BUFPUTSL(ob, "</p>\n"); }
static void latex_header(struct buf *ob, struct buf *text, int level, void *opaque) { if (ob->size) bufputc(ob, '\n'); switch(level) { case 1: BUFPUTSL(ob,"\\section{"); break; case 2: BUFPUTSL(ob, "\\subsection{"); break; case 3: BUFPUTSL(ob, "\\subsubsection{"); break; default: fprintf(stderr, "Warning: ignoring header level %d\n", level); } if (text) bufput(ob, text->data, text->size); if (level >= 1 && level <= 3) BUFPUTSL(ob, "}\n"); }
static int rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque) { struct html_renderopt *options = opaque; if (!link || !link->size) return 0; BUFPUTSL(ob, "<img src=\""); escape_href(ob, link->data, link->size); BUFPUTSL(ob, "\" alt=\""); if (alt && alt->size) escape_html(ob, alt->data, alt->size); if (title && title->size) { BUFPUTSL(ob, "\" title=\""); escape_html(ob, title->data, title->size); } bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">"); return 1; }
/******************** * GENERIC RENDERER * ********************/ static int rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, void *opaque) { struct html_renderopt *options = opaque; if (!link || !link->size) return 0; if ((options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size) && type != MKDA_EMAIL) return 0; BUFPUTSL(ob, "<a href=\""); if (type == MKDA_EMAIL) BUFPUTSL(ob, "mailto:"); escape_href(ob, link->data, link->size); if (options->link_attributes) { bufputc(ob, '\"'); options->link_attributes(ob, link, opaque); bufputc(ob, '>'); } else { BUFPUTSL(ob, "\">"); } /* * Pretty printing: if we get an email address as * an actual URI, e.g. `mailto:[email protected]`, we don't * want to print the `mailto:` prefix */ if (bufprefix(link, "mailto:") == 0) { escape_html(ob, link->data + 7, link->size - 7); } else { escape_html(ob, link->data, link->size); } BUFPUTSL(ob, "</a>"); return 1; }
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 we have an option, if it is "_blank" which means to open a new tab, then we should check to make sure the item is not on the lightnet domain before outputting the target. We don't want to open new windows for links within the lightnet.is domain. */ if (options->target != NULL && (strcmp(options->target, "_blank") != 0 || strstr((const char*) link->data, options->domain) == 0) ) { BUFPUTSL(ob, " target=\""); bufputs(ob, options->target); bufputc(ob, '\"'); } }
static void toc_header(struct buf *ob, const struct buf *text, int level, void *opaque) { struct html_renderopt *options = opaque; /* set the level offset if this is the first header * we're parsing for the document */ if (options->toc_data.current_level == 0) { options->toc_data.level_offset = level - 1; } level -= options->toc_data.level_offset; if (level > options->toc_data.current_level) { while (level > options->toc_data.current_level) { BUFPUTSL(ob, "<ul>\n<li>\n"); options->toc_data.current_level++; } } else if (level < options->toc_data.current_level) { BUFPUTSL(ob, "</li>\n"); while (level < options->toc_data.current_level) { BUFPUTSL(ob, "</ul>\n</li>\n"); options->toc_data.current_level--; } BUFPUTSL(ob,"<li>\n"); } else { BUFPUTSL(ob,"</li>\n<li>\n"); } bufprintf(ob, "<a href=\"#toc_%d\">", options->toc_data.header_count++); if (text) escape_html(ob, text->data, text->size); BUFPUTSL(ob, "</a>\n"); }
static void toc_header(struct buf *ob, const struct buf *text, int level, void *opaque) { struct html_renderopt *options = opaque; if (level > options->toc_data.current_level) { while (level > options->toc_data.current_level) { BUFPUTSL(ob, "<ul>\n<li>\n"); options->toc_data.current_level++; } } else if (level < options->toc_data.current_level) { BUFPUTSL(ob, "</li>\n"); while (level < options->toc_data.current_level) { BUFPUTSL(ob, "</ul>\n</li>\n"); options->toc_data.current_level--; } BUFPUTSL(ob,"<li>\n"); } else { BUFPUTSL(ob,"</li>\n<li>\n"); } bufprintf(ob, "<a href=\"#toc_%d\">", options->toc_data.header_count++); if (text) bufput(ob, text->data, text->size); BUFPUTSL(ob, "</a>\n"); }
static size_t smartypants_cb__dash(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (size >= 3 && text[1] == '-' && text[2] == '-') { BUFPUTSL(ob, "—"); return 2; } if (size >= 2 && text[1] == '-') { if(size >= 4 && (previous_char == '>' || previous_char == '\n') && text[2] == ' ' && text[3] == '<') { // A hack to avoid '-- ' being converted to an ndash. } else { BUFPUTSL(ob, "–"); return 1; } } bufputc(ob, text[0]); return 0; }
static void nat_paragraph(struct buf *ob, struct buf *text, void *opaque) { size_t i = 0; if (ob->size) bufputc(ob, '\n'); BUFPUTSL(ob, "<p"); if (text && text->size && text->data[0] == '(') { i = 1; while (i < text->size && (text->data[i] == ' ' /* this seems to be a bit more restrictive than */ /* what is allowed for class names */ || (text->data[i] >= 'a' && text->data[i] <= 'z') || (text->data[i] >= 'A' && text->data[i] <= 'Z') || (text->data[i] >= '0' && text->data[i] <= '0'))) i += 1; if (i < text->size && text->data[i] == ')') { bufprintf(ob, " class=\"%.*s\"", (int)(i - 1), text->data + 1); i += 1; } else i = 0; } bufputc(ob, '>'); if (text) bufput(ob, text->data + i, text->size - i); BUFPUTSL(ob, "</p>\n"); }
static int rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque) { struct html_renderopt *options = opaque; if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size)) return 0; BUFPUTSL(ob, "<img src=\""); escape_href(ob, link->data, link->size); BUFPUTSL(ob, "\" alt=\""); if (alt && alt->size) escape_html(ob, alt->data, alt->size); if (title && title->size) { BUFPUTSL(ob, "\" title=\""); escape_html(ob, title->data, title->size); } bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">"); return 1; }
static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) { struct html_renderopt *options = 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) return; BUFPUTSL(ob, "<p>"); if (options->flags & HTML_HARD_WRAP) { size_t org; while (i < text->size) { org = i; while (i < text->size && text->data[i] != '\n') i++; if (i > org) bufput(ob, text->data + org, i - org); if (i >= text->size) break; BUFPUTSL(ob, "<br"); bufputs(ob, options->close_tag); i++; } } else { bufput(ob, &text->data[i], text->size - i); } BUFPUTSL(ob, "</p>\n"); }
static void toc_header(struct buf *ob, struct buf *text, int level, void *opaque) { struct xhtml_renderopt *options = opaque; if (level > options->toc_data.current_level) { if (level > 1) BUFPUTSL(ob, "<li>"); BUFPUTSL(ob, "<ul>\n"); } if (level < options->toc_data.current_level) { BUFPUTSL(ob, "</ul>"); if (options->toc_data.current_level > 1) BUFPUTSL(ob, "</li>\n"); } options->toc_data.current_level = level; bufprintf(ob, "<li><a href=\"#toc_%d\">", options->toc_data.header_count++); if (text) bufput(ob, text->data, text->size); BUFPUTSL(ob, "</a></li>\n"); }
static size_t smartypants_cb__squote(struct buf *ob, struct smartypants_data *smrt, uint8_t previous_char, const uint8_t *text, size_t size) { if (size >= 2) { uint8_t t1 = tolower(text[1]); if (t1 == '\'') { if (smartypants_quotes(ob, previous_char, size >= 3 ? text[2] : 0, 'd', &smrt->in_dquote)) return 1; } if ((t1 == 's' || t1 == 't' || t1 == 'm' || t1 == 'd') && (size == 3 || word_boundary(text[2]))) { BUFPUTSL(ob, "’"); return 0; } if (size >= 3) { uint8_t t2 = tolower(text[2]); if (((t1 == 'r' && t2 == 'e') || (t1 == 'l' && t2 == 'l') || (t1 == 'v' && t2 == 'e')) && (size == 4 || word_boundary(text[3]))) { BUFPUTSL(ob, "’"); return 0; } } } if (smartypants_quotes(ob, previous_char, size > 0 ? text[1] : 0, 's', &smrt->in_squote)) return 0; bufputc(ob, text[0]); return 0; }
static int print_link_wxh(struct buf *ob, struct buf *link) { size_t eq, ex, end; eq = link->size - 1; while (eq > 0 && (link->data[eq - 1] != ' ' || link->data[eq] != '=')) eq -= 1; if (eq <= 0) return 0; ex = eq + 1; while (ex < link->size && link->data[ex] >= '0' && link->data[ex] <= '9') ex += 1; if (ex >= link->size || ex == eq + 1 || link->data[ex] != 'x') return 0; end = ex + 1; while (end < link->size && link->data[end] >= '0' && link->data[end] <= '9') end += 1; if (end == ex + 1) return 0; /* everything is fine, proceeding to actual printing */ lus_attr_escape(ob, link->data, eq - 1); BUFPUTSL(ob, "\" width="); bufput(ob, link->data + eq + 1, ex - eq - 1); BUFPUTSL(ob, " height="); bufput(ob, link->data + ex + 1, end - ex - 1); return 1; }
static void rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque) { struct html_renderopt *options = opaque; if (ob->size) bufputc(ob, '\n'); if ((options->flags & HTML_TOC) && (level <= options->toc_data.nesting_level)) { bufprintf(ob, "<h%d id=\"", level); rndr_header_anchor(ob, text); BUFPUTSL(ob, "\">"); } else bufprintf(ob, "<h%d>", level); if (text) bufput(ob, text->data, text->size); bufprintf(ob, "</h%d>\n", level); }
/* * Rinku assumes valid HTML encoding for all input, but there's still * the case where a link can contain a double quote `"` that allows XSS. * * We need to properly escape the character we use for the `href` attribute * declaration */ static void print_link(struct buf *ob, const char *link, size_t size) { size_t i = 0, org; while (i < size) { org = i; while (i < size && link[i] != '"') i++; if (i > org) bufput(ob, link + org, i - org); if (i >= size) break; BUFPUTSL(ob, """); i++; } }
static void toc_header(struct buf *ob, const struct buf *text, int level, void *opaque) { struct html_renderopt *options = opaque; if (level <= options->toc_data.nesting_level) { /* set the level offset if this is the first header * we're parsing for the document */ if (options->toc_data.current_level == 0) options->toc_data.level_offset = level - 1; level -= options->toc_data.level_offset; if (level > options->toc_data.current_level) { while (level > options->toc_data.current_level) { BUFPUTSL(ob, "<ul>\n<li>\n"); options->toc_data.current_level++; } } else if (level < options->toc_data.current_level) { BUFPUTSL(ob, "</li>\n"); while (level < options->toc_data.current_level) { BUFPUTSL(ob, "</ul>\n</li>\n"); options->toc_data.current_level--; } BUFPUTSL(ob,"<li>\n"); } else { BUFPUTSL(ob,"</li>\n<li>\n"); } bufprintf(ob, "<a href=\"#"); rndr_header_anchor(ob, text); BUFPUTSL(ob, "\">"); if (text) { if (options->flags & HTML_ESCAPE) escape_html(ob, text->data, text->size); else bufput(ob, text->data, text->size); } BUFPUTSL(ob, "</a>\n"); } }
static void rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque) { struct html_renderopt *options = opaque; if (ob->size) bufputc(ob, '\n'); if (lang && lang->size) { size_t i, cls; if (options->flags & HTML_PRETTIFY) { BUFPUTSL(ob, "<pre><code class=\"prettyprint lang-"); cls++; } else { BUFPUTSL(ob, "<pre><code class=\""); } for (i = 0, cls = 0; i < lang->size; ++i, ++cls) { while (i < lang->size && isspace(lang->data[i])) i++; if (i < lang->size) { size_t org = i; while (i < lang->size && !isspace(lang->data[i])) i++; if (lang->data[org] == '.') org++; if (cls) bufputc(ob, ' '); escape_html(ob, lang->data + org, i - org); } } BUFPUTSL(ob, "\">"); } else if (options->flags & HTML_PRETTIFY) { BUFPUTSL(ob, "<pre><code class=\"prettyprint\">"); } else { BUFPUTSL(ob, "<pre><code>"); } if (text) escape_html(ob, text->data, text->size); BUFPUTSL(ob, "</code></pre>\n"); }
static void discount_table_cell(struct buf *ob, struct buf *text, int flags, void *opaque){ if (flags & MKD_CELL_HEAD) BUFPUTSL(ob, " <th"); else BUFPUTSL(ob, " <td"); switch (flags & MKD_CELL_ALIGN_MASK) { case MKD_CELL_ALIGN_LEFT: BUFPUTSL(ob, " align=\"left\""); break; case MKD_CELL_ALIGN_RIGHT: BUFPUTSL(ob, " align=\"right\""); break; case MKD_CELL_ALIGN_CENTER: BUFPUTSL(ob, " align=\"center\""); break; } bufputc(ob, '>'); if (text) bufput(ob, text->data, text->size); if (flags & MKD_CELL_HEAD) BUFPUTSL(ob, "</th>\n"); else BUFPUTSL(ob, "</td>\n"); }
void hoedown_escape_href(struct hoedown_buffer *ob, const uint8_t *src, size_t size) { static const char hex_chars[] = "0123456789ABCDEF"; size_t i = 0, org; char hex_str[3]; hex_str[0] = '%'; while (i < size) { org = i; while (i < size && HREF_SAFE[src[i]] != 0) i++; if (i > org) { if (org == 0) { if (i >= size) { hoedown_buffer_put(ob, src, size); return; } hoedown_buffer_grow(ob, ESCAPE_GROW_FACTOR(size)); } hoedown_buffer_put(ob, src + org, i - org); } /* escaping */ if (i >= size) break; switch (src[i]) { /* amp appears all the time in URLs, but needs * HTML-entity escaping to be inside an href */ case '&': BUFPUTSL(ob, "&"); break; /* the single quote is a valid URL character * according to the standard; it needs HTML * entity escaping too */ case '\'': BUFPUTSL(ob, "'"); break; /* the space can be escaped to %20 or a plus * sign. we're going with the generic escape * for now. the plus thing is more commonly seen * when building GET strings */ #if 0 case ' ': hoedown_buffer_putc(ob, '+'); break; #endif /* every other character goes with a %XX escaping */ default: hex_str[1] = hex_chars[(src[i] >> 4) & 0xF]; hex_str[2] = hex_chars[src[i] & 0xF]; hoedown_buffer_put(ob, hex_str, 3); } i++; } }
static void rndr_blockquote(struct buf *ob, struct buf *text, void *opaque) { if (ob->size) bufputc(ob, '\n'); BUFPUTSL(ob, "<blockquote>\n"); if (text) bufput(ob, text->data, text->size); BUFPUTSL(ob, "</blockquote>\n"); }
static void rndr_blockcode(struct buf *ob, struct buf *text, void *opaque) { if (ob->size) bufputc(ob, '\n'); BUFPUTSL(ob, "<pre><code>"); if (text) lus_body_escape(ob, text->data, text->size); BUFPUTSL(ob, "</code></pre>\n"); }
int rinku_autolink( struct buf *ob, const uint8_t *text, size_t size, autolink_mode mode, unsigned int flags, const char *link_attr, const char **skip_tags, void (*link_text_cb)(struct buf *, const uint8_t *, size_t, void *), void *payload) { size_t i, end; char active_chars[256] = {0}; int link_count = 0; if (!text || size == 0) return 0; active_chars['<'] = AUTOLINK_ACTION_SKIP_TAG; if (mode & AUTOLINK_EMAILS) active_chars['@'] = AUTOLINK_ACTION_EMAIL; if (mode & AUTOLINK_URLS) { active_chars['w'] = AUTOLINK_ACTION_WWW; active_chars['W'] = AUTOLINK_ACTION_WWW; active_chars[':'] = AUTOLINK_ACTION_URL; } if (link_attr != NULL) { while (rinku_isspace(*link_attr)) link_attr++; } bufgrow(ob, size); i = end = 0; while (i < size) { struct autolink_pos link; bool link_found; char action = 0; while (end < size && (action = active_chars[text[end]]) == 0) end++; if (end == size) { if (link_count > 0) bufput(ob, text + i, end - i); break; } if (action == AUTOLINK_ACTION_SKIP_TAG) { end += autolink__skip_tag(ob, text + end, size - end, skip_tags); continue; } link_found = g_callbacks[(int)action]( &link, text, end, size, flags); if (link_found && link.start >= i) { const uint8_t *link_str = text + link.start; const size_t link_len = link.end - link.start; bufput(ob, text + i, link.start - i); bufputs(ob, g_hrefs[(int)action]); print_link(ob, link_str, link_len); if (link_attr) { BUFPUTSL(ob, "\" "); bufputs(ob, link_attr); bufputc(ob, '>'); } else { BUFPUTSL(ob, "\">"); } if (link_text_cb) { link_text_cb(ob, link_str, link_len, payload); } else { bufput(ob, link_str, link_len); } BUFPUTSL(ob, "</a>"); link_count++; end = i = link.end; } else { end = end + 1; } } return link_count; }
static void discount_table_row(struct buf *ob, struct buf *cells, int flags, void *opaque){ (void)flags; BUFPUTSL(ob, " <tr>\n"); if (cells) bufput(ob, cells->data, cells->size); BUFPUTSL(ob, " </tr>\n"); }
static int xhtml_linebreak(struct buf *ob, void *opaque) { BUFPUTSL(ob, "<br />\n"); return 1; }
static void xhtml_hrule(struct buf *ob, void *opaque) { if (ob->size) bufputc(ob, '\n'); BUFPUTSL(ob, "<hr />\n"); }
static void rndr_paragraph(struct buf *ob, struct buf *text, void *opaque) { if (ob->size) bufputc(ob, '\n'); BUFPUTSL(ob, "<p>"); if (text) bufput(ob, text->data, text->size); BUFPUTSL(ob, "</p>\n"); }
static int rndr_codespan(struct buf *ob, struct buf *text, void *opaque) { BUFPUTSL(ob, "<code>"); if (text) lus_body_escape(ob, text->data, text->size); BUFPUTSL(ob, "</code>"); return 1; }
int rinku_autolink( struct buf *ob, const uint8_t *text, size_t size, autolink_mode mode, unsigned int flags, const char *link_attr, const char **skip_tags, void (*link_text_cb)(struct buf *ob, const struct buf *link, void *payload), void *payload) { size_t i, end, last_link_found = 0; struct buf *link = bufnew(16); char active_chars[256]; void (*link_url_cb)(struct buf *, const struct buf *, void *); int link_count = 0; if (!text || size == 0) return 0; memset(active_chars, 0x0, sizeof(active_chars)); active_chars['<'] = AUTOLINK_ACTION_SKIP_TAG; if (mode & AUTOLINK_EMAILS) active_chars['@'] = AUTOLINK_ACTION_EMAIL; if (mode & AUTOLINK_URLS) { active_chars['w'] = AUTOLINK_ACTION_WWW; active_chars['W'] = AUTOLINK_ACTION_WWW; active_chars[':'] = AUTOLINK_ACTION_URL; } if (link_text_cb == NULL) link_text_cb = &autolink__print; if (link_attr != NULL) { while (isspace(*link_attr)) link_attr++; } bufgrow(ob, size); i = end = 0; while (i < size) { size_t rewind, link_end; char action = 0; while (end < size && (action = active_chars[text[end]]) == 0) end++; if (end == size) { if (link_count > 0) bufput(ob, text + i, end - i); break; } if (action == AUTOLINK_ACTION_SKIP_TAG) { end += autolink__skip_tag(ob, text + end, size - end, skip_tags); continue; } link->size = 0; link_end = g_callbacks[(int)action]( &rewind, link, (uint8_t *)text + end, end - last_link_found, size - end, flags); /* print the link */ if (link_end > 0) { bufput(ob, text + i, end - i - rewind); bufputs(ob, g_hrefs[(int)action]); print_link(ob, link->data, link->size); if (link_attr) { BUFPUTSL(ob, "\" "); bufputs(ob, link_attr); bufputc(ob, '>'); } else { BUFPUTSL(ob, "\">"); } link_text_cb(ob, link, payload); BUFPUTSL(ob, "</a>"); link_count++; i = end + link_end; last_link_found = end = i; } else { end = end + 1; } } bufrelease(link); return link_count; }