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