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