commonmarker 0.23.6 → 0.23.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of commonmarker might be problematic. Click here for more details.

@@ -1304,4 +1304,5 @@ __attribute__((visibility("default"))) void Init_commonmarker() {
1304
1304
  rb_define_method(rb_cNode, "html_escape_html", rb_html_escape_html, 1);
1305
1305
 
1306
1306
  cmark_gfm_core_extensions_ensure_registered();
1307
+ cmark_init_standard_node_flags();
1307
1308
  }
@@ -63,10 +63,16 @@ static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *
63
63
  if (renderer->written_footnote_ix >= renderer->footnote_ix)
64
64
  return false;
65
65
  renderer->written_footnote_ix = renderer->footnote_ix;
66
+ char m[32];
67
+ snprintf(m, sizeof(m), "%d", renderer->written_footnote_ix);
66
68
 
67
69
  cmark_strbuf_puts(html, "<a href=\"#fnref-");
68
70
  houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
69
- cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩</a>");
71
+ cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"");
72
+ cmark_strbuf_puts(html, m);
73
+ cmark_strbuf_puts(html, "\" aria-label=\"Back to reference ");
74
+ cmark_strbuf_puts(html, m);
75
+ cmark_strbuf_puts(html, "\">↩</a>");
70
76
 
71
77
  if (node->footnote.def_count > 1)
72
78
  {
@@ -78,7 +84,15 @@ static bool S_put_footnote_backref(cmark_html_renderer *renderer, cmark_strbuf *
78
84
  houdini_escape_href(html, node->as.literal.data, node->as.literal.len);
79
85
  cmark_strbuf_puts(html, "-");
80
86
  cmark_strbuf_puts(html, n);
81
- cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref aria-label=\"Back to content\">↩<sup class=\"footnote-ref\">");
87
+ cmark_strbuf_puts(html, "\" class=\"footnote-backref\" data-footnote-backref data-footnote-backref-idx=\"");
88
+ cmark_strbuf_puts(html, m);
89
+ cmark_strbuf_puts(html, "-");
90
+ cmark_strbuf_puts(html, n);
91
+ cmark_strbuf_puts(html, "\" aria-label=\"Back to reference ");
92
+ cmark_strbuf_puts(html, m);
93
+ cmark_strbuf_puts(html, "-");
94
+ cmark_strbuf_puts(html, n);
95
+ cmark_strbuf_puts(html, "\">↩<sup class=\"footnote-ref\">");
82
96
  cmark_strbuf_puts(html, n);
83
97
  cmark_strbuf_puts(html, "</sup></a>");
84
98
  }
@@ -35,7 +35,6 @@ static const char *RIGHTSINGLEQUOTE = "\xE2\x80\x99";
35
35
 
