commonmarker 0.23.8 → 0.23.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|