aboutsummaryrefslogtreecommitdiff
path: root/src/xml.c
blob: 1ec1d7d68461e4cf31c7c91e76373df18160d796 (plain)
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include <assert.h>
  5. #include "config.h"
  6. #include "cmark.h"
  7. #include "node.h"
  8. #include "buffer.h"
  9. #include "houdini.h"
  10. // Functions to convert cmark_nodes to XML strings.
  11. static void escape_xml(cmark_strbuf *dest, const unsigned char *source, int length)
  12. {
  13. if (source != NULL) {
  14. if (length < 0)
  15. length = strlen((char *)source);
  16. houdini_escape_html0(dest, source, (size_t)length, 0);
  17. }
  18. }
  19. struct render_state {
  20. cmark_strbuf* xml;
  21. int indent;
  22. };
  23. static inline void indent(struct render_state *state)
  24. {
  25. int i;
  26. for (i = 0; i < state->indent; i++) {
  27. cmark_strbuf_putc(state->xml, ' ');
  28. }
  29. }
  30. static int
  31. S_render_node(cmark_node *node, cmark_event_type ev_type, void *vstate)
  32. {
  33. struct render_state *state = vstate;
  34. cmark_strbuf *xml = state->xml;
  35. bool literal = false;
  36. bool entering = (ev_type == CMARK_EVENT_ENTER);
  37. if (entering) {
  38. indent(state);
  39. cmark_strbuf_printf(xml, "<%s",
  40. cmark_node_get_type_string(node));
  41. if (node->start_line != 0) {
  42. cmark_strbuf_printf(xml, " sourcepos=\"%d:%d-%d\"",
  43. node->start_line,
  44. node->start_column,
  45. node->end_line);
  46. }
  47. literal = false;
  48. switch (node->type) {
  49. case CMARK_NODE_TEXT:
  50. case CMARK_NODE_CODE:
  51. case CMARK_NODE_HTML:
  52. case CMARK_NODE_INLINE_HTML:
  53. cmark_strbuf_puts(xml, ">");
  54. escape_xml(xml, node->as.literal.data,
  55. node->as.literal.len);
  56. cmark_strbuf_puts(xml, "</");
  57. cmark_strbuf_puts(xml,
  58. cmark_node_get_type_string(node));
  59. literal = true;
  60. break;
  61. case CMARK_NODE_CODE_BLOCK:
  62. if (node->as.code.info.len > 0) {
  63. cmark_strbuf_puts(xml, " info=\"");
  64. escape_xml(xml, node->as.code.info.data,
  65. node->as.code.info.len);
  66. cmark_strbuf_putc(xml, '"');
  67. }
  68. cmark_strbuf_puts(xml, ">");
  69. escape_xml(xml, node->as.code.literal.data,
  70. node->as.code.literal.len);
  71. cmark_strbuf_puts(xml, "</");
  72. cmark_strbuf_puts(xml,
  73. cmark_node_get_type_string(node));
  74. literal = true;
  75. break;
  76. case CMARK_NODE_LINK:
  77. case CMARK_NODE_IMAGE:
  78. cmark_strbuf_puts(xml, " url=\"");
  79. escape_xml(xml, node->as.link.url, -1);
  80. cmark_strbuf_putc(xml, '"');
  81. cmark_strbuf_puts(xml, " title=\"");
  82. escape_xml(xml, node->as.link.title, -1);
  83. cmark_strbuf_putc(xml, '"');
  84. break;
  85. default:
  86. break;
  87. }
  88. if (node->first_child) {
  89. state->indent += 2;
  90. } else if (!literal) {
  91. cmark_strbuf_puts(xml, " /");
  92. }
  93. } else {
  94. if (node->first_child) {
  95. state->indent -= 2;
  96. }
  97. indent(state);
  98. cmark_strbuf_printf(xml, "</%s",
  99. cmark_node_get_type_string(node));
  100. }
  101. // TODO print attributes
  102. cmark_strbuf_puts(xml, ">\n");
  103. return 1;
  104. }
  105. char *cmark_render_xml(cmark_node *root)
  106. {
  107. char *result;
  108. cmark_strbuf xml = GH_BUF_INIT;
  109. cmark_event_type ev_type;
  110. cmark_node *cur;
  111. struct render_state state = { &xml, 0 };
  112. cmark_iter *iter = cmark_iter_new(root);
  113. cmark_strbuf_puts(state.xml,
  114. "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
  115. cmark_strbuf_puts(state.xml,
  116. "<!DOCTYPE CommonMark SYSTEM \"CommonMark.dtd\">\n");
  117. while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
  118. cur = cmark_iter_get_node(iter);
  119. S_render_node(cur, ev_type, &state);
  120. }
  121. result = (char *)cmark_strbuf_detach(&xml);
  122. cmark_iter_free(iter);
  123. cmark_strbuf_free(&xml);
  124. return result;
  125. }