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