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