commonmarker 0.17.13 → 0.23.4
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.
- checksums.yaml +5 -5
- data/README.md +94 -18
- data/Rakefile +24 -5
- data/bin/commonmarker +107 -47
- data/commonmarker.gemspec +18 -15
- data/ext/commonmarker/autolink.c +10 -6
- data/ext/commonmarker/blocks.c +102 -31
- data/ext/commonmarker/buffer.c +0 -1
- data/ext/commonmarker/chunk.h +0 -1
- data/ext/commonmarker/cmark-gfm-core-extensions.h +29 -0
- data/ext/commonmarker/cmark-gfm-extension_api.h +19 -2
- data/ext/commonmarker/cmark-gfm.h +19 -5
- data/ext/commonmarker/cmark-gfm_version.h +2 -2
- data/ext/commonmarker/commonmark.c +33 -12
- data/ext/commonmarker/commonmarker.c +209 -100
- data/ext/commonmarker/core-extensions.c +2 -0
- data/ext/commonmarker/ext_scanners.c +622 -684
- data/ext/commonmarker/ext_scanners.h +2 -0
- data/ext/commonmarker/extconf.rb +3 -1
- data/ext/commonmarker/footnotes.c +23 -0
- data/ext/commonmarker/footnotes.h +2 -0
- data/ext/commonmarker/houdini_href_e.c +1 -1
- data/ext/commonmarker/html.c +46 -25
- data/ext/commonmarker/inlines.c +127 -30
- data/ext/commonmarker/iterator.h +0 -1
- data/ext/commonmarker/map.h +0 -1
- data/ext/commonmarker/node.c +17 -3
- data/ext/commonmarker/node.h +9 -0
- data/ext/commonmarker/parser.h +2 -1
- data/ext/commonmarker/plaintext.c +22 -0
- data/ext/commonmarker/render.c +18 -15
- data/ext/commonmarker/render.h +0 -1
- data/ext/commonmarker/scanners.c +779 -953
- data/ext/commonmarker/scanners.h +0 -2
- data/ext/commonmarker/strikethrough.c +4 -1
- data/ext/commonmarker/syntax_extension.c +10 -0
- data/ext/commonmarker/syntax_extension.h +2 -0
- data/ext/commonmarker/table.c +178 -31
- data/ext/commonmarker/tasklist.c +156 -0
- data/ext/commonmarker/tasklist.h +8 -0
- data/ext/commonmarker/xml.c +9 -2
- data/lib/commonmarker/config.rb +41 -38
- data/lib/commonmarker/errors.rb +12 -0
- data/lib/commonmarker/node/inspect.rb +15 -17
- data/lib/commonmarker/node.rb +14 -2
- data/lib/commonmarker/renderer/html_renderer.rb +45 -36
- data/lib/commonmarker/renderer.rb +16 -10
- data/lib/commonmarker/version.rb +3 -1
- data/lib/commonmarker.rb +8 -7
- data/test/benchmark.rb +26 -21
- data/test/fixtures/strong.md +1 -0
- data/test/fixtures/table.md +10 -0
- data/test/test_attributes.rb +5 -3
- data/test/test_basics.rb +19 -0
- data/test/test_commands.rb +72 -0
- data/test/test_commonmark.rb +15 -13
- data/test/test_doc.rb +31 -29
- data/test/test_encoding.rb +9 -5
- data/test/test_extensions.rb +66 -73
- data/test/test_footnotes.rb +47 -12
- data/test/test_gc.rb +6 -2
- data/test/test_helper.rb +25 -15
- data/test/test_linebreaks.rb +2 -0
- data/test/test_maliciousness.rb +189 -190
- data/test/test_node.rb +12 -12
- data/test/test_options.rb +17 -15
- data/test/test_pathological_inputs.rb +14 -12
- data/test/test_plaintext.rb +23 -21
- data/test/test_renderer.rb +29 -10
- data/test/test_smartpunct.rb +7 -2
- data/test/test_spec.rb +7 -4
- data/test/test_tasklists.rb +43 -0
- data/test/test_xml.rb +107 -0
- metadata +74 -30
data/ext/commonmarker/blocks.c
CHANGED
@@ -36,6 +36,10 @@ static bool S_last_line_blank(const cmark_node *node) {
|
|
36
36
|
return (node->flags & CMARK_NODE__LAST_LINE_BLANK) != 0;
|
37
37
|
}
|
38
38
|
|
39
|
+
static bool S_last_line_checked(const cmark_node *node) {
|
40
|
+
return (node->flags & CMARK_NODE__LAST_LINE_CHECKED) != 0;
|
41
|
+
}
|
42
|
+
|
39
43
|
static CMARK_INLINE cmark_node_type S_type(const cmark_node *node) {
|
40
44
|
return (cmark_node_type)node->type;
|
41
45
|
}
|
@@ -47,6 +51,10 @@ static void S_set_last_line_blank(cmark_node *node, bool is_blank) {
|
|
47
51
|
node->flags &= ~CMARK_NODE__LAST_LINE_BLANK;
|
48
52
|
}
|
49
53
|
|
54
|
+
static void S_set_last_line_checked(cmark_node *node) {
|
55
|
+
node->flags |= CMARK_NODE__LAST_LINE_CHECKED;
|
56
|
+
}
|
57
|
+
|
50
58
|
static CMARK_INLINE bool S_is_line_end_char(char c) {
|
51
59
|
return (c == '\n' || c == '\r');
|
52
60
|
}
|
@@ -121,8 +129,6 @@ static void cmark_parser_reset(cmark_parser *parser) {
|
|
121
129
|
parser->root = document;
|
122
130
|
parser->current = document;
|
123
131
|
|
124
|
-
parser->last_buffer_ended_with_cr = false;
|
125
|
-
|
126
132
|
parser->syntax_extensions = saved_exts;
|
127
133
|
parser->inline_syntax_extensions = saved_inline_exts;
|
128
134
|
parser->options = saved_options;
|
@@ -234,19 +240,35 @@ static void remove_trailing_blank_lines(cmark_strbuf *ln) {
|
|
234
240
|
|
235
241
|
// Check to see if a node ends with a blank line, descending
|
236
242
|
// if needed into lists and sublists.
|
237
|
-
static bool
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
}
|
243
|
+
static bool S_ends_with_blank_line(cmark_node *node) {
|
244
|
+
if (S_last_line_checked(node)) {
|
245
|
+
return(S_last_line_blank(node));
|
246
|
+
} else if ((S_type(node) == CMARK_NODE_LIST ||
|
247
|
+
S_type(node) == CMARK_NODE_ITEM) && node->last_child) {
|
248
|
+
S_set_last_line_checked(node);
|
249
|
+
return(S_ends_with_blank_line(node->last_child));
|
250
|
+
} else {
|
251
|
+
S_set_last_line_checked(node);
|
252
|
+
return (S_last_line_blank(node));
|
248
253
|
}
|
249
|
-
|
254
|
+
}
|
255
|
+
|
256
|
+
// returns true if content remains after link defs are resolved.
|
257
|
+
static bool resolve_reference_link_definitions(
|
258
|
+
cmark_parser *parser,
|
259
|
+
cmark_node *b) {
|
260
|
+
bufsize_t pos;
|
261
|
+
cmark_strbuf *node_content = &b->content;
|
262
|
+
cmark_chunk chunk = {node_content->ptr, node_content->size, 0};
|
263
|
+
while (chunk.len && chunk.data[0] == '[' &&
|
264
|
+
(pos = cmark_parse_reference_inline(parser->mem, &chunk,
|
265
|
+
parser->refmap))) {
|
266
|
+
|
267
|
+
chunk.data += pos;
|
268
|
+
chunk.len -= pos;
|
269
|
+
}
|
270
|
+
cmark_strbuf_drop(node_content, (node_content->size - chunk.len));
|
271
|
+
return !is_blank(&b->content, 0);
|
250
272
|
}
|
251
273
|
|
252
274
|
static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
|
@@ -254,6 +276,7 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
|
|
254
276
|
cmark_node *item;
|
255
277
|
cmark_node *subitem;
|
256
278
|
cmark_node *parent;
|
279
|
+
bool has_content;
|
257
280
|
|
258
281
|
parent = b->parent;
|
259
282
|
assert(b->flags &
|
@@ -283,15 +306,8 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
|
|
283
306
|
switch (S_type(b)) {
|
284
307
|
case CMARK_NODE_PARAGRAPH:
|
285
308
|
{
|
286
|
-
|
287
|
-
|
288
|
-
(pos = cmark_parse_reference_inline(parser->mem, &chunk, parser->refmap))) {
|
289
|
-
|
290
|
-
chunk.data += pos;
|
291
|
-
chunk.len -= pos;
|
292
|
-
}
|
293
|
-
cmark_strbuf_drop(node_content, (node_content->size - chunk.len));
|
294
|
-
if (is_blank(node_content, 0)) {
|
309
|
+
has_content = resolve_reference_link_definitions(parser, b);
|
310
|
+
if (!has_content) {
|
295
311
|
// remove blank node (former reference def)
|
296
312
|
cmark_node_free(b);
|
297
313
|
}
|
@@ -343,7 +359,8 @@ static cmark_node *finalize(cmark_parser *parser, cmark_node *b) {
|
|
343
359
|
// spaces between them:
|
344
360
|
subitem = item->first_child;
|
345
361
|
while (subitem) {
|
346
|
-
if (
|
362
|
+
if ((item->next || subitem->next) &&
|
363
|
+
S_ends_with_blank_line(subitem)) {
|
347
364
|
b->as.list.tight = false;
|
348
365
|
break;
|
349
366
|
}
|
@@ -451,7 +468,6 @@ static void process_footnotes(cmark_parser *parser) {
|
|
451
468
|
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
452
469
|
cur = cmark_iter_get_node(iter);
|
453
470
|
if (ev_type == CMARK_EVENT_EXIT && cur->type == CMARK_NODE_FOOTNOTE_DEFINITION) {
|
454
|
-
cmark_node_unlink(cur);
|
455
471
|
cmark_footnote_create(map, cur);
|
456
472
|
}
|
457
473
|
}
|
@@ -468,6 +484,15 @@ static void process_footnotes(cmark_parser *parser) {
|
|
468
484
|
if (!footnote->ix)
|
469
485
|
footnote->ix = ++ix;
|
470
486
|
|
487
|
+
// store a reference to this footnote reference's footnote definition
|
488
|
+
// this is used by renderers when generating label ids
|
489
|
+
cur->parent_footnote_def = footnote->node;
|
490
|
+
|
491
|
+
// keep track of a) count of how many times this footnote def has been
|
492
|
+
// referenced, and b) which reference index this footnote ref is at.
|
493
|
+
// this is used by renderers when generating links and backreferences.
|
494
|
+
cur->footnote.ref_ix = ++footnote->node->footnote.def_count;
|
495
|
+
|
471
496
|
char n[32];
|
472
497
|
snprintf(n, sizeof(n), "%d", footnote->ix);
|
473
498
|
cmark_chunk_free(parser->mem, &cur->as.literal);
|
@@ -498,13 +523,16 @@ static void process_footnotes(cmark_parser *parser) {
|
|
498
523
|
qsort(map->sorted, map->size, sizeof(cmark_map_entry *), sort_footnote_by_ix);
|
499
524
|
for (unsigned int i = 0; i < map->size; ++i) {
|
500
525
|
cmark_footnote *footnote = (cmark_footnote *)map->sorted[i];
|
501
|
-
if (!footnote->ix)
|
526
|
+
if (!footnote->ix) {
|
527
|
+
cmark_node_unlink(footnote->node);
|
502
528
|
continue;
|
529
|
+
}
|
503
530
|
cmark_node_append_child(parser->root, footnote->node);
|
504
531
|
footnote->node = NULL;
|
505
532
|
}
|
506
533
|
}
|
507
534
|
|
535
|
+
cmark_unlink_footnotes_map(map);
|
508
536
|
cmark_map_free(map);
|
509
537
|
}
|
510
538
|
|
@@ -748,6 +776,40 @@ static void chop_trailing_hashtags(cmark_chunk *ch) {
|
|
748
776
|
}
|
749
777
|
}
|
750
778
|
|
779
|
+
// Check for thematic break. On failure, return 0 and update
|
780
|
+
// thematic_break_kill_pos with the index at which the
|
781
|
+
// parse fails. On success, return length of match.
|
782
|
+
// "...three or more hyphens, asterisks,
|
783
|
+
// or underscores on a line by themselves. If you wish, you may use
|
784
|
+
// spaces between the hyphens or asterisks."
|
785
|
+
static int S_scan_thematic_break(cmark_parser *parser, cmark_chunk *input,
|
786
|
+
bufsize_t offset) {
|
787
|
+
bufsize_t i;
|
788
|
+
char c;
|
789
|
+
char nextc = '\0';
|
790
|
+
int count;
|
791
|
+
i = offset;
|
792
|
+
c = peek_at(input, i);
|
793
|
+
if (!(c == '*' || c == '_' || c == '-')) {
|
794
|
+
parser->thematic_break_kill_pos = i;
|
795
|
+
return 0;
|
796
|
+
}
|
797
|
+
count = 1;
|
798
|
+
while ((nextc = peek_at(input, ++i))) {
|
799
|
+
if (nextc == c) {
|
800
|
+
count++;
|
801
|
+
} else if (nextc != ' ' && nextc != '\t') {
|
802
|
+
break;
|
803
|
+
}
|
804
|
+
}
|
805
|
+
if (count >= 3 && (nextc == '\r' || nextc == '\n')) {
|
806
|
+
return (i - offset) + 1;
|
807
|
+
} else {
|
808
|
+
parser->thematic_break_kill_pos = i;
|
809
|
+
return 0;
|
810
|
+
}
|
811
|
+
}
|
812
|
+
|
751
813
|
// Find first nonspace character from current offset, setting
|
752
814
|
// parser->first_nonspace, parser->first_nonspace_column,
|
753
815
|
// parser->indent, and parser->blank. Does not advance parser->offset.
|
@@ -1040,6 +1102,7 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
|
|
1040
1102
|
bufsize_t matched = 0;
|
1041
1103
|
int lev = 0;
|
1042
1104
|
bool save_partially_consumed_tab;
|
1105
|
+
bool has_content;
|
1043
1106
|
int save_offset;
|
1044
1107
|
int save_column;
|
1045
1108
|
|
@@ -1112,13 +1175,20 @@ static void open_new_blocks(cmark_parser *parser, cmark_node **container,
|
|
1112
1175
|
} else if (!indented && cont_type == CMARK_NODE_PARAGRAPH &&
|
1113
1176
|
(lev =
|
1114
1177
|
scan_setext_heading_line(input, parser->first_nonspace))) {
|
1115
|
-
|
1116
|
-
(*container)
|
1117
|
-
|
1118
|
-
|
1178
|
+
// finalize paragraph, resolving reference links
|
1179
|
+
has_content = resolve_reference_link_definitions(parser, *container);
|
1180
|
+
|
1181
|
+
if (has_content) {
|
1182
|
+
|
1183
|
+
(*container)->type = (uint16_t)CMARK_NODE_HEADING;
|
1184
|
+
(*container)->as.heading.level = lev;
|
1185
|
+
(*container)->as.heading.setext = true;
|
1186
|
+
S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
|
1187
|
+
}
|
1119
1188
|
} else if (!indented &&
|
1120
1189
|
!(cont_type == CMARK_NODE_PARAGRAPH && !all_matched) &&
|
1121
|
-
|
1190
|
+
(parser->thematic_break_kill_pos <= parser->first_nonspace) &&
|
1191
|
+
(matched = S_scan_thematic_break(parser, input, parser->first_nonspace))) {
|
1122
1192
|
// it's only now that we know the line is not part of a setext heading:
|
1123
1193
|
*container = add_child(parser, *container, CMARK_NODE_THEMATIC_BREAK,
|
1124
1194
|
parser->first_nonspace + 1);
|
@@ -1377,6 +1447,7 @@ static void S_process_line(cmark_parser *parser, const unsigned char *buffer,
|
|
1377
1447
|
parser->column = 0;
|
1378
1448
|
parser->first_nonspace = 0;
|
1379
1449
|
parser->first_nonspace_column = 0;
|
1450
|
+
parser->thematic_break_kill_pos = 0;
|
1380
1451
|
parser->indent = 0;
|
1381
1452
|
parser->blank = false;
|
1382
1453
|
parser->partially_consumed_tab = false;
|
data/ext/commonmarker/buffer.c
CHANGED
data/ext/commonmarker/chunk.h
CHANGED
@@ -7,6 +7,7 @@ extern "C" {
|
|
7
7
|
|
8
8
|
#include "cmark-gfm-extension_api.h"
|
9
9
|
#include "cmark-gfm-extensions_export.h"
|
10
|
+
#include "config.h" // for bool
|
10
11
|
#include <stdint.h>
|
11
12
|
|
12
13
|
CMARK_GFM_EXTENSIONS_EXPORT
|
@@ -15,9 +16,37 @@ void cmark_gfm_core_extensions_ensure_registered(void);
|
|
15
16
|
CMARK_GFM_EXTENSIONS_EXPORT
|
16
17
|
uint16_t cmark_gfm_extensions_get_table_columns(cmark_node *node);
|
17
18
|
|
19
|
+
/** Sets the number of columns for the table, returning 1 on success and 0 on error.
|
20
|
+
*/
|
21
|
+
CMARK_GFM_EXTENSIONS_EXPORT
|
22
|
+
int cmark_gfm_extensions_set_table_columns(cmark_node *node, uint16_t n_columns);
|
23
|
+
|
18
24
|
CMARK_GFM_EXTENSIONS_EXPORT
|
19
25
|
uint8_t *cmark_gfm_extensions_get_table_alignments(cmark_node *node);
|
20
26
|
|
27
|
+
/** Sets the alignments for the table, returning 1 on success and 0 on error.
|
28
|
+
*/
|
29
|
+
CMARK_GFM_EXTENSIONS_EXPORT
|
30
|
+
int cmark_gfm_extensions_set_table_alignments(cmark_node *node, uint16_t ncols, uint8_t *alignments);
|
31
|
+
|
32
|
+
CMARK_GFM_EXTENSIONS_EXPORT
|
33
|
+
int cmark_gfm_extensions_get_table_row_is_header(cmark_node *node);
|
34
|
+
|
35
|
+
/** Sets whether the node is a table header row, returning 1 on success and 0 on error.
|
36
|
+
*/
|
37
|
+
CMARK_GFM_EXTENSIONS_EXPORT
|
38
|
+
int cmark_gfm_extensions_set_table_row_is_header(cmark_node *node, int is_header);
|
39
|
+
|
40
|
+
CMARK_GFM_EXTENSIONS_EXPORT
|
41
|
+
bool cmark_gfm_extensions_get_tasklist_item_checked(cmark_node *node);
|
42
|
+
/* For backwards compatibility */
|
43
|
+
#define cmark_gfm_extensions_tasklist_is_checked cmark_gfm_extensions_get_tasklist_item_checked
|
44
|
+
|
45
|
+
/** Sets whether a tasklist item is "checked" (completed), returning 1 on success and 0 on error.
|
46
|
+
*/
|
47
|
+
CMARK_GFM_EXTENSIONS_EXPORT
|
48
|
+
int cmark_gfm_extensions_set_tasklist_item_checked(cmark_node *node, bool is_checked);
|
49
|
+
|
21
50
|
#ifdef __cplusplus
|
22
51
|
}
|
23
52
|
#endif
|
@@ -106,8 +106,6 @@ typedef struct cmark_plugin cmark_plugin;
|
|
106
106
|
* with 'cmark_syntax_extension_set_private',
|
107
107
|
* and optionally define a free function for this data.
|
108
108
|
*/
|
109
|
-
typedef struct cmark_syntax_extension cmark_syntax_extension;
|
110
|
-
|
111
109
|
typedef struct subject cmark_inline_parser;
|
112
110
|
|
113
111
|
/** Exposed raw for now */
|
@@ -238,6 +236,9 @@ typedef int (*cmark_commonmark_escape_func) (cmark_syntax_extension *extension,
|
|
238
236
|
cmark_node *node,
|
239
237
|
int c);
|
240
238
|
|
239
|
+
typedef const char* (*cmark_xml_attr_func) (cmark_syntax_extension *extension,
|
240
|
+
cmark_node *node);
|
241
|
+
|
241
242
|
typedef void (*cmark_html_render_func) (cmark_syntax_extension *extension,
|
242
243
|
struct cmark_html_renderer *renderer,
|
243
244
|
cmark_node *node,
|
@@ -254,6 +255,10 @@ typedef cmark_node *(*cmark_postprocess_func) (cmark_syntax_extension *extension
|
|
254
255
|
|
255
256
|
typedef int (*cmark_ispunct_func) (char c);
|
256
257
|
|
258
|
+
typedef void (*cmark_opaque_alloc_func) (cmark_syntax_extension *extension,
|
259
|
+
cmark_mem *mem,
|
260
|
+
cmark_node *node);
|
261
|
+
|
257
262
|
typedef void (*cmark_opaque_free_func) (cmark_syntax_extension *extension,
|
258
263
|
cmark_mem *mem,
|
259
264
|
cmark_node *node);
|
@@ -343,6 +348,12 @@ void cmark_syntax_extension_set_latex_render_func(cmark_syntax_extension *extens
|
|
343
348
|
/** See the documentation for 'cmark_syntax_extension'
|
344
349
|
*/
|
345
350
|
CMARK_GFM_EXPORT
|
351
|
+
void cmark_syntax_extension_set_xml_attr_func(cmark_syntax_extension *extension,
|
352
|
+
cmark_xml_attr_func func);
|
353
|
+
|
354
|
+
/** See the documentation for 'cmark_syntax_extension'
|
355
|
+
*/
|
356
|
+
CMARK_GFM_EXPORT
|
346
357
|
void cmark_syntax_extension_set_man_render_func(cmark_syntax_extension *extension,
|
347
358
|
cmark_common_render_func func);
|
348
359
|
|
@@ -382,6 +393,12 @@ CMARK_GFM_EXPORT
|
|
382
393
|
void cmark_syntax_extension_set_postprocess_func(cmark_syntax_extension *extension,
|
383
394
|
cmark_postprocess_func func);
|
384
395
|
|
396
|
+
/** See the documentation for 'cmark_syntax_extension'
|
397
|
+
*/
|
398
|
+
CMARK_GFM_EXPORT
|
399
|
+
void cmark_syntax_extension_set_opaque_alloc_func(cmark_syntax_extension *extension,
|
400
|
+
cmark_opaque_alloc_func func);
|
401
|
+
|
385
402
|
/** See the documentation for 'cmark_syntax_extension'
|
386
403
|
*/
|
387
404
|
CMARK_GFM_EXPORT
|
@@ -92,6 +92,7 @@ typedef enum {
|
|
92
92
|
typedef struct cmark_node cmark_node;
|
93
93
|
typedef struct cmark_parser cmark_parser;
|
94
94
|
typedef struct cmark_iter cmark_iter;
|
95
|
+
typedef struct cmark_syntax_extension cmark_syntax_extension;
|
95
96
|
|
96
97
|
/**
|
97
98
|
* ## Custom memory allocator support
|
@@ -187,6 +188,13 @@ CMARK_GFM_EXPORT cmark_node *cmark_node_new(cmark_node_type type);
|
|
187
188
|
CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_mem(cmark_node_type type,
|
188
189
|
cmark_mem *mem);
|
189
190
|
|
191
|
+
CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_ext(cmark_node_type type,
|
192
|
+
cmark_syntax_extension *extension);
|
193
|
+
|
194
|
+
CMARK_GFM_EXPORT cmark_node *cmark_node_new_with_mem_and_ext(cmark_node_type type,
|
195
|
+
cmark_mem *mem,
|
196
|
+
cmark_syntax_extension *extension);
|
197
|
+
|
190
198
|
/** Frees the memory allocated for a node and any children.
|
191
199
|
*/
|
192
200
|
CMARK_GFM_EXPORT void cmark_node_free(cmark_node *node);
|
@@ -682,14 +690,20 @@ char *cmark_render_latex_with_mem(cmark_node *root, int options, int width, cmar
|
|
682
690
|
*/
|
683
691
|
#define CMARK_OPT_HARDBREAKS (1 << 2)
|
684
692
|
|
685
|
-
/**
|
686
|
-
|
687
|
-
|
688
|
-
* by a placeholder HTML comment. Unsafe links are replaced by
|
689
|
-
* empty strings.
|
693
|
+
/** `CMARK_OPT_SAFE` is defined here for API compatibility,
|
694
|
+
but it no longer has any effect. "Safe" mode is now the default:
|
695
|
+
set `CMARK_OPT_UNSAFE` to disable it.
|
690
696
|
*/
|
691
697
|
#define CMARK_OPT_SAFE (1 << 3)
|
692
698
|
|
699
|
+
/** Render raw HTML and unsafe links (`javascript:`, `vbscript:`,
|
700
|
+
* `file:`, and `data:`, except for `image/png`, `image/gif`,
|
701
|
+
* `image/jpeg`, or `image/webp` mime types). By default,
|
702
|
+
* raw HTML is replaced by a placeholder HTML comment. Unsafe
|
703
|
+
* links are replaced by empty strings.
|
704
|
+
*/
|
705
|
+
#define CMARK_OPT_UNSAFE (1 << 17)
|
706
|
+
|
693
707
|
/** Render `softbreak` elements as spaces.
|
694
708
|
*/
|
695
709
|
#define CMARK_OPT_NOBREAKS (1 << 4)
|
@@ -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) | (
|
5
|
-
#define CMARK_GFM_VERSION_STRING "0.
|
4
|
+
#define CMARK_GFM_VERSION ((0 << 24) | (29 << 16) | (0 << 8) | 3)
|
5
|
+
#define CMARK_GFM_VERSION_STRING "0.29.0.gfm.3"
|
6
6
|
|
7
7
|
#endif
|
@@ -34,7 +34,8 @@ static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node,
|
|
34
34
|
needs_escaping =
|
35
35
|
c < 0x80 && escape != LITERAL &&
|
36
36
|
((escape == NORMAL &&
|
37
|
-
(c
|
37
|
+
(c < 0x20 ||
|
38
|
+
c == '*' || c == '_' || c == '[' || c == ']' || c == '#' || c == '<' ||
|
38
39
|
c == '>' || c == '\\' || c == '`' || c == '~' || c == '!' ||
|
39
40
|
(c == '&' && cmark_isalpha(nextc)) || (c == '!' && nextc == '[') ||
|
40
41
|
(renderer->begin_content && (c == '-' || c == '+' || c == '=') &&
|
@@ -50,14 +51,18 @@ static CMARK_INLINE void outc(cmark_renderer *renderer, cmark_node *node,
|
|
50
51
|
(c == '`' || c == '<' || c == '>' || c == '"' || c == '\\')));
|
51
52
|
|
52
53
|
if (needs_escaping) {
|
53
|
-
if (cmark_isspace((char)c)) {
|
54
|
+
if (escape == URL && cmark_isspace((char)c)) {
|
54
55
|
// use percent encoding for spaces
|
55
|
-
snprintf(encoded, ENCODED_SIZE, "%%%
|
56
|
+
snprintf(encoded, ENCODED_SIZE, "%%%2X", c);
|
56
57
|
cmark_strbuf_puts(renderer->buffer, encoded);
|
57
58
|
renderer->column += 3;
|
58
|
-
} else {
|
59
|
+
} else if (cmark_ispunct((char)c)) {
|
59
60
|
cmark_render_ascii(renderer, "\\");
|
60
61
|
cmark_render_code_point(renderer, c);
|
62
|
+
} else { // render as entity
|
63
|
+
snprintf(encoded, ENCODED_SIZE, "&#%d;", c);
|
64
|
+
cmark_strbuf_puts(renderer->buffer, encoded);
|
65
|
+
renderer->column += (int)strlen(encoded);
|
61
66
|
}
|
62
67
|
} else {
|
63
68
|
cmark_render_code_point(renderer, c);
|
@@ -168,9 +173,11 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
168
173
|
int list_number;
|
169
174
|
cmark_delim_type list_delim;
|
170
175
|
int numticks;
|
176
|
+
bool extra_spaces;
|
171
177
|
int i;
|
172
178
|
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
173
179
|
const char *info, *code, *title;
|
180
|
+
char fencechar[2] = {'\0', '\0'};
|
174
181
|
size_t info_len, code_len;
|
175
182
|
char listmarker[LISTMARKER_SIZE];
|
176
183
|
char *emph_delim;
|
@@ -283,6 +290,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
283
290
|
}
|
284
291
|
info = cmark_node_get_fence_info(node);
|
285
292
|
info_len = strlen(info);
|
293
|
+
fencechar[0] = strchr(info, '`') == NULL ? '`' : '~';
|
286
294
|
code = cmark_node_get_literal(node);
|
287
295
|
code_len = strlen(code);
|
288
296
|
// use indented form if no info, and code doesn't
|
@@ -302,7 +310,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
302
310
|
numticks = 3;
|
303
311
|
}
|
304
312
|
for (i = 0; i < numticks; i++) {
|
305
|
-
LIT(
|
313
|
+
LIT(fencechar);
|
306
314
|
}
|
307
315
|
LIT(" ");
|
308
316
|
OUT(info, false, LITERAL);
|
@@ -310,7 +318,7 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
310
318
|
OUT(cmark_node_get_literal(node), false, LITERAL);
|
311
319
|
CR();
|
312
320
|
for (i = 0; i < numticks; i++) {
|
313
|
-
LIT(
|
321
|
+
LIT(fencechar);
|
314
322
|
}
|
315
323
|
}
|
316
324
|
BLANKLINE();
|
@@ -369,14 +377,17 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
369
377
|
code = cmark_node_get_literal(node);
|
370
378
|
code_len = strlen(code);
|
371
379
|
numticks = shortest_unused_backtick_sequence(code);
|
380
|
+
extra_spaces = code_len == 0 ||
|
381
|
+
code[0] == '`' || code[code_len - 1] == '`' ||
|
382
|
+
code[0] == ' ' || code[code_len - 1] == ' ';
|
372
383
|
for (i = 0; i < numticks; i++) {
|
373
384
|
LIT("`");
|
374
385
|
}
|
375
|
-
if (
|
386
|
+
if (extra_spaces) {
|
376
387
|
LIT(" ");
|
377
388
|
}
|
378
389
|
OUT(cmark_node_get_literal(node), allow_wrap, LITERAL);
|
379
|
-
if (
|
390
|
+
if (extra_spaces) {
|
380
391
|
LIT(" ");
|
381
392
|
}
|
382
393
|
for (i = 0; i < numticks; i++) {
|
@@ -466,7 +477,13 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
466
477
|
case CMARK_NODE_FOOTNOTE_REFERENCE:
|
467
478
|
if (entering) {
|
468
479
|
LIT("[^");
|
469
|
-
|
480
|
+
|
481
|
+
char *footnote_label = renderer->mem->calloc(node->parent_footnote_def->as.literal.len + 1, sizeof(char));
|
482
|
+
memmove(footnote_label, node->parent_footnote_def->as.literal.data, node->parent_footnote_def->as.literal.len);
|
483
|
+
|
484
|
+
OUT(footnote_label, false, LITERAL);
|
485
|
+
renderer->mem->free(footnote_label);
|
486
|
+
|
470
487
|
LIT("]");
|
471
488
|
}
|
472
489
|
break;
|
@@ -475,9 +492,13 @@ static int S_render_node(cmark_renderer *renderer, cmark_node *node,
|
|
475
492
|
if (entering) {
|
476
493
|
renderer->footnote_ix += 1;
|
477
494
|
LIT("[^");
|
478
|
-
|
479
|
-
|
480
|
-
|
495
|
+
|
496
|
+
char *footnote_label = renderer->mem->calloc(node->as.literal.len + 1, sizeof(char));
|
497
|
+
memmove(footnote_label, node->as.literal.data, node->as.literal.len);
|
498
|
+
|
499
|
+
OUT(footnote_label, false, LITERAL);
|
500
|
+
renderer->mem->free(footnote_label);
|
501
|
+
|
481
502
|
LIT("]:\n");
|
482
503
|
|
483
504
|
cmark_strbuf_puts(renderer->prefix, " ");
|