36
36
  typedef struct bracket {
37
37
  struct bracket *previous;
38
- struct delimiter *previous_delimiter;
39
38
  cmark_node *inl_text;
40
39
  bufsize_t position;
41
40
  bool image;
@@ -45,9 +44,15 @@ typedef struct bracket {
45
44
  bool in_bracket_image1;
46
45
  } bracket;
47
46
 
47
+ #define FLAG_SKIP_HTML_CDATA (1u << 0)
48
+ #define FLAG_SKIP_HTML_DECLARATION (1u << 1)
49
+ #define FLAG_SKIP_HTML_PI (1u << 2)
50
+ #define FLAG_SKIP_HTML_COMMENT (1u << 3)
51
+
48
52
  typedef struct subject{
49
53
  cmark_mem *mem;
50
54
  cmark_chunk input;
55
+ unsigned flags;
51
56
  int line;
52
57
  bufsize_t pos;
53
58
  int block_offset;
@@ -57,6 +62,7 @@ typedef struct subject{
57
62
  bracket *last_bracket;
58
63
  bufsize_t backticks[MAXBACKTICKS + 1];
59
64
  bool scanned_for_backticks;
65
+ bool no_link_openers;
60
66
  } subject;
61
67
 
62
68
  // Extensions may populate this.
@@ -111,6 +117,24 @@ static cmark_node *make_str_with_entities(subject *subj,
111
117
  }
112
118
  }
113
119
 
120
+ // Like cmark_node_append_child but without costly sanity checks.
121
+ // Assumes that child was newly created.
122
+ static void append_child(cmark_node *node, cmark_node *child) {
123
+ cmark_node *old_last_child = node->last_child;
124
+
125
+ child->next = NULL;
126
+ child->prev = old_last_child;
127
+ child->parent = node;
128
+ node->last_child = child;
129
+
130
+ if (old_last_child) {
131
+ old_last_child->next = child;
132
+ } else {
133
+ // Also set first_child if node previously had no children.
134
+ node->first_child = child;
135
+ }
136
+ }
137
+
114
138
  // Duplicate a chunk by creating a copy of the buffer not by reusing the
115
139
  // buffer like cmark_chunk_dup does.
116
140
  static cmark_chunk chunk_clone(cmark_mem *mem, cmark_chunk *src) {
@@ -154,7 +178,7 @@ static CMARK_INLINE cmark_node *make_autolink(subject *subj,
154
178
  link->start_line = link->end_line = subj->line;
155
179
  link->start_column = start_column + 1;
156
180
  link->end_column = end_column + 1;
157
- cmark_node_append_child(link, make_str_with_entities(subj, start_column + 1, end_column - 1, &url));
181
+ append_child(link, make_str_with_entities(subj, start_column + 1, end_column - 1, &url));
158
182
  return link;
159
183
  }
160
184
 
@@ -163,6 +187,7 @@ static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset,
163
187
  int i;
164
188
  e->mem = mem;
165
189
  e->input = *chunk;
190
+ e->flags = 0;
166
191
  e->line = line_number;
167
192
  e->pos = 0;
168
193
  e->block_offset = block_offset;
@@ -174,6 +199,7 @@ static void subject_from_buf(cmark_mem *mem, int line_number, int block_offset,
174
199
  e->backticks[i] = 0;
175
200
  }
176
201
  e->scanned_for_backticks = false;
202
+ e->no_link_openers = true;
177
203
  }
178
204
 
179
205
  static CMARK_INLINE int isbacktick(int c) { return (c == '`'); }
@@ -505,6 +531,7 @@ static void push_delimiter(subject *subj, unsigned char c, bool can_open,
505
531
  delim->can_open = can_open;
506
532
  delim->can_close = can_close;
507
533
  delim->inl_text = inl_text;
534
+ delim->position = subj->pos;
508
535
  delim->length = inl_text->as.literal.len;
509
536
  delim->previous = subj->last_delim;
510
537
  delim->next = NULL;
@@ -525,7 +552,6 @@ static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
525
552
  b->active = true;
526
553
  b->inl_text = inl_text;
527
554
  b->previous = subj->last_bracket;
528
- b->previous_delimiter = subj->last_delim;
529
555
  b->position = subj->pos;
530
556
  b->bracket_after = false;
531
557
  if (image) {
@@ -534,6 +560,9 @@ static void push_bracket(subject *subj, bool image, cmark_node *inl_text) {
534
560
  b->in_bracket_image0 = true;
535
561
  }
536
562
  subj->last_bracket = b;
563
+ if (!image) {
564
+ subj->no_link_openers = false;
565
+ }
537
566
  }
538
567
 
539
568
  // Assumes the subject has a c at the current position.
@@ -640,12 +669,13 @@ static cmark_syntax_extension *get_extension_for_special_char(cmark_parser *pars
640
669
  return NULL;
641
670
  }
642
671
 
643
- static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *stack_bottom) {
644
- delimiter *closer = subj->last_delim;
672
+ static void process_emphasis(cmark_parser *parser, subject *subj, bufsize_t stack_bottom) {
673
+ delimiter *candidate;
674
+ delimiter *closer = NULL;
645
675
  delimiter *opener;
646
676
  delimiter *old_closer;
647
677
  bool opener_found;
648
- delimiter *openers_bottom[3][128];
678
+ bufsize_t openers_bottom[3][128];
649
679
  int i;
650
680
 
651
681
  // initialize openers_bottom:
@@ -658,8 +688,10 @@ static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *sta
658
688
  }
659
689
 
660
690
  // move back to first relevant delim.
661
- while (closer != NULL && closer->previous != stack_bottom) {
662
- closer = closer->previous;
691
+ candidate = subj->last_delim;
692
+ while (candidate != NULL && candidate->position >= stack_bottom) {
693
+ closer = candidate;
694
+ candidate = candidate->previous;
663
695
  }
664
696
 
665
697
  // now move forward, looking for closers, and handling each
@@ -669,8 +701,8 @@ static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *sta
669
701
  // Now look backwards for first matching opener:
670
702
  opener = closer->previous;
671
703
  opener_found = false;
672
- while (opener != NULL && opener != stack_bottom &&
673
- opener != openers_bottom[closer->length % 3][closer->delim_char]) {
704
+ while (opener != NULL && opener->position >= stack_bottom &&
705
+ opener->position >= openers_bottom[closer->length % 3][closer->delim_char]) {
674
706
  if (opener->can_open && opener->delim_char == closer->delim_char) {
675
707
  // interior closer of size 2 can't match opener of size 1
676
708
  // or of size 1 can't match 2
@@ -696,27 +728,29 @@ static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *sta
696
728
  } else {
697
729
  closer = closer->next;
698
730
  }
699
- } else if (closer->delim_char == '\'') {
731
+ } else if (closer->delim_char == '\'' || closer->delim_char == '"') {
700
732
  cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
701
- closer->inl_text->as.literal = cmark_chunk_literal(RIGHTSINGLEQUOTE);
702
- if (opener_found) {
703
- cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
704
- opener->inl_text->as.literal = cmark_chunk_literal(LEFTSINGLEQUOTE);
733
+ if (closer->delim_char == '\'') {
734
+ closer->inl_text->as.literal = cmark_chunk_literal(RIGHTSINGLEQUOTE);
735
+ } else {
736
+ closer->inl_text->as.literal = cmark_chunk_literal(RIGHTDOUBLEQUOTE);
705
737
  }
706
738
  closer = closer->next;
707
- } else if (closer->delim_char == '"') {
708
- cmark_chunk_free(subj->mem, &closer->inl_text->as.literal);
709
- closer->inl_text->as.literal = cmark_chunk_literal(RIGHTDOUBLEQUOTE);
710
739
  if (opener_found) {
711
740
  cmark_chunk_free(subj->mem, &opener->inl_text->as.literal);
712
- opener->inl_text->as.literal = cmark_chunk_literal(LEFTDOUBLEQUOTE);
741
+ if (old_closer->delim_char == '\'') {
742
+ opener->inl_text->as.literal = cmark_chunk_literal(LEFTSINGLEQUOTE);
743
+ } else {
744
+ opener->inl_text->as.literal = cmark_chunk_literal(LEFTDOUBLEQUOTE);
745
+ }
746
+ remove_delimiter(subj, opener);
747
+ remove_delimiter(subj, old_closer);
713
748
  }
714
- closer = closer->next;
715
749
  }
716
750
  if (!opener_found) {
717
751
  // set lower bound for future searches for openers
718
752
  openers_bottom[old_closer->length % 3][old_closer->delim_char] =
719
- old_closer->previous;
753
+ old_closer->position;
720
754
  if (!old_closer->can_open) {
721
755
  // we can remove a closer that can't be an
722
756
  // opener, once we've seen there's no
@@ -729,7 +763,8 @@ static void process_emphasis(cmark_parser *parser, subject *subj, delimiter *sta
729
763
  }
730
764
  }
731
765
  // free all delimiters in list until stack_bottom:
732
- while (subj->last_delim != NULL && subj->last_delim != stack_bottom) {
766
+ while (subj->last_delim != NULL &&
767
+ subj->last_delim->position >= stack_bottom) {
733
768
  remove_delimiter(subj, subj->last_delim);
734
769
  }
735
770
  }
@@ -768,7 +803,8 @@ static delimiter *S_insert_emph(subject *subj, delimiter *opener,
768
803
  tmp = opener_inl->next;
769
804
  while (tmp && tmp != closer_inl) {
770
805
  tmpnext = tmp->next;
771
- cmark_node_append_child(emph, tmp);
806
+ cmark_node_unlink(tmp);
807
+ append_child(emph, tmp);
772
808
  tmp = tmpnext;
773
809
  }
774
810
  cmark_node_insert_after(opener_inl, emph);
@@ -899,7 +935,63 @@ static cmark_node *handle_pointy_brace(subject *subj, int options) {
899
935
  }
900
936
 
901
937
  // finally, try to match an html tag
902
- matchlen = scan_html_tag(&subj->input, subj->pos);
938
+ if (subj->pos + 2 <= subj->input.len) {
939
+ int c = subj->input.data[subj->pos];
940
+ if (c == '!' && (subj->flags & FLAG_SKIP_HTML_COMMENT) == 0) {
941
+ c = subj->input.data[subj->pos+1];
942
+ if (c == '-' && subj->input.data[subj->pos+2] == '-') {
943
+ if (subj->input.data[subj->pos+3] == '>') {
944
+ matchlen = 4;
945
+ } else if (subj->input.data[subj->pos+3] == '-' &&
946
+ subj->input.data[subj->pos+4] == '>') {
947
+ matchlen = 5;
948
+ } else {
949
+ matchlen = scan_html_comment(&subj->input, subj->pos + 1);
950
+ if (matchlen > 0) {
951
+ matchlen += 1; // prefix "<"
952
+ } else { // no match through end of input: set a flag so
953
+ // we don't reparse looking for -->:
954
+ subj->flags |= FLAG_SKIP_HTML_COMMENT;
955
+ }
956
+ }
957
+ } else if (c == '[') {
958
+ if ((subj->flags & FLAG_SKIP_HTML_CDATA) == 0) {
959
+ matchlen = scan_html_cdata(&subj->input, subj->pos + 2);
960
+ if (matchlen > 0) {
961
+ // The regex doesn't require the final "]]>". But if we're not at
962
+ // the end of input, it must come after the match. Otherwise,
963
+ // disable subsequent scans to avoid quadratic behavior.
964
+ matchlen += 5; // prefix "![", suffix "]]>"
965
+ if (subj->pos + matchlen > subj->input.len) {
966
+ subj->flags |= FLAG_SKIP_HTML_CDATA;
967
+ matchlen = 0;
968
+ }
969
+ }
970
+ }
971
+ } else if ((subj->flags & FLAG_SKIP_HTML_DECLARATION) == 0) {
972
+ matchlen = scan_html_declaration(&subj->input, subj->pos + 1);
973
+ if (matchlen > 0) {
974
+ matchlen += 2; // prefix "!", suffix ">"
975
+ if (subj->pos + matchlen > subj->input.len) {
976
+ subj->flags |= FLAG_SKIP_HTML_DECLARATION;
977
+ matchlen = 0;
978
+ }
979
+ }
980
+ }
981
+ } else if (c == '?') {
982
+ if ((subj->flags & FLAG_SKIP_HTML_PI) == 0) {
983
+ // Note that we allow an empty match.
984
+ matchlen = scan_html_pi(&subj->input, subj->pos + 1);
985
+ matchlen += 3; // prefix "?", suffix "?>"
986
+ if (subj->pos + matchlen > subj->input.len) {
987
+ subj->flags |= FLAG_SKIP_HTML_PI;
988
+ matchlen = 0;
989
+ }
990
+ }
991
+ } else {
992
+ matchlen = scan_html_tag(&subj->input, subj->pos);
993
+ }
994
+ }
903
995
  if (matchlen > 0) {
904
996
  contents = cmark_chunk_dup(&subj->input, subj->pos - 1, matchlen + 1);
905
997
  subj->pos += matchlen;
@@ -1065,16 +1157,16 @@ static cmark_node *handle_close_bracket(cmark_parser *parser, subject *subj) {
1065
1157
  return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
1066
1158
  }
1067
1159
 
1068
- if (!opener->active) {
1160
+ // If we got here, we matched a potential link/image text.
1161
+ // Now we check to see if it's a link/image.
1162
+ is_image = opener->image;
1163
+
1164
+ if (!is_image && subj->no_link_openers) {
1069
1165
  // take delimiter off stack
1070
1166
  pop_bracket(subj);
1071
1167
  return make_str(subj, subj->pos - 1, subj->pos - 1, cmark_chunk_literal("]"));
1072
1168
  }
1073
1169
 
1074
- // If we got here, we matched a potential link/image text.
1075
- // Now we check to see if it's a link/image.
1076
- is_image = opener->image;
1077
-
1078
1170
  after_link_text_pos = subj->pos;
1079
1171
 
1080
1172
  // First, look for an inline link.
@@ -1193,7 +1285,7 @@ noMatch:
1193
1285
  // being replacing the opening '[' text node with a `^footnote-ref]` node.
1194
1286
  cmark_node_insert_before(opener->inl_text, fnref);
1195
1287
 
1196
- process_emphasis(parser, subj, opener->previous_delimiter);
1288
+ process_emphasis(parser, subj, opener->position);
1197
1289
  // sometimes, the footnote reference text gets parsed into multiple nodes
1198
1290
  // i.e. '[^example]' parsed into '[', '^exam', 'ple]'.
1199
1291
  // this happens for ex with the autolink extension. when the autolinker
@@ -1238,42 +1330,22 @@ match:
1238
1330
  tmp = opener->inl_text->next;
1239
1331
  while (tmp) {
1240
1332
  tmpnext = tmp->next;
1241
- cmark_node_append_child(inl, tmp);
1333
+ cmark_node_unlink(tmp);
1334
+ append_child(inl, tmp);
1242
1335
  tmp = tmpnext;
1243
1336
  }
1244
1337
 
1245
1338
  // Free the bracket [:
1246
1339
  cmark_node_free(opener->inl_text);
1247
1340
 
1248
- process_emphasis(parser, subj, opener->previous_delimiter);
1341
+ process_emphasis(parser, subj, opener->position);
1249
1342
  pop_bracket(subj);
1250
1343
 
1251
- // Now, if we have a link, we also want to deactivate earlier link
1252
- // delimiters. (This code can be removed if we decide to allow links
1344
+ // Now, if we have a link, we also want to deactivate links until
1345
+ // we get a new opener. (This code can be removed if we decide to allow links
1253
1346
  // inside links.)
1254
1347
  if (!is_image) {
1255
- opener = subj->last_bracket;
1256
- while (opener != NULL) {
1257
- if (!opener->image) {
1258
- if (!opener->active) {
1259
- break;
1260
- } else {
1261
- opener->active = false;
1262
- }
1263
- }
1264
- opener = opener->previous;
1265
- }
1266
- bool in_bracket_image1 = false;
1267
- if (opener) {
1268
- in_bracket_image1 = opener->in_bracket_image1;
1269
- }
1270
- bracket *opener2 = subj->last_bracket;
1271
- while (opener2 != opener) {
1272
- if (opener2->image) {
1273
- opener2->in_bracket_image1 = in_bracket_image1;
1274
- }
1275
- opener2 = opener2->previous;
1276
- }
1348
+ subj->no_link_openers = true;
1277
1349
  }
1278
1350
 
1279
1351
  return NULL;
@@ -1451,7 +1523,7 @@ static int parse_inline(cmark_parser *parser, subject *subj, cmark_node *parent,
1451
1523
  new_inl = make_str(subj, startpos, endpos - 1, contents);
1452
1524
  }
1453
1525
  if (new_inl != NULL) {
1454
- cmark_node_append_child(parent, new_inl);
1526
+ append_child(parent, new_inl);
1455
1527
  }
1456
1528
 
1457
1529
  return 1;
@@ -1470,7 +1542,7 @@ void cmark_parse_inlines(cmark_parser *parser,
1470
1542
  while (!is_eof(&subj) && parse_inline(parser, &subj, parent, options))
1471
1543
  ;
1472
1544
 
1473
- process_emphasis(parser, &subj, NULL);
1545
+ process_emphasis(parser, &subj, 0);
1474
1546
  // free bracket and delim stack
1475
1547
  while (subj.last_delim) {
1476
1548
  remove_delimiter(&subj, subj.last_delim);
@@ -51,7 +51,7 @@ refsearch(const void *label, const void *p2) {
51
51
  }
52
52
 
53
53
  static void sort_map(cmark_map *map) {
54
- unsigned int i = 0, last = 0, size = map->size;
54
+ size_t i = 0, last = 0, size = map->size;
55
55
  cmark_map_entry *r = map->refs, **sorted = NULL;
56
56
 
57
57
  sorted = (cmark_map_entry **)map->mem->calloc(size, sizeof(cmark_map_entry *));
@@ -73,6 +73,7 @@ static void sort_map(cmark_map *map) {
73
73
 
74
74
  cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label) {
75
75
  cmark_map_entry **ref = NULL;
76
+ cmark_map_entry *r = NULL;
76
77
  unsigned char *norm;
77
78
 
78
79
  if (label->len < 1 || label->len > MAX_LINK_LABEL_LENGTH)
@@ -91,10 +92,15 @@ cmark_map_entry *cmark_map_lookup(cmark_map *map, cmark_chunk *label) {
91
92
  ref = (cmark_map_entry **)bsearch(norm, map->sorted, map->size, sizeof(cmark_map_entry *), refsearch);
92
93
  map->mem->free(norm);
93
94
 
94
- if (!ref)
95
- return NULL;
95
+ if (ref != NULL) {
96
+ r = ref[0];
97
+ /* Check for expansion limit */
98
+ if (r->size > map->max_ref_size - map->ref_size)
99
+ return NULL;
100
+ map->ref_size += r->size;
101
+ }
96
102
 
97
- return ref[0];
103
+ return r;
98
104
  }
99
105
 
100
106
  void cmark_map_free(cmark_map *map) {
@@ -118,5 +124,6 @@ cmark_map *cmark_map_new(cmark_mem *mem, cmark_map_free_f free) {
118
124
  cmark_map *map = (cmark_map *)mem->calloc(1, sizeof(cmark_map));
119
125
  map->mem = mem;
120
126
  map->free = free;
127
+ map->max_ref_size = UINT_MAX;
121
128
  return map;
122
129
  }
@@ -10,7 +10,8 @@ extern "C" {
10
10
  struct cmark_map_entry {
11
11
  struct cmark_map_entry *next;
12
12
  unsigned char *label;
13
- unsigned int age;
13
+ size_t age;
14
+ size_t size;
14
15
  };
15
16
 
16
17
  typedef struct cmark_map_entry cmark_map_entry;
@@ -23,7 +24,9 @@ struct cmark_map {
23
24
  cmark_mem *mem;
24
25
  cmark_map_entry *refs;
25
26
  cmark_map_entry **sorted;
26
- unsigned int size;
27
+ size_t size;
28
+ size_t ref_size;
29
+ size_t max_ref_size;
27
30
  cmark_map_free_f free;
28
31
  };
29
32
 
@@ -9,6 +9,28 @@ static void S_node_unlink(cmark_node *node);
9
9
 
10
10
  #define NODE_MEM(node) cmark_node_mem(node)
11
11
 
12
+ void cmark_register_node_flag(cmark_node_internal_flags *flags) {
13
+ static cmark_node_internal_flags nextflag = CMARK_NODE__REGISTER_FIRST;
14
+
15
+ // flags should be a pointer to a global variable and this function
16
+ // should only be called once to initialize its value.
17
+ if (*flags) {
18
+ fprintf(stderr, "flag initialization error in cmark_register_node_flag\n");
19
+ abort();
20
+ }
21
+
22
+ // Check that we haven't run out of bits.
23
+ if (nextflag == 0) {
24
+ fprintf(stderr, "too many flags in cmark_register_node_flag\n");
25
+ abort();
26
+ }
27
+
28
+ *flags = nextflag;
29
+ nextflag <<= 1;
30
+ }
31
+
32
+ void cmark_init_standard_node_flags() {}
33
+
12
34
  bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type) {
13
35
  if (child_type == CMARK_NODE_DOCUMENT) {
14
36
  return false;
@@ -301,6 +323,14 @@ cmark_node *cmark_node_last_child(cmark_node *node) {
301
323
  }
302
324
  }
303
325
 
326
+ cmark_node *cmark_node_parent_footnote_def(cmark_node *node) {
327
+ if (node == NULL) {
328
+ return NULL;
329
+ } else {
330
+ return node->parent_footnote_def;
331
+ }
332
+ }
333
+
304
334
  void *cmark_node_get_user_data(cmark_node *node) {
305
335
  if (node == NULL) {
306
336
  return NULL;
@@ -52,8 +52,14 @@ enum cmark_node__internal_flags {
52
52
  CMARK_NODE__OPEN = (1 << 0),
53
53
  CMARK_NODE__LAST_LINE_BLANK = (1 << 1),
54
54
  CMARK_NODE__LAST_LINE_CHECKED = (1 << 2),
55
+
56
+ // Extensions can register custom flags by calling `cmark_register_node_flag`.
57
+ // This is the starting value for the custom flags.
58
+ CMARK_NODE__REGISTER_FIRST = (1 << 3),
55
59
  };
56
60
 
61
+ typedef uint16_t cmark_node_internal_flags;
62
+
57
63
  struct cmark_node {
58
64
  cmark_strbuf content;
59
65
 
@@ -72,7 +78,7 @@ struct cmark_node {
72
78
  int end_column;
73
79
  int internal_offset;
74
80
  uint16_t type;
75
- uint16_t flags;
81
+ cmark_node_internal_flags flags;
76
82
 
77
83
  cmark_syntax_extension *extension;
78
84
 
@@ -95,6 +101,26 @@ struct cmark_node {
95
101
  } as;
96
102
  };
97
103
 
104
+ /**
105
+ * Syntax extensions can use this function to register a custom node
106
+ * flag. The flags are stored in the `flags` field of the `cmark_node`
107
+ * struct. The `flags` parameter should be the address of a global variable
108
+ * which will store the flag value.
109
+ */
110
+ CMARK_GFM_EXPORT
111
+ void cmark_register_node_flag(cmark_node_internal_flags *flags);
112
+
113
+ /**
114
+ * DEPRECATED.
115
+ *
116
+ * This function was added in cmark-gfm version 0.29.0.gfm.7, and was
117
+ * required to be called at program start time, which caused
118
+ * backwards-compatibility issues in applications that use cmark-gfm as a
119
+ * library. It is now a no-op.
120
+ */
121
+ CMARK_GFM_EXPORT
122
+ void cmark_init_standard_node_flags();
123
+
98
124
  static CMARK_INLINE cmark_mem *cmark_node_mem(cmark_node *node) {
99
125
  return node->content.mem;
100
126
  }
@@ -46,6 +46,7 @@ struct cmark_parser {
46
46
  /* Options set by the user, see the Options section in cmark.h */
47
47
  int options;
48
48
  bool last_buffer_ended_with_cr;
49
+ size_t total_size;
49
50
  cmark_llist *syntax_extensions;
50
51
  cmark_llist *inline_syntax_extensions;
51
52
  cmark_ispunct_func backslash_ispunct;
@@ -32,6 +32,7 @@ void cmark_reference_create(cmark_map *map, cmark_chunk *label,
32
32
  ref->title = cmark_clean_title(map->mem, title);
33
33
  ref->entry.age = map->size;
34
34
  ref->entry.next = map->refs;
35
+ ref->entry.size = ref->url.len + ref->title.len;
35
36
 
36
37
  map->refs = (cmark_map_entry *)ref;
37
38
  map->size++;