aboutsummaryrefslogtreecommitdiff
path: root/src/cmark.c
blob: 14901af0e8a9f9cfcbb7f9e6657c39e752608620 (plain)
  1. #include <stdlib.h>
  2. #include <assert.h>
  3. #include <stdio.h>
  4. #include <stdbool.h>
  5. #include "references.h"
  6. #include "html/houdini.h"
  7. #include "cmark.h"
  8. #include "buffer.h"
  9. #include "ast.h"
  10. unsigned char *cmark_markdown_to_html(unsigned char *text, int len)
  11. {
  12. node_block *blocks;
  13. unsigned char *result;
  14. blocks = cmark_parse_document(text, len);
  15. result = cmark_render_html(blocks);
  16. cmark_free_blocks(blocks);
  17. return result;
  18. }
  19. // Utility function used by free_inlines
  20. static void splice_into_list(cmark_node_inl* e, node_inl* children) {
  21. cmark_node_inl * tmp;
  22. if (children) {
  23. tmp = children;
  24. // Find last child
  25. while (tmp->next) {
  26. tmp = tmp->next;
  27. }
  28. // Splice children into list
  29. tmp->next = e->next;
  30. e->next = children;
  31. }
  32. return ;
  33. }
  34. // Free an inline list. Avoid recursion to prevent stack overflows
  35. // on deeply nested structures.
  36. void cmark_free_inlines(cmark_node_inl* e)
  37. {
  38. node_inl * next;
  39. while (e != NULL) {
  40. switch (e->tag){
  41. case CMARK_INL_STRING:
  42. case CMARK_INL_RAW_HTML:
  43. case CMARK_INL_CODE:
  44. cmark_chunk_free(&e->content.literal);
  45. break;
  46. case CMARK_INL_LINEBREAK:
  47. case CMARK_INL_SOFTBREAK:
  48. break;
  49. case CMARK_INL_LINK:
  50. case CMARK_INL_IMAGE:
  51. free(e->content.linkable.url);
  52. free(e->content.linkable.title);
  53. splice_into_list(e, e->content.linkable.label);
  54. break;
  55. case CMARK_INL_EMPH:
  56. case CMARK_INL_STRONG:
  57. splice_into_list(e, e->content.inlines);
  58. break;
  59. default:
  60. fprintf(stderr, "[WARN] (%s:%d) Unknown inline tag %d",
  61. __FILE__, __LINE__, e->tag);
  62. break;
  63. }
  64. next = e->next;
  65. free(e);
  66. e = next;
  67. }
  68. }
  69. inline cmark_node_inl *cmark_make_link(cmark_node_inl *label, unsigned char *url, unsigned char *title)
  70. {
  71. cmark_node_inl* e = (cmark_node_inl *)calloc(1, sizeof(*e));
  72. if(e != NULL) {
  73. e->tag = CMARK_INL_LINK;
  74. e->content.linkable.label = label;
  75. e->content.linkable.url = url;
  76. e->content.linkable.title = title;
  77. e->next = NULL;
  78. }
  79. return e;
  80. }
  81. unsigned char *clean_autolink(chunk *url, int is_email)
  82. {
  83. strbuf buf = GH_BUF_INIT;
  84. chunk_trim(url);
  85. if (url->len == 0)
  86. return NULL;
  87. if (is_email)
  88. strbuf_puts(&buf, "mailto:");
  89. houdini_unescape_html_f(&buf, url->data, url->len);
  90. return strbuf_detach(&buf);
  91. }
  92. inline cmark_node_inl* cmark_make_autolink(cmark_node_inl* label, chunk url, int is_email)
  93. {
  94. return cmark_make_link(label, clean_autolink(&url, is_email), NULL);
  95. }
  96. inline cmark_node_inl* cmark_make_inlines(cmark_inl_tag t, cmark_node_inl* contents)
  97. {
  98. cmark_node_inl * e = (cmark_node_inl *)calloc(1, sizeof(*e));
  99. if(e != NULL) {
  100. e->tag = t;
  101. e->content.inlines = contents;
  102. e->next = NULL;
  103. }
  104. return e;
  105. }
  106. // Create an inline with a literal string value.
  107. inline cmark_node_inl* cmark_make_literal(cmark_inl_tag t, cmark_chunk s)
  108. {
  109. cmark_node_inl * e = (cmark_node_inl *)calloc(1, sizeof(*e));
  110. if(e != NULL) {
  111. e->tag = t;
  112. e->content.literal = s;
  113. e->next = NULL;
  114. }
  115. return e;
  116. }
  117. // Create an inline with no value.
  118. inline cmark_node_inl* cmark_make_simple(cmark_inl_tag t)
  119. {
  120. cmark_node_inl* e = (cmark_node_inl *)calloc(1, sizeof(*e));
  121. if(e != NULL) {
  122. e->tag = t;
  123. e->next = NULL;
  124. }
  125. return e;
  126. }
  127. // Free a node_block list and any children.
  128. void cmark_free_blocks(cmark_node_block *e)
  129. {
  130. cmark_node_block * next;
  131. while (e != NULL) {
  132. cmark_free_inlines(e->inline_content);
  133. strbuf_free(&e->string_content);
  134. if (e->tag == CMARK_BLOCK_FENCED_CODE) {
  135. strbuf_free(&e->as.code.info);
  136. } else if (e->tag == CMARK_BLOCK_DOCUMENT) {
  137. reference_map_free(e->as.document.refmap);
  138. }
  139. if (e->last_child) {
  140. // Splice children into list
  141. e->last_child->next = e->next;
  142. e->next = e->children;
  143. }
  144. next = e->next;
  145. free(e);
  146. e = next;
  147. }
  148. }