aboutsummaryrefslogtreecommitdiff
path: root/src/node.c
blob: c5ce642fb28ffc049fcdf61d9592669700fe15ac (plain)
  1. #include <stdlib.h>
  2. #include <string.h>
  3. #include "config.h"
  4. #include "node.h"
  5. static void
  6. S_node_unlink(cmark_node *node);
  7. cmark_node*
  8. cmark_node_new(cmark_node_type type) {
  9. cmark_node *node = (cmark_node *)calloc(1, sizeof(*node));
  10. node->type = type;
  11. return node;
  12. }
  13. void
  14. cmark_node_destroy(cmark_node *node) {
  15. S_node_unlink(node);
  16. node->next = NULL;
  17. cmark_free_nodes(node);
  18. }
  19. cmark_node_type
  20. cmark_node_get_type(cmark_node *node)
  21. {
  22. return node->type;
  23. }
  24. static const char*
  25. S_type_string(cmark_node *node)
  26. {
  27. switch (node->type) {
  28. case CMARK_NODE_DOCUMENT: return "DOCUMENT";
  29. case CMARK_NODE_BQUOTE: return "BQUOTE";
  30. case CMARK_NODE_LIST: return "LIST";
  31. case CMARK_NODE_LIST_ITEM: return "LIST_ITEM";
  32. case CMARK_NODE_FENCED_CODE: return "FENCED_CODE";
  33. case CMARK_NODE_INDENTED_CODE: return "INDENTED_CODE";
  34. case CMARK_NODE_HTML: return "HTML";
  35. case CMARK_NODE_PARAGRAPH: return "PARAGRAPH";
  36. case CMARK_NODE_ATX_HEADER: return "ATX_HEADER";
  37. case CMARK_NODE_SETEXT_HEADER: return "SETEXT_HEADER";
  38. case CMARK_NODE_HRULE: return "HRULE";
  39. case CMARK_NODE_REFERENCE_DEF: return "REFERENCE_DEF";
  40. case CMARK_NODE_STRING: return "STRING";
  41. case CMARK_NODE_SOFTBREAK: return "SOFTBREAK";
  42. case CMARK_NODE_LINEBREAK: return "LINEBREAK";
  43. case CMARK_NODE_INLINE_CODE: return "INLINE_CODE";
  44. case CMARK_NODE_INLINE_HTML: return "INLINE_HTML";
  45. case CMARK_NODE_EMPH: return "EMPH";
  46. case CMARK_NODE_STRONG: return "STRONG";
  47. case CMARK_NODE_LINK: return "LINK";
  48. case CMARK_NODE_IMAGE: return "IMAGE";
  49. }
  50. return "<unknown>";
  51. }
  52. cmark_node*
  53. cmark_node_next(cmark_node *node)
  54. {
  55. return node->next;
  56. }
  57. cmark_node*
  58. cmark_node_previous(cmark_node *node)
  59. {
  60. return node->prev;
  61. }
  62. cmark_node*
  63. cmark_node_parent(cmark_node *node)
  64. {
  65. return node->parent;
  66. }
  67. cmark_node*
  68. cmark_node_first_child(cmark_node *node)
  69. {
  70. return node->first_child;
  71. }
  72. cmark_node*
  73. cmark_node_last_child(cmark_node *node)
  74. {
  75. return node->last_child;
  76. }
  77. static char*
  78. S_strdup(const char *str) {
  79. size_t size = strlen(str) + 1;
  80. char *dup = (char *)malloc(size);
  81. memcpy(dup, str, size);
  82. return dup;
  83. }
  84. const char*
  85. cmark_node_get_content(cmark_node *node) {
  86. switch (node->type) {
  87. case NODE_STRING:
  88. case NODE_INLINE_HTML:
  89. case NODE_INLINE_CODE:
  90. return cmark_chunk_to_cstr(&node->as.literal);
  91. default:
  92. break;
  93. }
  94. return NULL;
  95. }
  96. int
  97. cmark_node_set_content(cmark_node *node, const char *content) {
  98. switch (node->type) {
  99. case NODE_STRING:
  100. case NODE_INLINE_HTML:
  101. case NODE_INLINE_CODE:
  102. cmark_chunk_set_cstr(&node->as.literal, content);
  103. return 1;
  104. default:
  105. break;
  106. }
  107. return 0;
  108. }
  109. const char*
  110. cmark_node_get_url(cmark_node *node) {
  111. switch (node->type) {
  112. case NODE_LINK:
  113. case NODE_IMAGE:
  114. return (char *)node->as.link.url;
  115. default:
  116. break;
  117. }
  118. return NULL;
  119. }
  120. int
  121. cmark_node_set_url(cmark_node *node, const char *url) {
  122. switch (node->type) {
  123. case NODE_LINK:
  124. case NODE_IMAGE:
  125. free(node->as.link.url);
  126. node->as.link.url = (unsigned char *)S_strdup(url);
  127. return 1;
  128. default:
  129. break;
  130. }
  131. return 0;
  132. }
  133. static inline bool
  134. S_is_block(cmark_node *node) {
  135. return node->type >= CMARK_NODE_FIRST_BLOCK
  136. && node->type <= CMARK_NODE_LAST_BLOCK;
  137. }
  138. static inline bool
  139. S_is_inline(cmark_node *node) {
  140. return node->type >= CMARK_NODE_FIRST_INLINE
  141. && node->type <= CMARK_NODE_LAST_INLINE;
  142. }
  143. static bool
  144. S_can_contain(cmark_node *node, cmark_node *child)
  145. {
  146. if (child->type == CMARK_NODE_DOCUMENT) {
  147. return false;
  148. }
  149. switch (node->type) {
  150. case CMARK_NODE_DOCUMENT:
  151. case CMARK_NODE_BQUOTE:
  152. case CMARK_NODE_LIST_ITEM:
  153. return S_is_block(child)
  154. && child->type != CMARK_NODE_LIST_ITEM;
  155. case CMARK_NODE_LIST:
  156. return child->type == CMARK_NODE_LIST_ITEM;
  157. case CMARK_NODE_PARAGRAPH:
  158. case CMARK_NODE_ATX_HEADER:
  159. case CMARK_NODE_SETEXT_HEADER:
  160. case CMARK_NODE_EMPH:
  161. case CMARK_NODE_STRONG:
  162. case CMARK_NODE_LINK:
  163. case CMARK_NODE_IMAGE:
  164. return S_is_inline(child);
  165. default:
  166. break;
  167. }
  168. return false;
  169. }
  170. // Unlink a node without adjusting its next, prev, and parent pointers.
  171. static void
  172. S_node_unlink(cmark_node *node)
  173. {
  174. if (node->prev) {
  175. node->prev->next = node->next;
  176. }
  177. if (node->next) {
  178. node->next->prev = node->prev;
  179. }
  180. // Adjust first_child and last_child of parent.
  181. cmark_node *parent = node->parent;
  182. if (parent) {
  183. if (parent->first_child == node) {
  184. parent->first_child = node->next;
  185. }
  186. if (parent->last_child == node) {
  187. parent->last_child = node->prev;
  188. }
  189. }
  190. }
  191. void
  192. cmark_node_unlink(cmark_node *node) {
  193. S_node_unlink(node);
  194. node->next = NULL;
  195. node->prev = NULL;
  196. node->parent = NULL;
  197. }
  198. int
  199. cmark_node_insert_before(cmark_node *node, cmark_node *sibling)
  200. {
  201. if (!S_can_contain(node->parent, sibling)) {
  202. return 0;
  203. }
  204. S_node_unlink(sibling);
  205. cmark_node *old_prev = node->prev;
  206. // Insert 'sibling' between 'old_prev' and 'node'.
  207. if (old_prev) {
  208. old_prev->next = sibling;
  209. }
  210. sibling->prev = old_prev;
  211. sibling->next = node;
  212. node->prev = sibling;
  213. // Set new parent.
  214. cmark_node *parent = node->parent;
  215. sibling->parent = parent;
  216. // Adjust first_child of parent if inserted as first child.
  217. if (parent && !old_prev) {
  218. parent->first_child = sibling;
  219. }
  220. return 1;
  221. }
  222. int
  223. cmark_node_insert_after(cmark_node *node, cmark_node *sibling)
  224. {
  225. if (!S_can_contain(node->parent, sibling)) {
  226. return 0;
  227. }
  228. S_node_unlink(sibling);
  229. cmark_node *old_next = node->next;
  230. // Insert 'sibling' between 'node' and 'old_next'.
  231. if (old_next) {
  232. old_next->prev = sibling;
  233. }
  234. sibling->next = old_next;
  235. sibling->prev = node;
  236. node->next = sibling;
  237. // Set new parent.
  238. cmark_node *parent = node->parent;
  239. sibling->parent = parent;
  240. // Adjust last_child of parent if inserted as last child.
  241. if (parent && !old_next) {
  242. parent->last_child = sibling;
  243. }
  244. return 1;
  245. }
  246. int
  247. cmark_node_prepend_child(cmark_node *node, cmark_node *child)
  248. {
  249. if (!S_can_contain(node, child)) {
  250. return 0;
  251. }
  252. S_node_unlink(child);
  253. cmark_node *old_first_child = node->first_child;
  254. child->next = old_first_child;
  255. child->prev = NULL;
  256. child->parent = node;
  257. node->first_child = child;
  258. if (old_first_child) {
  259. old_first_child->prev = child;
  260. }
  261. else {
  262. // Also set last_child if node previously had no children.
  263. node->last_child = child;
  264. }
  265. return 1;
  266. }
  267. int
  268. cmark_node_append_child(cmark_node *node, cmark_node *child)
  269. {
  270. if (!S_can_contain(node, child)) {
  271. return 0;
  272. }
  273. S_node_unlink(child);
  274. cmark_node *old_last_child = node->last_child;
  275. child->next = NULL;
  276. child->prev = old_last_child;
  277. child->parent = node;
  278. node->last_child = child;
  279. if (old_last_child) {
  280. old_last_child->next = child;
  281. }
  282. else {
  283. // Also set first_child if node previously had no children.
  284. node->first_child = child;
  285. }
  286. return 1;
  287. }
  288. static void
  289. S_print_error(cmark_node *node, const char *elem)
  290. {
  291. fprintf(stderr, "Invalid '%s' in node type %s at %d:%d\n", elem,
  292. S_type_string(node), node->start_line, node->start_column);
  293. }
  294. int
  295. cmark_node_check(cmark_node *node)
  296. {
  297. cmark_node *cur = node;
  298. int errors = 0;
  299. while (cur) {
  300. if (cur->first_child) {
  301. if (cur->first_child->parent != cur) {
  302. S_print_error(cur->first_child, "parent");
  303. cur->first_child->parent = cur;
  304. ++errors;
  305. }
  306. cur = cur->first_child;
  307. }
  308. else if (cur->next) {
  309. if (cur->next->prev != cur) {
  310. S_print_error(cur->next, "prev");
  311. cur->next->prev = cur;
  312. ++errors;
  313. }
  314. if (cur->next->parent != cur->parent) {
  315. S_print_error(cur->next, "parent");
  316. cur->next->parent = cur->parent;
  317. ++errors;
  318. }
  319. cur = cur->next;
  320. }
  321. else {
  322. if (cur->parent->last_child != cur) {
  323. S_print_error(cur->parent, "last_child");
  324. cur->parent->last_child = cur;
  325. ++errors;
  326. }
  327. cmark_node *ancestor = cur->parent;
  328. cur = NULL;
  329. while (ancestor != node->parent) {
  330. if (ancestor->next) {
  331. cur = ancestor->next;
  332. break;
  333. }
  334. ancestor = ancestor->parent;
  335. }
  336. }
  337. }
  338. return errors;
  339. }
  340. // Free a cmark_node list and any children.
  341. void cmark_free_nodes(cmark_node *e)
  342. {
  343. cmark_node *next;
  344. while (e != NULL) {
  345. strbuf_free(&e->string_content);
  346. switch (e->type){
  347. case NODE_FENCED_CODE:
  348. strbuf_free(&e->as.code.info);
  349. break;
  350. case NODE_STRING:
  351. case NODE_INLINE_HTML:
  352. case NODE_INLINE_CODE:
  353. cmark_chunk_free(&e->as.literal);
  354. break;
  355. case NODE_LINK:
  356. case NODE_IMAGE:
  357. free(e->as.link.url);
  358. free(e->as.link.title);
  359. break;
  360. default:
  361. break;
  362. }
  363. if (e->last_child) {
  364. // Splice children into list
  365. e->last_child->next = e->next;
  366. e->next = e->first_child;
  367. }
  368. next = e->next;
  369. free(e);
  370. e = next;
  371. }
  372. }