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