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