commonmarker 0.23.8 → 0.23.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/commonmarker/autolink.c +1 -1
- data/ext/commonmarker/blocks.c +15 -3
- data/ext/commonmarker/cmark-gfm.h +11 -0
- data/ext/commonmarker/cmark-gfm_version.h +2 -2
- data/ext/commonmarker/commonmark.c +19 -34
- data/ext/commonmarker/html.c +6 -4
- data/ext/commonmarker/latex.c +6 -4
- data/ext/commonmarker/man.c +7 -11
- data/ext/commonmarker/node.c +47 -11
- data/ext/commonmarker/node.h +17 -1
- data/ext/commonmarker/plaintext.c +12 -29
- data/ext/commonmarker/render.c +15 -7
- data/ext/commonmarker/table.c +91 -46
- data/ext/commonmarker/xml.c +2 -1
- data/lib/commonmarker/renderer/html_renderer.rb +6 -2
- data/lib/commonmarker/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8197daf1b6113b658c30459c0c4436ee688bfdd2381d3f541ff31bf3c3177bae
|
4
|
+
data.tar.gz: 25761b81607e28e9f569a61f502913c55f88319023b7d12e362d87dd37a935f7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 822690178eaaedc1e173e81e1d7c302d1670b95d362ca056ac9552df81a46861f582e4624698325e7d9bac347079fb4af6f99d88fbd832d40b84c4cbaff16cfc
|
7
|
+
data.tar.gz: 19150ccc6ec605f469e09fcf56a4dcb40f5b6e1125ca890bad8a2e6dc8931abcf82c533535222340c6377b16f243dd95717b7158bf2836118b8e1542802a6afe
|
data/ext/commonmarker/autolink.c
CHANGED
@@ -296,7 +296,7 @@ static cmark_node *match(cmark_syntax_extension *ext, cmark_parser *parser,
|
|
296
296
|
// inline was finished in inlines.c.
|
297
297
|
}
|
298
298
|
|
299
|
-
static bool validate_protocol(char protocol[], uint8_t *data, size_t rewind, size_t max_rewind) {
|
299
|
+
static bool validate_protocol(const char protocol[], uint8_t *data, size_t rewind, size_t max_rewind) {
|
300
300
|
size_t len = strlen(protocol);
|
301
301
|
|
302
302
|
if (len > (max_rewind - rewind)) {
|
data/ext/commonmarker/blocks.c
CHANGED
@@ -27,6 +27,14 @@
|
|
27
27
|
#define CODE_INDENT 4
|
28
28
|
#define TAB_STOP 4
|
29
29
|
|
30
|
+
/**
|
31
|
+
* Very deeply nested lists can cause quadratic performance issues.
|
32
|
+
* This constant is used in open_new_blocks() to limit the nesting
|
33
|
+
* depth. It is unlikely that a non-contrived markdown document will
|
34
|
+
* be nested this deeply.
|
35
|
+
*/
|
36
|
+
#define MAX_LIST_DEPTH 100
|
37
|
+
|
30
38
|
#ifndef MIN
|
31
39
|
#define MIN(x, y) ((x < y) ? x : y)
|
32
40
|
#endif
|
@@ -1119,10 +1127,11 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
|
|
1119
1127
|
bool has_content;
|
1120
1128
|
int save_offset;
|
1121
1129
|
int save_column;
|
1130
|
+
size_t depth = 0;
|
1122
1131
|
|
1123
1132
|
while (cont_type != CMARK_NODE_CODE_BLOCK &&
|
1124
1133
|
cont_type != CMARK_NODE_HTML_BLOCK) {
|
1125
|
-
|
1134
|
+
depth++;
|
1126
1135
|
S_find_first_nonspace(parser, input);
|
1127
1136
|
indented = parser->indent >= CODE_INDENT;
|
1128
1137
|
|
@@ -1208,15 +1217,17 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
|
|
1208
1217
|
parser->first_nonspace + 1);
|
1209
1218
|
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
|
1210
1219
|
} else if (!indented &&
|
1211
|
-
parser->options & CMARK_OPT_FOOTNOTES &&
|
1220
|
+
(parser->options & CMARK_OPT_FOOTNOTES) &&
|
1221
|
+
depth < MAX_LIST_DEPTH &&
|
1212
1222
|
(matched = scan_footnote_definition(input, parser->first_nonspace))) {
|
1213
1223
|
cmark_chunk c = cmark_chunk_dup(input, parser->first_nonspace + 2, matched - 2);
|
1214
|
-
cmark_chunk_to_cstr(parser->mem, &c);
|
1215
1224
|
|
1216
1225
|
while (c.data[c.len - 1] != ']')
|
1217
1226
|
--c.len;
|
1218
1227
|
--c.len;
|
1219
1228
|
|
1229
|
+
cmark_chunk_to_cstr(parser->mem, &c);
|
1230
|
+
|
1220
1231
|
S_advance_offset(parser, input, parser->first_nonspace + matched - parser->offset, false);
|
1221
1232
|
*container = add_child(parser, *container, CMARK_NODE_FOOTNOTE_DEFINITION, parser->first_nonspace + matched + 1);
|
1222
1233
|
(*container)->as.literal = c;
|
@@ -1224,6 +1235,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
|
|
1224
1235
|
(*container)->internal_offset = matched;
|
1225
1236
|
} else if ((!indented || cont_type == CMARK_NODE_LIST) &&
|
1226
1237
|
parser->indent < 4 &&
|
1238
|
+
depth < MAX_LIST_DEPTH &&
|
1227
1239
|
(matched = parse_list_marker(
|
1228
1240
|
parser->mem, input, parser->first_nonspace,
|
1229
1241
|
(*container)->type == CMARK_NODE_PARAGRAPH, &data))) {
|
@@ -413,6 +413,17 @@ CMARK_GFM_EXPORT int cmark_node_get_list_tight(cmark_node *node);
|
|
413
413
|
*/
|
414
414
|
CMARK_GFM_EXPORT int cmark_node_set_list_tight(cmark_node *node, int tight);
|
415
415
|
|
416
|
+
/**
|
417
|
+
* Returns item index of 'node'. This is only used when rendering output
|
418
|
+
* formats such as commonmark, which need to output the index. It is not
|
419
|
+
* required for formats such as html or latex.
|
420
|
+
*/
|
421
|
+
CMARK_GFM_EXPORT int cmark_node_get_item_index(cmark_node *node);
|
422
|
+
|
423
|
+
/** Sets item index of 'node'. Returns 1 on success, 0 on failure.
|
424
|
+
*/
|
425
|
+
CMARK_GFM_EXPORT int cmark_node_set_item_index(cmark_node *node, int idx);
|
426
|
+
|
416
427
|
/** Returns the info string from a fenced code block.
|
417
428
|
*/
|
418
429
|
CMARK_GFM_EXPORT const char *cmark_node_get_fence_info(cmark_node *node);
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#ifndef CMARK_GFM_VERSION_H
|
2
2
|
#define CMARK_GFM_VERSION_H
|
3
3
|
|
4
|
-
#define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) |
|
5
|
-
#define CMARK_GFM_VERSION_STRING "0.29.0.gfm.
|
4
|
+
#define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 11)
|
5
|
+
#define CMARK_GFM_VERSION_STRING "0.29.0.gfm.11"
|
6
6
|
|
7
7
|
#endif
|
@@ -153,23 +153,8 @@ static bool is_autolink(cmark_node *node) {
|
|
153
153
|
link_text->as.literal.len) == 0);
|
154
154
|
}
|
155
155
|
|
156
|
-
// if node is a block node, returns node.
|
157
|
-
// otherwise returns first block-level node that is an ancestor of node.
|
158
|
-
// if there is no block-level ancestor, returns NULL.
|
159
|
-
static cmark_node *get_containing_block(cmark_node *node) {
|
160
|
-
while (node) {
|
161
|
-
if (CMARK_NODE_BLOCK_P(node)) {
|
162
|
-
return node;
|
163
|
-
} else {
|
164
|
-
node = node->parent;
|
165
|
-
}
|
166
|
-
}
|
167
|
-
return NULL;
|
168
|
-
}
|
169
|
-
|
170
156
|
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
171
157
|
cmark_event_type ev_type, int options) {
|
172
|
-
cmark_node *tmp;
|
173
158
|
int list_number;
|
174
159
|
cmark_delim_type list_delim;
|
175
160
|
int numticks;
|
@@ -180,7 +165,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
180
165
|
char fencechar[2] = {'\0', '\0'};
|
181
166
|
size_t info_len, code_len;
|
182
167
|
char listmarker[LISTMARKER_SIZE];
|
183
|
-
char *emph_delim;
|
168
|
+
const char *emph_delim;
|
184
169
|
bool first_in_list_item;
|
185
170
|
bufsize_t marker_width;
|
186
171
|
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options) &&
|
@@ -189,14 +174,17 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
189
174
|
// Don't adjust tight list status til we've started the list.
|
190
175
|
// Otherwise we loose the blank line between a paragraph and
|
191
176
|
// a following list.
|
192
|
-
if (
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
177
|
+
if (entering) {
|
178
|
+
if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
|
179
|
+
renderer->in_tight_list_item = node->parent->parent->as.list.tight;
|
180
|
+
}
|
181
|
+
} else {
|
182
|
+
if (node->type == CMARK_NODE_LIST) {
|
183
|
+
renderer->in_tight_list_item =
|
184
|
+
node->parent &&
|
185
|
+
node->parent->type == CMARK_NODE_ITEM &&
|
186
|
+
node->parent->parent->as.list.tight;
|
187
|
+
}
|
200
188
|
}
|
201
189
|
|
202
190
|
if (node->extension && node->extension->commonmark_render_func) {
|
@@ -234,13 +222,8 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
234
222
|
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
235
223
|
marker_width = 4;
|
236
224
|
} else {
|
237
|
-
list_number =
|
225
|
+
list_number = cmark_node_get_item_index(node);
|
238
226
|
list_delim = cmark_node_get_list_delim(node->parent);
|
239
|
-
tmp = node;
|
240
|
-
while (tmp->prev) {
|
241
|
-
tmp = tmp->prev;
|
242
|
-
list_number += 1;
|
243
|
-
}
|
244
227
|
// we ensure a width of at least 4 so
|
245
228
|
// we get nice transition from single digits
|
246
229
|
// to double
|
@@ -405,10 +388,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
405
388
|
break;
|
406
389
|
|
407
390
|
case CMARK_NODE_STRONG:
|
408
|
-
if (
|
409
|
-
|
410
|
-
|
411
|
-
|
391
|
+
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
|
392
|
+
if (entering) {
|
393
|
+
LIT("**");
|
394
|
+
} else {
|
395
|
+
LIT("**");
|
396
|
+
}
|
412
397
|
}
|
413
398
|
break;
|
414
399
|
|
data/ext/commonmarker/html.c
CHANGED
@@ -364,10 +364,12 @@ static int S_render_node(cmark_html_renderer *renderer, cmark_node *node,
|
|
364
364
|
break;
|
365
365
|
|
366
366
|
case CMARK_NODE_STRONG:
|
367
|
-
if (
|
368
|
-
|
369
|
-
|
370
|
-
|
367
|
+
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
|
368
|
+
if (entering) {
|
369
|
+
cmark_strbuf_puts(html, "<strong>");
|
370
|
+
} else {
|
371
|
+
cmark_strbuf_puts(html, "</strong>");
|
372
|
+
}
|
371
373
|
}
|
372
374
|
break;
|
373
375
|
|
data/ext/commonmarker/latex.c
CHANGED
@@ -385,10 +385,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
385
385
|
break;
|
386
386
|
|
387
387
|
case CMARK_NODE_STRONG:
|
388
|
-
if (
|
389
|
-
|
390
|
-
|
391
|
-
|
388
|
+
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
|
389
|
+
if (entering) {
|
390
|
+
LIT("\\textbf{");
|
391
|
+
} else {
|
392
|
+
LIT("}");
|
393
|
+
}
|
392
394
|
}
|
393
395
|
break;
|
394
396
|
|
data/ext/commonmarker/man.c
CHANGED
@@ -74,7 +74,6 @@ static void S_outc(cmark_renderer *renderer, cmark_node *node,
|
|
74
74
|
|
75
75
|
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
76
76
|
cmark_event_type ev_type, int options) {
|
77
|
-
cmark_node *tmp;
|
78
77
|
int list_number;
|
79
78
|
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
80
79
|
bool allow_wrap = renderer->width > 0 && !(CMARK_OPT_NOBREAKS & options);
|
@@ -123,12 +122,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
123
122
|
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
124
123
|
LIT("\\[bu] 2");
|
125
124
|
} else {
|
126
|
-
list_number =
|
127
|
-
tmp = node;
|
128
|
-
while (tmp->prev) {
|
129
|
-
tmp = tmp->prev;
|
130
|
-
list_number += 1;
|
131
|
-
}
|
125
|
+
list_number = cmark_node_get_item_index(node);
|
132
126
|
char list_number_s[LIST_NUMBER_SIZE];
|
133
127
|
snprintf(list_number_s, LIST_NUMBER_SIZE, "\"%d.\" 4", list_number);
|
134
128
|
LIT(list_number_s);
|
@@ -225,10 +219,12 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
225
219
|
break;
|
226
220
|
|
227
221
|
case CMARK_NODE_STRONG:
|
228
|
-
if (
|
229
|
-
|
230
|
-
|
231
|
-
|
222
|
+
if (node->parent == NULL || node->parent->type != CMARK_NODE_STRONG) {
|
223
|
+
if (entering) {
|
224
|
+
LIT("\\f[B]");
|
225
|
+
} else {
|
226
|
+
LIT("\\f[]");
|
227
|
+
}
|
232
228
|
}
|
233
229
|
break;
|
234
230
|
|
data/ext/commonmarker/node.c
CHANGED
@@ -5,6 +5,16 @@
|
|
5
5
|
#include "node.h"
|
6
6
|
#include "syntax_extension.h"
|
7
7
|
|
8
|
+
/**
|
9
|
+
* Expensive safety checks are off by default, but can be enabled
|
10
|
+
* by calling cmark_enable_safety_checks().
|
11
|
+
*/
|
12
|
+
static bool enable_safety_checks = false;
|
13
|
+
|
14
|
+
void cmark_enable_safety_checks(bool enable) {
|
15
|
+
enable_safety_checks = enable;
|
16
|
+
}
|
17
|
+
|
8
18
|
static void S_node_unlink(cmark_node *node);
|
9
19
|
|
10
20
|
#define NODE_MEM(node) cmark_node_mem(node)
|
@@ -29,7 +39,7 @@ void cmark_register_node_flag(cmark_node_internal_flags *flags) {
|
|
29
39
|
nextflag <<= 1;
|
30
40
|
}
|
31
41
|
|
32
|
-
void cmark_init_standard_node_flags() {}
|
42
|
+
void cmark_init_standard_node_flags(void) {}
|
33
43
|
|
34
44
|
bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type) {
|
35
45
|
if (child_type == CMARK_NODE_DOCUMENT) {
|
@@ -70,8 +80,6 @@ bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type) {
|
|
70
80
|
}
|
71
81
|
|
72
82
|
static bool S_can_contain(cmark_node *node, cmark_node *child) {
|
73
|
-
cmark_node *cur;
|
74
|
-
|
75
83
|
if (node == NULL || child == NULL) {
|
76
84
|
return false;
|
77
85
|
}
|
@@ -79,14 +87,16 @@ static bool S_can_contain(cmark_node *node, cmark_node *child) {
|
|
79
87
|
return 0;
|
80
88
|
}
|
81
89
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
+
if (enable_safety_checks) {
|
91
|
+
// Verify that child is not an ancestor of node or equal to node.
|
92
|
+
cmark_node *cur = node;
|
93
|
+
do {
|
94
|
+
if (cur == child) {
|
95
|
+
return false;
|
96
|
+
}
|
97
|
+
cur = cur->parent;
|
98
|
+
} while (cur != NULL);
|
99
|
+
}
|
90
100
|
|
91
101
|
return cmark_node_can_contain_type(node, (cmark_node_type) child->type);
|
92
102
|
}
|
@@ -367,6 +377,7 @@ const char *cmark_node_get_literal(cmark_node *node) {
|
|
367
377
|
case CMARK_NODE_HTML_INLINE:
|
368
378
|
case CMARK_NODE_CODE:
|
369
379
|
case CMARK_NODE_FOOTNOTE_REFERENCE:
|
380
|
+
case CMARK_NODE_FOOTNOTE_DEFINITION:
|
370
381
|
return cmark_chunk_to_cstr(NODE_MEM(node), &node->as.literal);
|
371
382
|
|
372
383
|
case CMARK_NODE_CODE_BLOCK:
|
@@ -554,6 +565,31 @@ int cmark_node_set_list_tight(cmark_node *node, int tight) {
|
|
554
565
|
}
|
555
566
|
}
|
556
567
|
|
568
|
+
int cmark_node_get_item_index(cmark_node *node) {
|
569
|
+
if (node == NULL) {
|
570
|
+
return 0;
|
571
|
+
}
|
572
|
+
|
573
|
+
if (node->type == CMARK_NODE_ITEM) {
|
574
|
+
return node->as.list.start;
|
575
|
+
} else {
|
576
|
+
return 0;
|
577
|
+
}
|
578
|
+
}
|
579
|
+
|
580
|
+
int cmark_node_set_item_index(cmark_node *node, int idx) {
|
581
|
+
if (node == NULL || idx < 0) {
|
582
|
+
return 0;
|
583
|
+
}
|
584
|
+
|
585
|
+
if (node->type == CMARK_NODE_ITEM) {
|
586
|
+
node->as.list.start = idx;
|
587
|
+
return 1;
|
588
|
+
} else {
|
589
|
+
return 0;
|
590
|
+
}
|
591
|
+
}
|
592
|
+
|
557
593
|
const char *cmark_node_get_fence_info(cmark_node *node) {
|
558
594
|
if (node == NULL) {
|
559
595
|
return NULL;
|
data/ext/commonmarker/node.h
CHANGED
@@ -82,6 +82,14 @@ struct cmark_node {
|
|
82
82
|
|
83
83
|
cmark_syntax_extension *extension;
|
84
84
|
|
85
|
+
/**
|
86
|
+
* Used during cmark_render() to cache the most recent non-NULL
|
87
|
+
* extension, if you go up the parent chain like this:
|
88
|
+
*
|
89
|
+
* node->parent->...parent->extension
|
90
|
+
*/
|
91
|
+
cmark_syntax_extension *ancestor_extension;
|
92
|
+
|
85
93
|
union {
|
86
94
|
int ref_ix;
|
87
95
|
int def_count;
|
@@ -97,6 +105,7 @@ struct cmark_node {
|
|
97
105
|
cmark_link link;
|
98
106
|
cmark_custom custom;
|
99
107
|
int html_block_type;
|
108
|
+
int cell_index; // For keeping track of TABLE_CELL table alignments
|
100
109
|
void *opaque;
|
101
110
|
} as;
|
102
111
|
};
|
@@ -119,7 +128,7 @@ void cmark_register_node_flag(cmark_node_internal_flags *flags);
|
|
119
128
|
* library. It is now a no-op.
|
120
129
|
*/
|
121
130
|
CMARK_GFM_EXPORT
|
122
|
-
void cmark_init_standard_node_flags();
|
131
|
+
void cmark_init_standard_node_flags(void);
|
123
132
|
|
124
133
|
static CMARK_INLINE cmark_mem *cmark_node_mem(cmark_node *node) {
|
125
134
|
return node->content.mem;
|
@@ -144,6 +153,13 @@ static CMARK_INLINE bool CMARK_NODE_INLINE_P(cmark_node *node) {
|
|
144
153
|
|
145
154
|
CMARK_GFM_EXPORT bool cmark_node_can_contain_type(cmark_node *node, cmark_node_type child_type);
|
146
155
|
|
156
|
+
/**
|
157
|
+
* Enable (or disable) extra safety checks. These extra checks cause
|
158
|
+
* extra performance overhead (in some cases quadratic), so they are only
|
159
|
+
* intended to be used during testing.
|
160
|
+
*/
|
161
|
+
CMARK_GFM_EXPORT void cmark_enable_safety_checks(bool enable);
|
162
|
+
|
147
163
|
#ifdef __cplusplus
|
148
164
|
}
|
149
165
|
#endif
|
@@ -16,23 +16,8 @@ static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node,
|
|
16
16
|
cmark_render_code_point(renderer, c);
|
17
17
|
}
|
18
18
|
|
19
|
-
// if node is a block node, returns node.
|
20
|
-
// otherwise returns first block-level node that is an ancestor of node.
|
21
|
-
// if there is no block-level ancestor, returns NULL.
|
22
|
-
static cmark_node *get_containing_block(cmark_node *node) {
|
23
|
-
while (node) {
|
24
|
-
if (CMARK_NODE_BLOCK_P(node)) {
|
25
|
-
return node;
|
26
|
-
} else {
|
27
|
-
node = node->parent;
|
28
|
-
}
|
29
|
-
}
|
30
|
-
return NULL;
|
31
|
-
}
|
32
|
-
|
33
19
|
static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
34
20
|
cmark_event_type ev_type, int options) {
|
35
|
-
cmark_node *tmp;
|
36
21
|
int list_number;
|
37
22
|
cmark_delim_type list_delim;
|
38
23
|
int i;
|
@@ -46,14 +31,17 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
46
31
|
// Don't adjust tight list status til we've started the list.
|
47
32
|
// Otherwise we loose the blank line between a paragraph and
|
48
33
|
// a following list.
|
49
|
-
if (
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
34
|
+
if (entering) {
|
35
|
+
if (node->parent && node->parent->type == CMARK_NODE_ITEM) {
|
36
|
+
renderer->in_tight_list_item = node->parent->parent->as.list.tight;
|
37
|
+
}
|
38
|
+
} else {
|
39
|
+
if (node->type == CMARK_NODE_LIST) {
|
40
|
+
renderer->in_tight_list_item =
|
41
|
+
node->parent &&
|
42
|
+
node->parent->type == CMARK_NODE_ITEM &&
|
43
|
+
node->parent->parent->as.list.tight;
|
44
|
+
}
|
57
45
|
}
|
58
46
|
|
59
47
|
if (node->extension && node->extension->plaintext_render_func) {
|
@@ -79,13 +67,8 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
79
67
|
if (cmark_node_get_list_type(node->parent) == CMARK_BULLET_LIST) {
|
80
68
|
marker_width = 4;
|
81
69
|
} else {
|
82
|
-
list_number =
|
70
|
+
list_number = cmark_node_get_item_index(node);
|
83
71
|
list_delim = cmark_node_get_list_delim(node->parent);
|
84
|
-
tmp = node;
|
85
|
-
while (tmp->prev) {
|
86
|
-
tmp = tmp->prev;
|
87
|
-
list_number += 1;
|
88
|
-
}
|
89
72
|
// we ensure a width of at least 4 so
|
90
73
|
// we get nice transition from single digits
|
91
74
|
// to double
|
data/ext/commonmarker/render.c
CHANGED
@@ -31,13 +31,7 @@ static void S_out(cmark_renderer *renderer, cmark_node *node,
|
|
31
31
|
cmark_chunk remainder = cmark_chunk_literal("");
|
32
32
|
int k = renderer->buffer->size - 1;
|
33
33
|
|
34
|
-
cmark_syntax_extension *ext =
|
35
|
-
cmark_node *n = node;
|
36
|
-
while (n && !ext) {
|
37
|
-
ext = n->extension;
|
38
|
-
if (!ext)
|
39
|
-
n = n->parent;
|
40
|
-
}
|
34
|
+
cmark_syntax_extension *ext = node->ancestor_extension;
|
41
35
|
if (ext && !ext->commonmark_escape_func)
|
42
36
|
ext = NULL;
|
43
37
|
|
@@ -182,6 +176,20 @@ char *cmark_render(cmark_mem *mem, cmark_node *root, int options, int width,
|
|
182
176
|
|
183
177
|
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
184
178
|
cur = cmark_iter_get_node(iter);
|
179
|
+
if (cur->extension) {
|
180
|
+
cur->ancestor_extension = cur->extension;
|
181
|
+
} else if (cur->parent) {
|
182
|
+
cur->ancestor_extension = cur->parent->ancestor_extension;
|
183
|
+
}
|
184
|
+
if (cur->type == CMARK_NODE_ITEM) {
|
185
|
+
// Calculate the list item's index, for the benefit of output formats
|
186
|
+
// like commonmark and plaintext.
|
187
|
+
if (cur->prev) {
|
188
|
+
cmark_node_set_item_index(cur, 1 + cmark_node_get_item_index(cur->prev));
|
189
|
+
} else {
|
190
|
+
cmark_node_set_item_index(cur, cmark_node_get_list_start(cur->parent));
|
191
|
+
}
|
192
|
+
}
|
185
193
|
if (!render_node(&renderer, cur, ev_type, options)) {
|
186
194
|
// a false value causes us to skip processing
|
187
195
|
// the node's contents. this is used for
|
data/ext/commonmarker/table.c
CHANGED
@@ -11,6 +11,9 @@
|
|
11
11
|
#include "table.h"
|
12
12
|
#include "cmark-gfm-core-extensions.h"
|
13
13
|
|
14
|
+
// Limit to prevent a malicious input from causing a denial of service.
|
15
|
+
#define MAX_AUTOCOMPLETED_CELLS 0x80000
|
16
|
+
|
14
17
|
// Custom node flag, initialized in `create_table_extension`.
|
15
18
|
static cmark_node_internal_flags CMARK_NODE__TABLE_VISITED;
|
16
19
|
|
@@ -31,6 +34,8 @@ typedef struct {
|
|
31
34
|
typedef struct {
|
32
35
|
uint16_t n_columns;
|
33
36
|
uint8_t *alignments;
|
37
|
+
int n_rows;
|
38
|
+
int n_nonempty_cells;
|
34
39
|
} node_table;
|
35
40
|
|
36
41
|
typedef struct {
|
@@ -83,6 +88,33 @@ static int set_n_table_columns(cmark_node *node, uint16_t n_columns) {
|
|
83
88
|
return 1;
|
84
89
|
}
|
85
90
|
|
91
|
+
// Increment the number of rows in the table. Also update n_nonempty_cells,
|
92
|
+
// which keeps track of the number of cells which were parsed from the
|
93
|
+
// input file. (If one of the rows is too short, then the trailing cells
|
94
|
+
// are autocompleted. Autocompleted cells are not counted in n_nonempty_cells.)
|
95
|
+
// The purpose of this is to prevent a malicious input from generating a very
|
96
|
+
// large number of autocompleted cells, which could cause a denial of service
|
97
|
+
// vulnerability.
|
98
|
+
static int incr_table_row_count(cmark_node *node, int i) {
|
99
|
+
if (!node || node->type != CMARK_NODE_TABLE) {
|
100
|
+
return 0;
|
101
|
+
}
|
102
|
+
|
103
|
+
((node_table *)node->as.opaque)->n_rows++;
|
104
|
+
((node_table *)node->as.opaque)->n_nonempty_cells += i;
|
105
|
+
return 1;
|
106
|
+
}
|
107
|
+
|
108
|
+
// Calculate the number of autocompleted cells.
|
109
|
+
static int get_n_autocompleted_cells(cmark_node *node) {
|
110
|
+
if (!node || node->type != CMARK_NODE_TABLE) {
|
111
|
+
return 0;
|
112
|
+
}
|
113
|
+
|
114
|
+
const node_table *nt = (node_table *)node->as.opaque;
|
115
|
+
return (nt->n_columns * nt->n_rows) - nt->n_nonempty_cells;
|
116
|
+
}
|
117
|
+
|
86
118
|
static uint8_t *get_table_alignments(cmark_node *node) {
|
87
119
|
if (!node || node->type != CMARK_NODE_TABLE)
|
88
120
|
return 0;
|
@@ -98,6 +130,23 @@ static int set_table_alignments(cmark_node *node, uint8_t *alignments) {
|
|
98
130
|
return 1;
|
99
131
|
}
|
100
132
|
|
133
|
+
static uint8_t get_cell_alignment(cmark_node *node) {
|
134
|
+
if (!node || node->type != CMARK_NODE_TABLE_CELL)
|
135
|
+
return 0;
|
136
|
+
|
137
|
+
const uint8_t *alignments = get_table_alignments(node->parent->parent);
|
138
|
+
int i = node->as.cell_index;
|
139
|
+
return alignments[i];
|
140
|
+
}
|
141
|
+
|
142
|
+
static int set_cell_index(cmark_node *node, int i) {
|
143
|
+
if (!node || node->type != CMARK_NODE_TABLE_CELL)
|
144
|
+
return 0;
|
145
|
+
|
146
|
+
node->as.cell_index = i;
|
147
|
+
return 1;
|
148
|
+
}
|
149
|
+
|
101
150
|
static cmark_strbuf *unescape_pipes(cmark_mem *mem, unsigned char *string, bufsize_t len)
|
102
151
|
{
|
103
152
|
cmark_strbuf *res = (cmark_strbuf *)mem->calloc(1, sizeof(cmark_strbuf));
|
@@ -257,7 +306,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
|
|
257
306
|
unsigned char *input, int len) {
|
258
307
|
cmark_node *table_header;
|
259
308
|
table_row *header_row = NULL;
|
260
|
-
table_row *
|
309
|
+
table_row *delimiter_row = NULL;
|
261
310
|
node_table_row *ntr;
|
262
311
|
const char *parent_string;
|
263
312
|
uint16_t i;
|
@@ -270,16 +319,16 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
|
|
270
319
|
return parent_container;
|
271
320
|
}
|
272
321
|
|
273
|
-
// Since scan_table_start was successful, we must have a
|
274
|
-
|
275
|
-
|
276
|
-
|
322
|
+
// Since scan_table_start was successful, we must have a delimiter row.
|
323
|
+
delimiter_row = row_from_string(
|
324
|
+
self, parser, input + cmark_parser_get_first_nonspace(parser),
|
325
|
+
len - cmark_parser_get_first_nonspace(parser));
|
277
326
|
// assert may be optimized out, don't rely on it for security boundaries
|
278
|
-
if (!
|
327
|
+
if (!delimiter_row) {
|
279
328
|
return parent_container;
|
280
329
|
}
|
281
|
-
|
282
|
-
assert(
|
330
|
+
|
331
|
+
assert(delimiter_row);
|
283
332
|
|
284
333
|
cmark_arena_push();
|
285
334
|
|
@@ -289,8 +338,8 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
|
|
289
338
|
parent_string = cmark_node_get_string_content(parent_container);
|
290
339
|
header_row = row_from_string(self, parser, (unsigned char *)parent_string,
|
291
340
|
(int)strlen(parent_string));
|
292
|
-
if (!header_row || header_row->n_columns !=
|
293
|
-
free_table_row(parser->mem,
|
341
|
+
if (!header_row || header_row->n_columns != delimiter_row->n_columns) {
|
342
|
+
free_table_row(parser->mem, delimiter_row);
|
294
343
|
free_table_row(parser->mem, header_row);
|
295
344
|
cmark_arena_pop();
|
296
345
|
parent_container->flags |= CMARK_NODE__TABLE_VISITED;
|
@@ -298,14 +347,14 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
|
|
298
347
|
}
|
299
348
|
|
300
349
|
if (cmark_arena_pop()) {
|
301
|
-
|
350
|
+
delimiter_row = row_from_string(
|
302
351
|
self, parser, input + cmark_parser_get_first_nonspace(parser),
|
303
352
|
len - cmark_parser_get_first_nonspace(parser));
|
304
353
|
header_row = row_from_string(self, parser, (unsigned char *)parent_string,
|
305
354
|
(int)strlen(parent_string));
|
306
355
|
// row_from_string can return NULL, add additional check to ensure n_columns match
|
307
|
-
if (!
|
308
|
-
free_table_row(parser->mem,
|
356
|
+
if (!delimiter_row || !header_row || header_row->n_columns != delimiter_row->n_columns) {
|
357
|
+
free_table_row(parser->mem, delimiter_row);
|
309
358
|
free_table_row(parser->mem, header_row);
|
310
359
|
return parent_container;
|
311
360
|
}
|
@@ -313,7 +362,7 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
|
|
313
362
|
|
314
363
|
if (!cmark_node_set_type(parent_container, CMARK_NODE_TABLE)) {
|
315
364
|
free_table_row(parser->mem, header_row);
|
316
|
-
free_table_row(parser->mem,
|
365
|
+
free_table_row(parser->mem, delimiter_row);
|
317
366
|
return parent_container;
|
318
367
|
}
|
319
368
|
|
@@ -326,12 +375,12 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
|
|
326
375
|
parent_container->as.opaque = parser->mem->calloc(1, sizeof(node_table));
|
327
376
|
set_n_table_columns(parent_container, header_row->n_columns);
|
328
377
|
|
329
|
-
// allocate alignments based on
|
330
|
-
// since we populate the alignments array based on
|
378
|
+
// allocate alignments based on delimiter_row->n_columns
|
379
|
+
// since we populate the alignments array based on delimiter_row->cells
|
331
380
|
uint8_t *alignments =
|
332
|
-
(uint8_t *)parser->mem->calloc(
|
333
|
-
for (i = 0; i <
|
334
|
-
node_cell *node = &
|
381
|
+
(uint8_t *)parser->mem->calloc(delimiter_row->n_columns, sizeof(uint8_t));
|
382
|
+
for (i = 0; i < delimiter_row->n_columns; ++i) {
|
383
|
+
node_cell *node = &delimiter_row->cells[i];
|
335
384
|
bool left = node->buf->ptr[0] == ':', right = node->buf->ptr[node->buf->size - 1] == ':';
|
336
385
|
|
337
386
|
if (left && right)
|
@@ -353,25 +402,26 @@ static cmark_node *try_opening_table_header(cmark_syntax_extension *self,
|
|
353
402
|
table_header->as.opaque = ntr = (node_table_row *)parser->mem->calloc(1, sizeof(node_table_row));
|
354
403
|
ntr->is_header = true;
|
355
404
|
|
356
|
-
{
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
}
|
405
|
+
for (i = 0; i < header_row->n_columns; ++i) {
|
406
|
+
node_cell *cell = &header_row->cells[i];
|
407
|
+
cmark_node *header_cell = cmark_parser_add_child(parser, table_header,
|
408
|
+
CMARK_NODE_TABLE_CELL, parent_container->start_column + cell->start_offset);
|
409
|
+
header_cell->start_line = header_cell->end_line = parent_container->start_line;
|
410
|
+
header_cell->internal_offset = cell->internal_offset;
|
411
|
+
header_cell->end_column = parent_container->start_column + cell->end_offset;
|
412
|
+
cmark_node_set_string_content(header_cell, (char *) cell->buf->ptr);
|
413
|
+
cmark_node_set_syntax_extension(header_cell, self);
|
414
|
+
set_cell_index(header_cell, i);
|
367
415
|
}
|
368
416
|
|
417
|
+
incr_table_row_count(parent_container, i);
|
418
|
+
|
369
419
|
cmark_parser_advance_offset(
|
370
420
|
parser, (char *)input,
|
371
421
|
(int)strlen((char *)input) - 1 - cmark_parser_get_offset(parser), false);
|
372
422
|
|
373
423
|
free_table_row(parser->mem, header_row);
|
374
|
-
free_table_row(parser->mem,
|
424
|
+
free_table_row(parser->mem, delimiter_row);
|
375
425
|
return parent_container;
|
376
426
|
}
|
377
427
|
|
@@ -385,6 +435,10 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
|
|
385
435
|
if (cmark_parser_is_blank(parser))
|
386
436
|
return NULL;
|
387
437
|
|
438
|
+
if (get_n_autocompleted_cells(parent_container) > MAX_AUTOCOMPLETED_CELLS) {
|
439
|
+
return NULL;
|
440
|
+
}
|
441
|
+
|
388
442
|
table_row_block =
|
389
443
|
cmark_parser_add_child(parser, parent_container, CMARK_NODE_TABLE_ROW,
|
390
444
|
parent_container->start_column);
|
@@ -412,12 +466,16 @@ static cmark_node *try_opening_table_row(cmark_syntax_extension *self,
|
|
412
466
|
node->end_column = parent_container->start_column + cell->end_offset;
|
413
467
|
cmark_node_set_string_content(node, (char *) cell->buf->ptr);
|
414
468
|
cmark_node_set_syntax_extension(node, self);
|
469
|
+
set_cell_index(node, i);
|
415
470
|
}
|
416
471
|
|
472
|
+
incr_table_row_count(parent_container, i);
|
473
|
+
|
417
474
|
for (; i < table_columns; ++i) {
|
418
475
|
cmark_node *node = cmark_parser_add_child(
|
419
476
|
parser, table_row_block, CMARK_NODE_TABLE_CELL, 0);
|
420
477
|
cmark_node_set_syntax_extension(node, self);
|
478
|
+
set_cell_index(node, i);
|
421
479
|
}
|
422
480
|
}
|
423
481
|
|
@@ -602,13 +660,7 @@ static const char *xml_attr(cmark_syntax_extension *extension,
|
|
602
660
|
cmark_node *node) {
|
603
661
|
if (node->type == CMARK_NODE_TABLE_CELL) {
|
604
662
|
if (cmark_gfm_extensions_get_table_row_is_header(node->parent)) {
|
605
|
-
|
606
|
-
int i = 0;
|
607
|
-
cmark_node *n;
|
608
|
-
for (n = node->parent->first_child; n; n = n->next, ++i)
|
609
|
-
if (n == node)
|
610
|
-
break;
|
611
|
-
switch (alignments[i]) {
|
663
|
+
switch (get_cell_alignment(node)) {
|
612
664
|
case 'l': return " align=\"left\"";
|
613
665
|
case 'c': return " align=\"center\"";
|
614
666
|
case 'r': return " align=\"right\"";
|
@@ -696,7 +748,6 @@ static void html_render(cmark_syntax_extension *extension,
|
|
696
748
|
cmark_event_type ev_type, int options) {
|
697
749
|
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
698
750
|
cmark_strbuf *html = renderer->html;
|
699
|
-
cmark_node *n;
|
700
751
|
|
701
752
|
// XXX: we just monopolise renderer->opaque.
|
702
753
|
struct html_table_state *table_state =
|
@@ -745,7 +796,6 @@ static void html_render(cmark_syntax_extension *extension,
|
|
745
796
|
}
|
746
797
|
}
|
747
798
|
} else if (node->type == CMARK_NODE_TABLE_CELL) {
|
748
|
-
uint8_t *alignments = get_table_alignments(node->parent->parent);
|
749
799
|
if (entering) {
|
750
800
|
cmark_html_render_cr(html);
|
751
801
|
if (table_state->in_table_header) {
|
@@ -754,12 +804,7 @@ static void html_render(cmark_syntax_extension *extension,
|
|
754
804
|
cmark_strbuf_puts(html, "<td");
|
755
805
|
}
|
756
806
|
|
757
|
-
|
758
|
-
for (n = node->parent->first_child; n; n = n->next, ++i)
|
759
|
-
if (n == node)
|
760
|
-
break;
|
761
|
-
|
762
|
-
switch (alignments[i]) {
|
807
|
+
switch (get_cell_alignment(node)) {
|
763
808
|
case 'l': html_table_add_align(html, "left", options); break;
|
764
809
|
case 'c': html_table_add_align(html, "center", options); break;
|
765
810
|
case 'r': html_table_add_align(html, "right", options); break;
|
data/ext/commonmarker/xml.c
CHANGED
@@ -11,6 +11,7 @@
|
|
11
11
|
#include "syntax_extension.h"
|
12
12
|
|
13
13
|
#define BUFFER_SIZE 100
|
14
|
+
#define MAX_INDENT 40
|
14
15
|
|
15
16
|
// Functions to convert cmark_nodes to XML strings.
|
16
17
|
|
@@ -26,7 +27,7 @@ struct render_state {
|
|
26
27
|
|
27
28
|
static CMARK_INLINE void indent(struct render_state *state) {
|
28
29
|
int i;
|
29
|
-
for (i = 0; i < state->indent; i++) {
|
30
|
+
for (i = 0; i < state->indent && i < MAX_INDENT; i++) {
|
30
31
|
cmark_strbuf_putc(state->xml, ' ');
|
31
32
|
}
|
32
33
|
}
|
@@ -129,8 +129,12 @@ module CommonMarker
|
|
129
129
|
out("<em>", :children, "</em>")
|
130
130
|
end
|
131
131
|
|
132
|
-
def strong(
|
133
|
-
|
132
|
+
def strong(node)
|
133
|
+
if node.parent&.type == :strong
|
134
|
+
out(:children)
|
135
|
+
else
|
136
|
+
out("<strong>", :children, "</strong>")
|
137
|
+
end
|
134
138
|
end
|
135
139
|
|
136
140
|
def link(node)
|
data/lib/commonmarker/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: commonmarker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.23.
|
4
|
+
version: 0.23.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Garen Torikian
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2023-
|
12
|
+
date: 2023-07-31 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: awesome_print
|