aboutsummaryrefslogtreecommitdiff
path: root/src/cmark.c
blob: 56bbb93047517259dc5ec1dd14749e1c47ceb4b6 (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. extern unsigned char *cmark_markdown_to_html(unsigned char *text, int len)
  10. {
  11. node_block *blocks;
  12. strbuf htmlbuf = GH_BUF_INIT;
  13. unsigned char *result;
  14. blocks = cmark_parse_document(text, len);
  15. cmark_render_html(&htmlbuf, blocks);
  16. cmark_free_blocks(blocks);
  17. result = strbuf_detach(&htmlbuf);
  18. strbuf_free(&htmlbuf);
  19. return result;
  20. }
  21. // Free a node_block list and any children.
  22. void cmark_free_blocks(cmark_node_block *e)
  23. {
  24. cmark_node_block * next;
  25. while (e != NULL) {
  26. cmark_free_inlines(e->inline_content);
  27. strbuf_free(&e->string_content);
  28. if (e->tag == CMARK_BLOCK_FENCED_CODE) {
  29. strbuf_free(&e->as.code.info);
  30. } else if (e->tag == CMARK_BLOCK_DOCUMENT) {
  31. reference_map_free(e->as.document.refmap);
  32. }
  33. if (e->last_child) {
  34. // Splice children into list
  35. e->last_child->next = e->next;
  36. e->next = e->children;
  37. }
  38. next = e->next;
  39. free(e);
  40. e = next;
  41. }
  42. }
  43. // Utility function used by free_inlines
  44. static void splice_into_list(cmark_node_inl* e, node_inl* children) {
  45. cmark_node_inl * tmp;
  46. if (children) {
  47. tmp = children;
  48. // Find last child
  49. while (tmp->next) {
  50. tmp = tmp->next;
  51. }
  52. // Splice children into list
  53. tmp->next = e->next;
  54. e->next = children;
  55. }
  56. return ;
  57. }
  58. // Free an inline list. Avoid recursion to prevent stack overflows
  59. // on deeply nested structures.
  60. void cmark_free_inlines(cmark_node_inl* e)
  61. {
  62. node_inl * next;
  63. while (e != NULL) {
  64. switch (e->tag){
  65. case CMARK_INL_STRING:
  66. case CMARK_INL_RAW_HTML:
  67. case CMARK_INL_CODE:
  68. cmark_chunk_free(&e->content.literal);
  69. break;
  70. case CMARK_INL_LINEBREAK:
  71. case CMARK_INL_SOFTBREAK:
  72. break;
  73. case CMARK_INL_LINK:
  74. case CMARK_INL_IMAGE:
  75. free(e->content.linkable.url);
  76. free(e->content.linkable.title);
  77. splice_into_list(e, e->content.linkable.label);
  78. break;
  79. case CMARK_INL_EMPH:
  80. case CMARK_INL_STRONG:
  81. splice_into_list(e, e->content.inlines);
  82. break;
  83. default:
  84. fprintf(stderr, "[WARN] (%s:%d) Unknown inline tag %d",
  85. __FILE__, __LINE__, e->tag);
  86. break;
  87. }
  88. next = e->next;
  89. free(e);
  90. e = next;
  91. }
  92. }
  93. inline cmark_node_inl *cmark_make_link(cmark_node_inl *label, unsigned char *url, unsigned char *title)
  94. {
  95. cmark_node_inl* e = calloc(1, sizeof(*e));
  96. if(e != NULL) {
  97. e->tag = CMARK_INL_LINK;
  98. e->content.linkable.label = label;
  99. e->content.linkable.url = url;
  100. e->content.linkable.title = title;
  101. e->next = NULL;
  102. }
  103. return e;
  104. }
  105. unsigned char *clean_autolink(chunk *url, int is_email)
  106. {
  107. strbuf buf = GH_BUF_INIT;
  108. chunk_trim(url);
  109. if (url->len == 0)
  110. return NULL;
  111. if (is_email)
  112. strbuf_puts(&buf, "mailto:");
  113. houdini_unescape_html_f(&buf, url->data, url->len);
  114. return strbuf_detach(&buf);
  115. }
  116. cmark_node_inl* cmark_make_autolink(cmark_node_inl* label, chunk url, int is_email)
  117. {
  118. return cmark_make_link(label, clean_autolink(&url, is_email), NULL);
  119. }
  120. inline cmark_node_inl* cmark_make_inlines(int t, cmark_node_inl* contents)
  121. {
  122. cmark_node_inl * e = calloc(1, sizeof(*e));
  123. if(e != NULL) {
  124. e->tag = t;
  125. e->content.inlines = contents;
  126. e->next = NULL;
  127. }
  128. return e;
  129. }
  130. // Create an inline with a literal string value.
  131. inline cmark_node_inl* cmark_make_literal(int t, cmark_chunk s)
  132. {
  133. cmark_node_inl * e = calloc(1, sizeof(*e));
  134. if(e != NULL) {
  135. e->tag = t;
  136. e->content.literal = s;
  137. e->next = NULL;
  138. }
  139. return e;
  140. }
  141. // Create an inline with no value.
  142. inline cmark_node_inl* cmark_make_simple(int t)
  143. {
  144. cmark_node_inl* e = calloc(1, sizeof(*e));
  145. if(e != NULL) {
  146. e->tag = t;
  147. e->next = NULL;
  148. }
  149. return e;
  150. }
  151. // Append inline list b to the end of inline list a.
  152. // Return pointer to head of new list.
  153. inline cmark_node_inl* cmark_append_inlines(cmark_node_inl* a, cmark_node_inl* b)
  154. {
  155. if (a == NULL) { // NULL acts like an empty list
  156. return b;
  157. }
  158. cmark_node_inl* cur = a;
  159. while (cur->next != NULL) {
  160. cur = cur->next;
  161. }
  162. cur->next = b;
  163. return a;
  164. }
  165. // Append block list b to the end of block list a.
  166. // Return pointer to head of new list.
  167. inline cmark_node_block* cmark_append_blocks(cmark_node_block* a, cmark_node_block* b)
  168. {
  169. if (a == NULL) { // NULL acts like an empty list
  170. return b;
  171. }
  172. cmark_node_block* cur = a;
  173. while (cur->next != NULL) {
  174. cur = cur->next;
  175. }
  176. cur->next = b;
  177. b->prev = cur;
  178. return a;
  179. }