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.

Files changed (74) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +94 -18
  3. data/Rakefile +24 -5
  4. data/bin/commonmarker +107 -47
  5. data/commonmarker.gemspec +18 -15
  6. data/ext/commonmarker/autolink.c +10 -6
  7. data/ext/commonmarker/blocks.c +102 -31
  8. data/ext/commonmarker/buffer.c +0 -1
  9. data/ext/commonmarker/chunk.h +0 -1
  10. data/ext/commonmarker/cmark-gfm-core-extensions.h +29 -0
  11. data/ext/commonmarker/cmark-gfm-extension_api.h +19 -2
  12. data/ext/commonmarker/cmark-gfm.h +19 -5
  13. data/ext/commonmarker/cmark-gfm_version.h +2 -2
  14. data/ext/commonmarker/commonmark.c +33 -12
  15. data/ext/commonmarker/commonmarker.c +209 -100
  16. data/ext/commonmarker/core-extensions.c +2 -0
  17. data/ext/commonmarker/ext_scanners.c +622 -684
  18. data/ext/commonmarker/ext_scanners.h +2 -0
  19. data/ext/commonmarker/extconf.rb +3 -1
  20. data/ext/commonmarker/footnotes.c +23 -0
  21. data/ext/commonmarker/footnotes.h +2 -0
  22. data/ext/commonmarker/houdini_href_e.c +1 -1
  23. data/ext/commonmarker/html.c +46 -25
  24. data/ext/commonmarker/inlines.c +127 -30
  25. data/ext/commonmarker/iterator.h +0 -1
  26. data/ext/commonmarker/map.h +0 -1
  27. data/ext/commonmarker/node.c +17 -3
  28. data/ext/commonmarker/node.h +9 -0
  29. data/ext/commonmarker/parser.h +2 -1
  30. data/ext/commonmarker/plaintext.c +22 -0
  31. data/ext/commonmarker/render.c +18 -15
  32. data/ext/commonmarker/render.h +0 -1
  33. data/ext/commonmarker/scanners.c +779 -953
  34. data/ext/commonmarker/scanners.h +0 -2
  35. data/ext/commonmarker/strikethrough.c +4 -1
  36. data/ext/commonmarker/syntax_extension.c +10 -0
  37. data/ext/commonmarker/syntax_extension.h +2 -0
  38. data/ext/commonmarker/table.c +178 -31
  39. data/ext/commonmarker/tasklist.c +156 -0
  40. data/ext/commonmarker/tasklist.h +8 -0
  41. data/ext/commonmarker/xml.c +9 -2
  42. data/lib/commonmarker/config.rb +41 -38
  43. data/lib/commonmarker/errors.rb +12 -0
  44. data/lib/commonmarker/node/inspect.rb +15 -17
  45. data/lib/commonmarker/node.rb +14 -2
  46. data/lib/commonmarker/renderer/html_renderer.rb +45 -36
  47. data/lib/commonmarker/renderer.rb +16 -10
  48. data/lib/commonmarker/version.rb +3 -1
  49. data/lib/commonmarker.rb +8 -7
  50. data/test/benchmark.rb +26 -21
  51. data/test/fixtures/strong.md +1 -0
  52. data/test/fixtures/table.md +10 -0
  53. data/test/test_attributes.rb +5 -3
  54. data/test/test_basics.rb +19 -0
  55. data/test/test_commands.rb +72 -0
  56. data/test/test_commonmark.rb +15 -13
  57. data/test/test_doc.rb +31 -29
  58. data/test/test_encoding.rb +9 -5
  59. data/test/test_extensions.rb +66 -73
  60. data/test/test_footnotes.rb +47 -12
  61. data/test/test_gc.rb +6 -2
  62. data/test/test_helper.rb +25 -15
  63. data/test/test_linebreaks.rb +2 -0
  64. data/test/test_maliciousness.rb +189 -190
  65. data/test/test_node.rb +12 -12
  66. data/test/test_options.rb +17 -15
  67. data/test/test_pathological_inputs.rb +14 -12
  68. data/test/test_plaintext.rb +23 -21
  69. data/test/test_renderer.rb +29 -10
  70. data/test/test_smartpunct.rb +7 -2
  71. data/test/test_spec.rb +7 -4
  72. data/test/test_tasklists.rb +43 -0
  73. data/test/test_xml.rb +107 -0
  74. metadata +74 -30
@@ -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 ends_with_blank_line(cmark_node *node) {
238
- cmark_node *cur = node;
239
- while (cur != NULL) {
240
- if (S_last_line_blank(cur)) {
241
- return true;
242
- }
243
- if (S_type(cur) == CMARK_NODE_LIST || S_type(cur) == CMARK_NODE_ITEM) {
244
- cur = cur->last_child;
245
- } else {
246
- cur = NULL;
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
- return false;
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
- cmark_chunk chunk = {node_content->ptr, node_content->size, 0};
287
- while (chunk.len && chunk.data[0] == '[' &&
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 (ends_with_blank_line(subitem) && (item->next || subitem->next)) {
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
- (*container)->type = (uint16_t)CMARK_NODE_HEADING;
1116
- (*container)->as.heading.level = lev;
1117
- (*container)->as.heading.setext = true;
1118
- S_advance_offset(parser, input, input->len - 1 - parser->offset, false);
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
- (matched = scan_thematic_break(input, parser->first_nonspace))) {
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;
@@ -10,7 +10,6 @@
10
10
  #include "config.h"
11
11
  #include "cmark_ctype.h"
12
12
  #include "buffer.h"
13
- #include "memory.h"
14
13
 
15
14
  /* Used as default value for cmark_strbuf->ptr so that people can always
16
15
  * assume ptr is non-NULL and zero terminated even for new cmark_strbufs.
@@ -6,7 +6,6 @@
6
6
  #include <assert.h>
7
7
  #include "cmark-gfm.h"
8
8
  #include "buffer.h"
9
- #include "memory.h"
10
9
  #include "cmark_ctype.h"
11
10
 
12
11
  #define CMARK_CHUNK_EMPTY \
@@ -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
- /** Suppress raw HTML and unsafe links (`javascript:`, `vbscript:`,
686
- * `file:`, and `data:`, except for `image/png`, `image/gif`,
687
- * `image/jpeg`, or `image/webp` mime types). Raw HTML is replaced
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) | (28 << 16) | (3 << 8) | 16)
5
- #define CMARK_GFM_VERSION_STRING "0.28.3.gfm.16"
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 == '*' || c == '_' || c == '[' || c == ']' || c == '#' || 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, "%%%2x", c);
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 (code_len == 0 || code[0] == '`') {
386
+ if (extra_spaces) {
376
387
  LIT(" ");
377
388
  }
378
389
  OUT(cmark_node_get_literal(node), allow_wrap, LITERAL);
379
- if (code_len == 0 || code[code_len - 1] == '`') {
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
- OUT(cmark_chunk_to_cstr(renderer->mem, &node->as.literal), false, LITERAL);
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
- char n[32];
479
- snprintf(n, sizeof(n), "%d", renderer->footnote_ix);
480
- OUT(n, false, LITERAL);
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, " ");