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