static void print_str(strbuf* buffer, const unsigned char *s, int len) { int i; if (len < 0) len = strlen((char *)s); strbuf_putc(buffer, '"'); for (i = 0; i < len; ++i) { unsigned char c = s[i]; switch (c) { case '\n': strbuf_printf(buffer, "\\n"); break; case '"': strbuf_printf(buffer, "\\\""); break; case '\\': strbuf_printf(buffer, "\\\\"); break; default: strbuf_putc(buffer, (int)c); } } strbuf_putc(buffer, '"'); }
// Prettyprint an inline list, for debugging. static void render_nodes(strbuf* buffer, cmark_node* node, int indent) { int i; cmark_list *data; while(node != NULL) { for (i=0; i < indent; i++) { strbuf_putc(buffer, ' '); } switch(node->type) { case NODE_DOCUMENT: break; case NODE_BLOCK_QUOTE: strbuf_printf(buffer, "block_quote\n"); break; case NODE_LIST_ITEM: strbuf_printf(buffer, "list_item\n"); break; case NODE_LIST: data = &(node->as.list); if (data->list_type == CMARK_ORDERED_LIST) { strbuf_printf(buffer, "list (type=ordered tight=%s start=%d delim=%s)\n", (data->tight ? "true" : "false"), data->start, (data->delimiter == CMARK_PAREN_DELIM ? "parens" : "period")); } else { strbuf_printf(buffer, "list (type=bullet tight=%s bullet_char=%c)\n", (data->tight ? "true" : "false"), data->bullet_char); } break; case NODE_HEADER: strbuf_printf(buffer, "header (level=%d)\n", node->as.header.level); break; case NODE_PARAGRAPH: strbuf_printf(buffer, "paragraph\n"); break; case NODE_HRULE: strbuf_printf(buffer, "hrule\n"); break; case NODE_CODE_BLOCK: strbuf_printf(buffer, "code_block info="); print_str(buffer, node->as.code.info.ptr, -1); strbuf_putc(buffer, ' '); print_str(buffer, node->string_content.ptr, -1); strbuf_putc(buffer, '\n'); break; case NODE_HTML: strbuf_printf(buffer, "html "); print_str(buffer, node->string_content.ptr, -1); strbuf_putc(buffer, '\n'); break; case NODE_TEXT: strbuf_printf(buffer, "text "); print_str(buffer, node->as.literal.data, node->as.literal.len); strbuf_putc(buffer, '\n'); break; case NODE_LINEBREAK: strbuf_printf(buffer, "linebreak\n"); break; case NODE_SOFTBREAK: strbuf_printf(buffer, "softbreak\n"); break; case NODE_INLINE_CODE: strbuf_printf(buffer, "code "); print_str(buffer, node->as.literal.data, node->as.literal.len); strbuf_putc(buffer, '\n'); break; case NODE_INLINE_HTML: strbuf_printf(buffer, "inline_html "); print_str(buffer, node->as.literal.data, node->as.literal.len); strbuf_putc(buffer, '\n'); break; case NODE_LINK: case NODE_IMAGE: strbuf_printf(buffer, "%s url=", node->type == NODE_LINK ? "link" : "image"); if (node->as.link.url) print_str(buffer, node->as.link.url, -1); if (node->as.link.title) { strbuf_printf(buffer, " title="); print_str(buffer, node->as.link.title, -1); } strbuf_putc(buffer, '\n'); break; case NODE_STRONG: strbuf_printf(buffer, "strong\n"); break; case NODE_EMPH: strbuf_printf(buffer, "emph\n"); break; default: break; } if (node->first_child) { // render children if any indent += INDENT; node = node->first_child; } else if (node->next) { // otherwise render next sibling node = node->next; } else { node = node->parent; // back up to parent while (node) { indent -= INDENT; if (node->next) { node = node->next; break; } else { node = node->parent; } if (!node) { break; } } } } }
// Convert a node_block list to HTML. Returns 0 on success, and sets result. static void blocks_to_html(strbuf *html, node_block *b) { struct ListData *data; render_stack* rstack = NULL; bool visit_children = false; bool tight = false; while(b != NULL) { visit_children = false; switch(b->tag) { case BLOCK_DOCUMENT: rstack = push_block(rstack, b->next, "", false, false); visit_children = true; break; case BLOCK_PARAGRAPH: if (tight) { inlines_to_html(html, b->inline_content); } else { cr(html); strbuf_puts(html, "<p>"); inlines_to_html(html, b->inline_content); strbuf_puts(html, "</p>\n"); } break; case BLOCK_BQUOTE: cr(html); strbuf_puts(html, "<blockquote>\n"); rstack = push_block(rstack, b->next, "</blockquote>\n", tight, false); tight = false; visit_children = true; break; case BLOCK_LIST_ITEM: cr(html); strbuf_puts(html, "<li>"); rstack = push_block(rstack, b->next, "</li>\n", tight, true); visit_children = true; break; case BLOCK_LIST: // make sure a list starts at the beginning of the line: cr(html); data = &(b->as.list); if (data->start > 1) { strbuf_printf(html, "<%s start=\"%d\">\n", data->list_type == bullet ? "ul" : "ol", data->start); } else { strbuf_puts(html, data->list_type == bullet ? "<ul>\n" : "<ol>\n"); } rstack = push_block(rstack, b->next, data->list_type == bullet ? "\n</ul>\n" : "\n</ol>\n", tight, false); tight = data->tight; visit_children = true; break; case BLOCK_ATX_HEADER: case BLOCK_SETEXT_HEADER: cr(html); strbuf_printf(html, "<h%d>", b->as.header.level); inlines_to_html(html, b->inline_content); strbuf_printf(html, "</h%d>\n", b->as.header.level); break; case BLOCK_INDENTED_CODE: case BLOCK_FENCED_CODE: cr(html); strbuf_puts(html, "<pre><code"); if (b->tag == BLOCK_FENCED_CODE) { strbuf *info = &b->as.code.info; if (strbuf_len(info) > 0) { int first_tag = strbuf_strchr(info, ' ', 0); if (first_tag < 0) first_tag = strbuf_len(info); strbuf_puts(html, " class=\"language-"); escape_html(html, info->ptr, first_tag); strbuf_putc(html, '"'); } } strbuf_putc(html, '>'); escape_html(html, b->string_content.ptr, b->string_content.size); strbuf_puts(html, "</code></pre>\n"); break; case BLOCK_HTML: strbuf_put(html, b->string_content.ptr, b->string_content.size); break; case BLOCK_HRULE: strbuf_puts(html, "<hr />\n"); break; case BLOCK_REFERENCE_DEF: break; default: assert(false); } if (visit_children) { b = b->children; } else { b = b->next; } while (b == NULL && rstack != NULL) { strbuf_puts(html, rstack->literal); if (rstack->trim) { strbuf_rtrim(html); } tight = rstack->tight; b = rstack->next_sibling.block; rstack = pop_render_stack(rstack); } } free_render_stack(rstack); }