1#include <stdlib.h>
2#include <string.h>
3
4#include "config.h"
5#include "node.h"
6
7static void S_node_unlink(cmark_node *node);
8
9static CMARK_INLINE bool S_is_block(cmark_node *node) {
10 if (node == NULL) {
11 return false;
12 }
13 return node->type >= CMARK_NODE_FIRST_BLOCK &&
14 node->type <= CMARK_NODE_LAST_BLOCK;
15}
16
17static CMARK_INLINE bool S_is_inline(cmark_node *node) {
18 if (node == NULL) {
19 return false;
20 }
21 return node->type >= CMARK_NODE_FIRST_INLINE &&
22 node->type <= CMARK_NODE_LAST_INLINE;
23}
24
25static bool S_can_contain(cmark_node *node, cmark_node *child) {
26 if (node == NULL || child == NULL || node == child) {
27 return false;
28 }
29
30 // Verify that child is not an ancestor of node.
31 if (child->first_child != NULL) {
32 cmark_node *cur = node->parent;
33
34 while (cur != NULL) {
35 if (cur == child) {
36 return false;
37 }
38 cur = cur->parent;
39 }
40 }
41
42 if (child->type == CMARK_NODE_DOCUMENT) {
43 return false;
44 }
45
46 switch (node->type) {
47 case CMARK_NODE_DOCUMENT:
48 case CMARK_NODE_BLOCK_QUOTE:
49 case CMARK_NODE_ITEM:
50 return S_is_block(child) && child->type != CMARK_NODE_ITEM;
51
52 case CMARK_NODE_LIST:
53 return child->type == CMARK_NODE_ITEM;
54
55 case CMARK_NODE_CUSTOM_BLOCK:
56 return true;
57
58 case CMARK_NODE_PARAGRAPH:
59 case CMARK_NODE_HEADING:
60 case CMARK_NODE_EMPH:
61 case CMARK_NODE_STRONG:
62 case CMARK_NODE_LINK:
63 case CMARK_NODE_IMAGE:
64 case CMARK_NODE_CUSTOM_INLINE:
65 return S_is_inline(child);
66
67 default:
68 break;
69 }
70
71 return false;
72}
73
74cmark_node *cmark_node_new_with_mem(cmark_node_type type, cmark_mem *mem) {
75 cmark_node *node = (cmark_node *)mem->calloc(1, sizeof(*node));
76 node->mem = mem;
77 node->type = (uint16_t)type;
78
79 switch (node->type) {
80 case CMARK_NODE_HEADING:
81 node->as.heading.level = 1;
82 break;
83
84 case CMARK_NODE_LIST: {
85 cmark_list *list = &node->as.list;
86 list->list_type = CMARK_BULLET_LIST;
87 list->start = 0;
88 list->tight = false;
89 break;
90 }
91
92 default:
93 break;
94 }
95
96 return node;
97}
98
99cmark_node *cmark_node_new(cmark_node_type type) {
100 extern cmark_mem DEFAULT_MEM_ALLOCATOR;
101 return cmark_node_new_with_mem(type, &DEFAULT_MEM_ALLOCATOR);
102}
103
104// Free a cmark_node list and any children.
105static void S_free_nodes(cmark_node *e) {
106 cmark_mem *mem = e->mem;
107 cmark_node *next;
108 while (e != NULL) {
109 switch (e->type) {
110 case CMARK_NODE_CODE_BLOCK:
111 mem->free(e->data);
112 mem->free(e->as.code.info);
113 break;
114 case CMARK_NODE_TEXT:
115 case CMARK_NODE_HTML_INLINE:
116 case CMARK_NODE_CODE:
117 case CMARK_NODE_HTML_BLOCK:
118 mem->free(e->data);
119 break;
120 case CMARK_NODE_LINK:
121 case CMARK_NODE_IMAGE:
122 mem->free(e->as.link.url);
123 mem->free(e->as.link.title);
124 break;
125 case CMARK_NODE_CUSTOM_BLOCK:
126 case CMARK_NODE_CUSTOM_INLINE:
127 mem->free(e->as.custom.on_enter);
128 mem->free(e->as.custom.on_exit);
129 break;
130 default:
131 break;
132 }
133 if (e->last_child) {
134 // Splice children into list
135 e->last_child->next = e->next;
136 e->next = e->first_child;
137 }
138 next = e->next;
139 mem->free(e);
140 e = next;
141 }
142}
143
144void cmark_node_free(cmark_node *node) {
145 S_node_unlink(node);
146 node->next = NULL;
147 S_free_nodes(node);
148}
149
150cmark_node_type cmark_node_get_type(cmark_node *node) {
151 if (node == NULL) {
152 return CMARK_NODE_NONE;
153 } else {
154 return (cmark_node_type)node->type;
155 }
156}
157
158const char *cmark_node_get_type_string(cmark_node *node) {
159 if (node == NULL) {
160 return "NONE";
161 }
162
163 switch (node->type) {
164 case CMARK_NODE_NONE:
165 return "none";
166 case CMARK_NODE_DOCUMENT:
167 return "document";
168 case CMARK_NODE_BLOCK_QUOTE:
169 return "block_quote";
170 case CMARK_NODE_LIST:
171 return "list";
172 case CMARK_NODE_ITEM:
173 return "item";
174 case CMARK_NODE_CODE_BLOCK:
175 return "code_block";
176 case CMARK_NODE_HTML_BLOCK:
177 return "html_block";
178 case CMARK_NODE_CUSTOM_BLOCK:
179 return "custom_block";
180 case CMARK_NODE_PARAGRAPH:
181 return "paragraph";
182 case CMARK_NODE_HEADING:
183 return "heading";
184 case CMARK_NODE_THEMATIC_BREAK:
185 return "thematic_break";
186 case CMARK_NODE_TEXT:
187 return "text";
188 case CMARK_NODE_SOFTBREAK:
189 return "softbreak";
190 case CMARK_NODE_LINEBREAK:
191 return "linebreak";
192 case CMARK_NODE_CODE:
193 return "code";
194 case CMARK_NODE_HTML_INLINE:
195 return "html_inline";
196 case CMARK_NODE_CUSTOM_INLINE:
197 return "custom_inline";
198 case CMARK_NODE_EMPH:
199 return "emph";
200 case CMARK_NODE_STRONG:
201 return "strong";
202 case CMARK_NODE_LINK:
203 return "link";
204 case CMARK_NODE_IMAGE:
205 return "image";
206 }
207
208 return "<unknown>";
209}
210
211cmark_node *cmark_node_next(cmark_node *node) {
212 if (node == NULL) {
213 return NULL;
214 } else {
215 return node->next;
216 }
217}
218
219cmark_node *cmark_node_previous(cmark_node *node) {
220 if (node == NULL) {
221 return NULL;
222 } else {
223 return node->prev;
224 }
225}
226
227cmark_node *cmark_node_parent(cmark_node *node) {
228 if (node == NULL) {
229 return NULL;
230 } else {
231 return node->parent;
232 }
233}
234
235cmark_node *cmark_node_first_child(cmark_node *node) {
236 if (node == NULL) {
237 return NULL;
238 } else {
239 return node->first_child;
240 }
241}
242
243cmark_node *cmark_node_last_child(cmark_node *node) {
244 if (node == NULL) {
245 return NULL;
246 } else {
247 return node->last_child;
248 }
249}
250
251static bufsize_t cmark_set_cstr(cmark_mem *mem, unsigned char **dst,
252 const char *src) {
253 unsigned char *old = *dst;
254 bufsize_t len;
255
256 if (src && src[0]) {
257 len = (bufsize_t)strlen(src);
258 *dst = (unsigned char *)mem->realloc(NULL, len + 1);
259 memcpy(*dst, src, len + 1);
260 } else {
261 len = 0;
262 *dst = NULL;
263 }
264 if (old) {
265 mem->free(old);
266 }
267
268 return len;
269}
270
271void *cmark_node_get_user_data(cmark_node *node) {
272 if (node == NULL) {
273 return NULL;
274 } else {
275 return node->user_data;
276 }
277}
278
279int cmark_node_set_user_data(cmark_node *node, void *user_data) {
280 if (node == NULL) {
281 return 0;
282 }
283 node->user_data = user_data;
284 return 1;
285}
286
287const char *cmark_node_get_literal(cmark_node *node) {
288 if (node == NULL) {
289 return NULL;
290 }
291
292 switch (node->type) {
293 case CMARK_NODE_HTML_BLOCK:
294 case CMARK_NODE_TEXT:
295 case CMARK_NODE_HTML_INLINE:
296 case CMARK_NODE_CODE:
297 case CMARK_NODE_CODE_BLOCK:
298 return node->data ? (char *)node->data : "";
299
300 default:
301 break;
302 }
303
304 return NULL;
305}
306
307int cmark_node_set_literal(cmark_node *node, const char *content) {
308 if (node == NULL) {
309 return 0;
310 }
311
312 switch (node->type) {
313 case CMARK_NODE_HTML_BLOCK:
314 case CMARK_NODE_TEXT:
315 case CMARK_NODE_HTML_INLINE:
316 case CMARK_NODE_CODE:
317 case CMARK_NODE_CODE_BLOCK:
318 node->len = cmark_set_cstr(node->mem, &node->data, content);
319 return 1;
320
321 default:
322 break;
323 }
324
325 return 0;
326}
327
328int cmark_node_get_heading_level(cmark_node *node) {
329 if (node == NULL) {
330 return 0;
331 }
332
333 switch (node->type) {
334 case CMARK_NODE_HEADING:
335 return node->as.heading.level;
336
337 default:
338 break;
339 }
340
341 return 0;
342}
343
344int cmark_node_set_heading_level(cmark_node *node, int level) {
345 if (node == NULL || level < 1 || level > 6) {
346 return 0;
347 }
348
349 switch (node->type) {
350 case CMARK_NODE_HEADING:
351 node->as.heading.level = level;
352 return 1;
353
354 default:
355 break;
356 }
357
358 return 0;
359}
360
361cmark_list_type cmark_node_get_list_type(cmark_node *node) {
362 if (node == NULL) {
363 return CMARK_NO_LIST;
364 }
365
366 if (node->type == CMARK_NODE_LIST) {
367 return (cmark_list_type)node->as.list.list_type;
368 } else {
369 return CMARK_NO_LIST;
370 }
371}
372
373int cmark_node_set_list_type(cmark_node *node, cmark_list_type type) {
374 if (!(type == CMARK_BULLET_LIST || type == CMARK_ORDERED_LIST)) {
375 return 0;
376 }
377
378 if (node == NULL) {
379 return 0;
380 }
381
382 if (node->type == CMARK_NODE_LIST) {
383 node->as.list.list_type = (unsigned char)type;
384 return 1;
385 } else {
386 return 0;
387 }
388}
389
390cmark_delim_type cmark_node_get_list_delim(cmark_node *node) {
391 if (node == NULL) {
392 return CMARK_NO_DELIM;
393 }
394
395 if (node->type == CMARK_NODE_LIST) {
396 return (cmark_delim_type)node->as.list.delimiter;
397 } else {
398 return CMARK_NO_DELIM;
399 }
400}
401
402int cmark_node_set_list_delim(cmark_node *node, cmark_delim_type delim) {
403 if (!(delim == CMARK_PERIOD_DELIM || delim == CMARK_PAREN_DELIM)) {
404 return 0;
405 }
406
407 if (node == NULL) {
408 return 0;
409 }
410
411 if (node->type == CMARK_NODE_LIST) {
412 node->as.list.delimiter = (unsigned char)delim;
413 return 1;
414 } else {
415 return 0;
416 }
417}
418
419int cmark_node_get_list_start(cmark_node *node) {
420 if (node == NULL) {
421 return 0;
422 }
423
424 if (node->type == CMARK_NODE_LIST) {
425 return node->as.list.start;
426 } else {
427 return 0;
428 }
429}
430
431int cmark_node_set_list_start(cmark_node *node, int start) {
432 if (node == NULL || start < 0) {
433 return 0;
434 }
435
436 if (node->type == CMARK_NODE_LIST) {
437 node->as.list.start = start;
438 return 1;
439 } else {
440 return 0;
441 }
442}
443
444int cmark_node_get_list_tight(cmark_node *node) {
445 if (node == NULL) {
446 return 0;
447 }
448
449 if (node->type == CMARK_NODE_LIST) {
450 return node->as.list.tight;
451 } else {
452 return 0;
453 }
454}
455
456int cmark_node_set_list_tight(cmark_node *node, int tight) {
457 if (node == NULL) {
458 return 0;
459 }
460
461 if (node->type == CMARK_NODE_LIST) {
462 node->as.list.tight = tight == 1;
463 return 1;
464 } else {
465 return 0;
466 }
467}
468
469const char *cmark_node_get_fence_info(cmark_node *node) {
470 if (node == NULL) {
471 return NULL;
472 }
473
474 if (node->type == CMARK_NODE_CODE_BLOCK) {
475 return node->as.code.info ? (char *)node->as.code.info : "";
476 } else {
477 return NULL;
478 }
479}
480
481int cmark_node_set_fence_info(cmark_node *node, const char *info) {
482 if (node == NULL) {
483 return 0;
484 }
485
486 if (node->type == CMARK_NODE_CODE_BLOCK) {
487 cmark_set_cstr(node->mem, &node->as.code.info, info);
488 return 1;
489 } else {
490 return 0;
491 }
492}
493
494const char *cmark_node_get_url(cmark_node *node) {
495 if (node == NULL) {
496 return NULL;
497 }
498
499 switch (node->type) {
500 case CMARK_NODE_LINK:
501 case CMARK_NODE_IMAGE:
502 return node->as.link.url ? (char *)node->as.link.url : "";
503 default:
504 break;
505 }
506
507 return NULL;
508}
509
510int cmark_node_set_url(cmark_node *node, const char *url) {
511 if (node == NULL) {
512 return 0;
513 }
514
515 switch (node->type) {
516 case CMARK_NODE_LINK:
517 case CMARK_NODE_IMAGE:
518 cmark_set_cstr(node->mem, &node->as.link.url, url);
519 return 1;
520 default:
521 break;
522 }
523
524 return 0;
525}
526
527const char *cmark_node_get_title(cmark_node *node) {
528 if (node == NULL) {
529 return NULL;
530 }
531
532 switch (node->type) {
533 case CMARK_NODE_LINK:
534 case CMARK_NODE_IMAGE:
535 return node->as.link.title ? (char *)node->as.link.title : "";
536 default:
537 break;
538 }
539
540 return NULL;
541}
542
543int cmark_node_set_title(cmark_node *node, const char *title) {
544 if (node == NULL) {
545 return 0;
546 }
547
548 switch (node->type) {
549 case CMARK_NODE_LINK:
550 case CMARK_NODE_IMAGE:
551 cmark_set_cstr(node->mem, &node->as.link.title, title);
552 return 1;
553 default:
554 break;
555 }
556
557 return 0;
558}
559
560const char *cmark_node_get_on_enter(cmark_node *node) {
561 if (node == NULL) {
562 return NULL;
563 }
564
565 switch (node->type) {
566 case CMARK_NODE_CUSTOM_INLINE:
567 case CMARK_NODE_CUSTOM_BLOCK:
568 return node->as.custom.on_enter ? (char *)node->as.custom.on_enter : "";
569 default:
570 break;
571 }
572
573 return NULL;
574}
575
576int cmark_node_set_on_enter(cmark_node *node, const char *on_enter) {
577 if (node == NULL) {
578 return 0;
579 }
580
581 switch (node->type) {
582 case CMARK_NODE_CUSTOM_INLINE:
583 case CMARK_NODE_CUSTOM_BLOCK:
584 cmark_set_cstr(node->mem, &node->as.custom.on_enter, on_enter);
585 return 1;
586 default:
587 break;
588 }
589
590 return 0;
591}
592
593const char *cmark_node_get_on_exit(cmark_node *node) {
594 if (node == NULL) {
595 return NULL;
596 }
597
598 switch (node->type) {
599 case CMARK_NODE_CUSTOM_INLINE:
600 case CMARK_NODE_CUSTOM_BLOCK:
601 return node->as.custom.on_exit ? (char *)node->as.custom.on_exit : "";
602 default:
603 break;
604 }
605
606 return NULL;
607}
608
609int cmark_node_set_on_exit(cmark_node *node, const char *on_exit) {
610 if (node == NULL) {
611 return 0;
612 }
613
614 switch (node->type) {
615 case CMARK_NODE_CUSTOM_INLINE:
616 case CMARK_NODE_CUSTOM_BLOCK:
617 cmark_set_cstr(node->mem, &node->as.custom.on_exit, on_exit);
618 return 1;
619 default:
620 break;
621 }
622
623 return 0;
624}
625
626int cmark_node_get_start_line(cmark_node *node) {
627 if (node == NULL) {
628 return 0;
629 }
630 return node->start_line;
631}
632
633int cmark_node_get_start_column(cmark_node *node) {
634 if (node == NULL) {
635 return 0;
636 }
637 return node->start_column;
638}
639
640int cmark_node_get_end_line(cmark_node *node) {
641 if (node == NULL) {
642 return 0;
643 }
644 return node->end_line;
645}
646
647int cmark_node_get_end_column(cmark_node *node) {
648 if (node == NULL) {
649 return 0;
650 }
651 return node->end_column;
652}
653
654// Unlink a node without adjusting its next, prev, and parent pointers.
655static void S_node_unlink(cmark_node *node) {
656 if (node == NULL) {
657 return;
658 }
659
660 if (node->prev) {
661 node->prev->next = node->next;
662 }
663 if (node->next) {
664 node->next->prev = node->prev;
665 }
666
667 // Adjust first_child and last_child of parent.
668 cmark_node *parent = node->parent;
669 if (parent) {
670 if (parent->first_child == node) {
671 parent->first_child = node->next;
672 }
673 if (parent->last_child == node) {
674 parent->last_child = node->prev;
675 }
676 }
677}
678
679void cmark_node_unlink(cmark_node *node) {
680 S_node_unlink(node);
681
682 node->next = NULL;
683 node->prev = NULL;
684 node->parent = NULL;
685}
686
687int cmark_node_insert_before(cmark_node *node, cmark_node *sibling) {
688 if (node == NULL || sibling == NULL) {
689 return 0;
690 }
691
692 if (!node->parent || !S_can_contain(node->parent, sibling)) {
693 return 0;
694 }
695
696 S_node_unlink(sibling);
697
698 cmark_node *old_prev = node->prev;
699
700 // Insert 'sibling' between 'old_prev' and 'node'.
701 if (old_prev) {
702 old_prev->next = sibling;
703 }
704 sibling->prev = old_prev;
705 sibling->next = node;
706 node->prev = sibling;
707
708 // Set new parent.
709 cmark_node *parent = node->parent;
710 sibling->parent = parent;
711
712 // Adjust first_child of parent if inserted as first child.
713 if (parent && !old_prev) {
714 parent->first_child = sibling;
715 }
716
717 return 1;
718}
719
720int cmark_node_insert_after(cmark_node *node, cmark_node *sibling) {
721 if (node == NULL || sibling == NULL) {
722 return 0;
723 }
724
725 if (!node->parent || !S_can_contain(node->parent, sibling)) {
726 return 0;
727 }
728
729 S_node_unlink(sibling);
730
731 cmark_node *old_next = node->next;
732
733 // Insert 'sibling' between 'node' and 'old_next'.
734 if (old_next) {
735 old_next->prev = sibling;
736 }
737 sibling->next = old_next;
738 sibling->prev = node;
739 node->next = sibling;
740
741 // Set new parent.
742 cmark_node *parent = node->parent;
743 sibling->parent = parent;
744
745 // Adjust last_child of parent if inserted as last child.
746 if (parent && !old_next) {
747 parent->last_child = sibling;
748 }
749
750 return 1;
751}
752
753int cmark_node_replace(cmark_node *oldnode, cmark_node *newnode) {
754 if (!cmark_node_insert_before(oldnode, newnode)) {
755 return 0;
756 }
757 cmark_node_unlink(oldnode);
758 return 1;
759}
760
761int cmark_node_prepend_child(cmark_node *node, cmark_node *child) {
762 if (!S_can_contain(node, child)) {
763 return 0;
764 }
765
766 S_node_unlink(child);
767
768 cmark_node *old_first_child = node->first_child;
769
770 child->next = old_first_child;
771 child->prev = NULL;
772 child->parent = node;
773 node->first_child = child;
774
775 if (old_first_child) {
776 old_first_child->prev = child;
777 } else {
778 // Also set last_child if node previously had no children.
779 node->last_child = child;
780 }
781
782 return 1;
783}
784
785int cmark_node_append_child(cmark_node *node, cmark_node *child) {
786 if (!S_can_contain(node, child)) {
787 return 0;
788 }
789
790 S_node_unlink(child);
791
792 cmark_node *old_last_child = node->last_child;
793
794 child->next = NULL;
795 child->prev = old_last_child;
796 child->parent = node;
797 node->last_child = child;
798
799 if (old_last_child) {
800 old_last_child->next = child;
801 } else {
802 // Also set first_child if node previously had no children.
803 node->first_child = child;
804 }
805
806 return 1;
807}
808
809static void S_print_error(FILE *out, cmark_node *node, const char *elem) {
810 if (out == NULL) {
811 return;
812 }
813 fprintf(out, "Invalid '%s' in node type %s at %d:%d\n", elem,
814 cmark_node_get_type_string(node), node->start_line,
815 node->start_column);
816}
817
818int cmark_node_check(cmark_node *node, FILE *out) {
819 cmark_node *cur;
820 int errors = 0;
821
822 if (!node) {
823 return 0;
824 }
825
826 cur = node;
827 for (;;) {
828 if (cur->first_child) {
829 if (cur->first_child->prev != NULL) {
830 S_print_error(out, cur->first_child, "prev");
831 cur->first_child->prev = NULL;
832 ++errors;
833 }
834 if (cur->first_child->parent != cur) {
835 S_print_error(out, cur->first_child, "parent");
836 cur->first_child->parent = cur;
837 ++errors;
838 }
839 cur = cur->first_child;
840 continue;
841 }
842
843 next_sibling:
844 if (cur == node) {
845 break;
846 }
847 if (cur->next) {
848 if (cur->next->prev != cur) {
849 S_print_error(out, cur->next, "prev");
850 cur->next->prev = cur;
851 ++errors;
852 }
853 if (cur->next->parent != cur->parent) {
854 S_print_error(out, cur->next, "parent");
855 cur->next->parent = cur->parent;
856 ++errors;
857 }
858 cur = cur->next;
859 continue;
860 }
861
862 if (cur->parent->last_child != cur) {
863 S_print_error(out, cur->parent, "last_child");
864 cur->parent->last_child = cur;
865 ++errors;
866 }
867 cur = cur->parent;
868 goto next_sibling;
869 }
870
871 return errors;
872}
873