diff options
| author | Felix (xq) Queißner <git@mq32.de> | 2020-06-09 18:27:38 +0200 |
|---|---|---|
| committer | Felix (xq) Queißner <git@mq32.de> | 2020-06-09 18:27:38 +0200 |
| commit | de8fd9328e9deb0d1ec596d7486686ea3cb688c2 (patch) | |
| tree | 9c25d7568dee522c1d2ed7b6d238d1dad54163f6 /lib/cmark/src/xml.c | |
| parent | 661ddc244793102ee0720871c4edcd64f80bc744 (diff) | |
| download | kristall-de8fd9328e9deb0d1ec596d7486686ea3cb688c2.tar.gz | |
Includes cmark markdown parser library.
Diffstat (limited to 'lib/cmark/src/xml.c')
| -rw-r--r-- | lib/cmark/src/xml.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/lib/cmark/src/xml.c b/lib/cmark/src/xml.c new file mode 100644 index 0000000..9306141 --- /dev/null +++ b/lib/cmark/src/xml.c @@ -0,0 +1,173 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <assert.h> + +#include "config.h" +#include "cmark.h" +#include "node.h" +#include "buffer.h" +#include "houdini.h" + +#define BUFFER_SIZE 100 + +// Functions to convert cmark_nodes to XML strings. + +static void escape_xml(cmark_strbuf *dest, const unsigned char *source, + bufsize_t length) { + houdini_escape_html0(dest, source, length, 0); +} + +struct render_state { + cmark_strbuf *xml; + int indent; +}; + +static CMARK_INLINE void indent(struct render_state *state) { + int i; + for (i = 0; i < state->indent; i++) { + cmark_strbuf_putc(state->xml, ' '); + } +} + +static int S_render_node(cmark_node *node, cmark_event_type ev_type, + struct render_state *state, int options) { + cmark_strbuf *xml = state->xml; + bool literal = false; + cmark_delim_type delim; + bool entering = (ev_type == CMARK_EVENT_ENTER); + char buffer[BUFFER_SIZE]; + + if (entering) { + indent(state); + cmark_strbuf_putc(xml, '<'); + cmark_strbuf_puts(xml, cmark_node_get_type_string(node)); + + if (options & CMARK_OPT_SOURCEPOS && node->start_line != 0) { + snprintf(buffer, BUFFER_SIZE, " sourcepos=\"%d:%d-%d:%d\"", + node->start_line, node->start_column, node->end_line, + node->end_column); + cmark_strbuf_puts(xml, buffer); + } + + literal = false; + + switch (node->type) { + case CMARK_NODE_DOCUMENT: + cmark_strbuf_puts(xml, " xmlns=\"http://commonmark.org/xml/1.0\""); + break; + case CMARK_NODE_TEXT: + case CMARK_NODE_CODE: + case CMARK_NODE_HTML_BLOCK: + case CMARK_NODE_HTML_INLINE: + cmark_strbuf_puts(xml, " xml:space=\"preserve\">"); + escape_xml(xml, node->data, node->len); + cmark_strbuf_puts(xml, "</"); + cmark_strbuf_puts(xml, cmark_node_get_type_string(node)); + literal = true; + break; + case CMARK_NODE_LIST: + switch (cmark_node_get_list_type(node)) { + case CMARK_ORDERED_LIST: + cmark_strbuf_puts(xml, " type=\"ordered\""); + snprintf(buffer, BUFFER_SIZE, " start=\"%d\"", + cmark_node_get_list_start(node)); + cmark_strbuf_puts(xml, buffer); + delim = cmark_node_get_list_delim(node); + if (delim == CMARK_PAREN_DELIM) { + cmark_strbuf_puts(xml, " delim=\"paren\""); + } else if (delim == CMARK_PERIOD_DELIM) { + cmark_strbuf_puts(xml, " delim=\"period\""); + } + break; + case CMARK_BULLET_LIST: + cmark_strbuf_puts(xml, " type=\"bullet\""); + break; + default: + break; + } + snprintf(buffer, BUFFER_SIZE, " tight=\"%s\"", + (cmark_node_get_list_tight(node) ? "true" : "false")); + cmark_strbuf_puts(xml, buffer); + break; + case CMARK_NODE_HEADING: + snprintf(buffer, BUFFER_SIZE, " level=\"%d\"", node->as.heading.level); + cmark_strbuf_puts(xml, buffer); + break; + case CMARK_NODE_CODE_BLOCK: + if (node->as.code.info) { + cmark_strbuf_puts(xml, " info=\""); + escape_xml(xml, node->as.code.info, strlen((char *)node->as.code.info)); + cmark_strbuf_putc(xml, '"'); + } + cmark_strbuf_puts(xml, " xml:space=\"preserve\">"); + escape_xml(xml, node->data, node->len); + cmark_strbuf_puts(xml, "</"); + cmark_strbuf_puts(xml, cmark_node_get_type_string(node)); + literal = true; + break; + case CMARK_NODE_CUSTOM_BLOCK: + case CMARK_NODE_CUSTOM_INLINE: + cmark_strbuf_puts(xml, " on_enter=\""); + escape_xml(xml, node->as.custom.on_enter, + strlen((char *)node->as.custom.on_enter)); + cmark_strbuf_putc(xml, '"'); + cmark_strbuf_puts(xml, " on_exit=\""); + escape_xml(xml, node->as.custom.on_exit, + strlen((char *)node->as.custom.on_exit)); + cmark_strbuf_putc(xml, '"'); + break; + case CMARK_NODE_LINK: + case CMARK_NODE_IMAGE: + cmark_strbuf_puts(xml, " destination=\""); + escape_xml(xml, node->as.link.url, strlen((char *)node->as.link.url)); + cmark_strbuf_putc(xml, '"'); + if (node->as.link.title) { + cmark_strbuf_puts(xml, " title=\""); + escape_xml(xml, node->as.link.title, + strlen((char *)node->as.link.title)); + cmark_strbuf_putc(xml, '"'); + } + break; + default: + break; + } + if (node->first_child) { + state->indent += 2; + } else if (!literal) { + cmark_strbuf_puts(xml, " /"); + } + cmark_strbuf_puts(xml, ">\n"); + + } else if (node->first_child) { + state->indent -= 2; + indent(state); + cmark_strbuf_puts(xml, "</"); + cmark_strbuf_puts(xml, cmark_node_get_type_string(node)); + cmark_strbuf_puts(xml, ">\n"); + } + + return 1; +} + +char *cmark_render_xml(cmark_node *root, int options) { + char *result; + cmark_strbuf xml = CMARK_BUF_INIT(root->mem); + cmark_event_type ev_type; + cmark_node *cur; + struct render_state state = {&xml, 0}; + + cmark_iter *iter = cmark_iter_new(root); + + cmark_strbuf_puts(state.xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"); + cmark_strbuf_puts(state.xml, + "<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n"); + while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) { + cur = cmark_iter_get_node(iter); + S_render_node(cur, ev_type, &state, options); + } + result = (char *)cmark_strbuf_detach(&xml); + + cmark_iter_free(iter); + return result; +} |
