commonmarker 0.2.1 → 0.3.0

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/Rakefile +0 -5
  4. data/ext/commonmarker/cmark/CMakeLists.txt +1 -1
  5. data/ext/commonmarker/cmark/Makefile +12 -7
  6. data/ext/commonmarker/cmark/README.md +12 -8
  7. data/ext/commonmarker/cmark/api_test/main.c +18 -2
  8. data/ext/commonmarker/cmark/benchmarks.md +4 -4
  9. data/ext/commonmarker/cmark/build/CMakeFiles/CMakeError.log +12 -12
  10. data/ext/commonmarker/cmark/build/CMakeFiles/CMakeOutput.log +106 -106
  11. data/ext/commonmarker/cmark/build/CMakeFiles/Makefile2 +7 -7
  12. data/ext/commonmarker/cmark/build/CMakeFiles/progress.marks +1 -1
  13. data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/build.make +1 -1
  14. data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/link.txt +1 -1
  15. data/ext/commonmarker/cmark/build/api_test/CMakeFiles/progress.marks +1 -1
  16. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/DependInfo.cmake +2 -0
  17. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/build.make +61 -9
  18. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/cmake_clean.cmake +2 -0
  19. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/link.txt +1 -1
  20. data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/progress.make +2 -0
  21. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/DependInfo.cmake +3 -1
  22. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/build.make +81 -29
  23. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/cmake_clean.cmake +3 -1
  24. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/link.txt +1 -1
  25. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/progress.make +19 -17
  26. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/C.includecache +56 -10
  27. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/DependInfo.cmake +2 -0
  28. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/blocks.c.o +0 -0
  29. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/build.make +60 -8
  30. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/cmake_clean.cmake +2 -0
  31. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/cmark.c.o +0 -0
  32. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/commonmark.c.o +0 -0
  33. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/depend.internal +27 -1
  34. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/depend.make +27 -1
  35. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/flags.make +1 -1
  36. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/html.c.o +0 -0
  37. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/inlines.c.o +0 -0
  38. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/latex.c.o +0 -0
  39. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/link.txt +1 -1
  40. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/man.c.o +0 -0
  41. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/progress.make +19 -17
  42. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/render.c.o +0 -0
  43. data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/scanners.c.o +0 -0
  44. data/ext/commonmarker/cmark/build/src/CMakeFiles/progress.marks +1 -1
  45. data/ext/commonmarker/cmark/build/src/Makefile +66 -0
  46. data/ext/commonmarker/cmark/build/src/cmake_install.cmake +3 -3
  47. data/ext/commonmarker/cmark/build/src/cmark_version.h +2 -2
  48. data/ext/commonmarker/cmark/build/src/libcmark.a +0 -0
  49. data/ext/commonmarker/cmark/build/src/libcmark.pc +1 -1
  50. data/ext/commonmarker/cmark/changelog.txt +144 -0
  51. data/ext/commonmarker/cmark/man/make_man_page.py +3 -3
  52. data/ext/commonmarker/cmark/man/man1/cmark.1 +10 -2
  53. data/ext/commonmarker/cmark/man/man3/cmark.3 +106 -85
  54. data/ext/commonmarker/cmark/src/CMakeLists.txt +5 -2
  55. data/ext/commonmarker/cmark/src/blocks.c +76 -9
  56. data/ext/commonmarker/cmark/src/cmark.c +9 -2
  57. data/ext/commonmarker/cmark/src/cmark.h +16 -3
  58. data/ext/commonmarker/cmark/src/commonmark.c +162 -309
  59. data/ext/commonmarker/cmark/src/html.c +30 -10
  60. data/ext/commonmarker/cmark/src/inlines.c +80 -72
  61. data/ext/commonmarker/cmark/src/latex.c +430 -0
  62. data/ext/commonmarker/cmark/src/main.c +12 -4
  63. data/ext/commonmarker/cmark/src/man.c +118 -156
  64. data/ext/commonmarker/cmark/src/node.h +1 -0
  65. data/ext/commonmarker/cmark/src/render.c +186 -0
  66. data/ext/commonmarker/cmark/src/render.h +66 -0
  67. data/ext/commonmarker/cmark/src/scanners.c +14586 -8944
  68. data/ext/commonmarker/cmark/src/scanners.h +16 -2
  69. data/ext/commonmarker/cmark/src/scanners.re +93 -9
  70. data/ext/commonmarker/cmark/test/__pycache__/cmark.cpython-34.pyc +0 -0
  71. data/ext/commonmarker/cmark/test/__pycache__/normalize.cpython-34.pyc +0 -0
  72. data/ext/commonmarker/cmark/test/smart_punct.txt +74 -10
  73. data/ext/commonmarker/cmark/test/spec.txt +726 -92
  74. data/ext/commonmarker/cmark/test/spec_tests.py +16 -13
  75. data/lib/commonmarker/config.rb +2 -0
  76. data/lib/commonmarker/version.rb +1 -1
  77. data/test/test_helper.rb +1 -1
  78. data/test/test_spec.rb +11 -10
  79. metadata +9 -6
  80. data/ext/commonmarker/cmark/algorithm.md +0 -116
  81. data/ext/commonmarker/cmark/src/debug.h +0 -36
  82. data/test/spec_tests.json +0 -4482
@@ -9,13 +9,13 @@ set(HEADERS
9
9
  iterator.h
10
10
  chunk.h
11
11
  references.h
12
- debug.h
13
12
  bench.h
14
13
  utf8.h
15
14
  scanners.h
16
15
  inlines.h
17
16
  houdini.h
18
17
  cmark_ctype.h
18
+ render.h
19
19
  )
20
20
  set(LIBRARY_SOURCES
21
21
  cmark.c
@@ -28,10 +28,12 @@ set(LIBRARY_SOURCES
28
28
  utf8.c
29
29
  buffer.c
30
30
  references.c
31
+ render.c
31
32
  man.c
32
33
  xml.c
33
34
  html.c
34
35
  commonmark.c
36
+ latex.c
35
37
  houdini_href_e.c
36
38
  houdini_html_e.c
37
39
  houdini_html_u.c
@@ -86,7 +88,8 @@ set_target_properties(${LIBRARY} PROPERTIES
86
88
  SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}
87
89
  VERSION ${PROJECT_VERSION})
88
90
  set_target_properties(${STATICLIBRARY} PROPERTIES
89
- COMPILE_FLAGS -DCMARK_STATIC_DEFINE)
91
+ COMPILE_FLAGS -DCMARK_STATIC_DEFINE
92
+ POSITION_INDEPENDENT_CODE ON)
90
93
 
91
94
  if (MSVC)
92
95
  set_target_properties(${STATICLIBRARY} PROPERTIES
@@ -13,7 +13,6 @@
13
13
  #include "inlines.h"
14
14
  #include "houdini.h"
15
15
  #include "buffer.h"
16
- #include "debug.h"
17
16
 
18
17
  #define CODE_INDENT 4
19
18
  #define TAB_STOP 4
@@ -401,11 +400,16 @@ static bufsize_t parse_list_marker(cmark_chunk *input, bufsize_t pos, cmark_list
401
400
  }
402
401
  } else if (cmark_isdigit(c)) {
403
402
  int start = 0;
403
+ int digits = 0;
404
404
 
405
405
  do {
406
406
  start = (10 * start) + (peek_at(input, pos) - '0');
407
407
  pos++;
408
- } while (cmark_isdigit(peek_at(input, pos)));
408
+ digits++;
409
+ // We limit to 9 digits to avoid overflow,
410
+ // assuming max int is 2^31 - 1
411
+ // This also seems to be the limit for 'start' in some browsers.
412
+ } while (digits < 9 && cmark_isdigit(peek_at(input, pos)));
409
413
 
410
414
  c = peek_at(input, pos);
411
415
  if (c == '.' || c == ')') {
@@ -560,8 +564,9 @@ static void chop_trailing_hashtags(cmark_chunk *ch)
560
564
  while (n >= 0 && peek_at(ch, n) == '#')
561
565
  n--;
562
566
 
563
- // Check for a be a space before the final #s:
564
- if (n != orig_n && n >= 0 && peek_at(ch, n) == ' ') {
567
+ // Check for a space before the final #s:
568
+ if (n != orig_n && n >= 0 &&
569
+ (peek_at(ch, n) == ' ' || peek_at(ch, n) == '\t')) {
565
570
  ch->len = n;
566
571
  cmark_chunk_rtrim(ch);
567
572
  }
@@ -722,8 +727,26 @@ S_process_line(cmark_parser *parser, const unsigned char *buffer, bufsize_t byte
722
727
 
723
728
  } else if (container->type == NODE_HTML) {
724
729
 
725
- if (parser->blank) {
726
- all_matched = false;
730
+ switch (container->as.html_block_type) {
731
+ case 1:
732
+ case 2:
733
+ case 3:
734
+ case 4:
735
+ case 5:
736
+ // these types of blocks can accept blanks
737
+ break;
738
+ case 6:
739
+ case 7:
740
+ if (parser->blank) {
741
+ all_matched = false;
742
+ }
743
+ break;
744
+ default:
745
+ fprintf(stderr,
746
+ "Error (%s:%d): Unknown HTML block type %d\n",
747
+ __FILE__, __LINE__,
748
+ container->as.html_block_type);
749
+ exit(1);
727
750
  }
728
751
 
729
752
  } else if (container->type == NODE_PARAGRAPH) {
@@ -789,9 +812,13 @@ S_process_line(cmark_parser *parser, const unsigned char *buffer, bufsize_t byte
789
812
  container->as.code.info = cmark_chunk_literal("");
790
813
  S_advance_offset(parser, &input, parser->first_nonspace + matched - parser->offset, false);
791
814
 
792
- } else if (!indented && (matched = scan_html_block_tag(&input, parser->first_nonspace))) {
815
+ } else if (!indented &&
816
+ ((matched = scan_html_block_start(&input, parser->first_nonspace)) ||
817
+ (container->type != NODE_PARAGRAPH &&
818
+ (matched = scan_html_block_start_7(&input, parser->first_nonspace))))) {
793
819
 
794
820
  container = add_child(parser, container, NODE_HTML, parser->first_nonspace + 1);
821
+ container->as.html_block_type = matched;
795
822
  // note, we don't adjust parser->offset because the tag is part of the text
796
823
 
797
824
  } else if (!indented &&
@@ -923,11 +950,51 @@ S_process_line(cmark_parser *parser, const unsigned char *buffer, bufsize_t byte
923
950
  assert(parser->current != NULL);
924
951
  }
925
952
 
926
- if (container->type == NODE_CODE_BLOCK ||
927
- container->type == NODE_HTML) {
953
+ if (container->type == NODE_CODE_BLOCK) {
954
+
955
+ add_line(container, &input, parser->offset);
956
+
957
+ } else if (container->type == NODE_HTML) {
928
958
 
929
959
  add_line(container, &input, parser->offset);
930
960
 
961
+ int matches_end_condition;
962
+ switch (container->as.html_block_type) {
963
+ case 1:
964
+ // </script>, </style>, </pre>
965
+ matches_end_condition =
966
+ scan_html_block_end_1(&input, parser->first_nonspace);
967
+ break;
968
+ case 2:
969
+ // -->
970
+ matches_end_condition =
971
+ scan_html_block_end_2(&input, parser->first_nonspace);
972
+ break;
973
+ case 3:
974
+ // ?>
975
+ matches_end_condition =
976
+ scan_html_block_end_3(&input, parser->first_nonspace);
977
+ break;
978
+ case 4:
979
+ // >
980
+ matches_end_condition =
981
+ scan_html_block_end_4(&input, parser->first_nonspace);
982
+ break;
983
+ case 5:
984
+ // ]]>
985
+ matches_end_condition =
986
+ scan_html_block_end_5(&input, parser->first_nonspace);
987
+ break;
988
+ default:
989
+ matches_end_condition = 0;
990
+ break;
991
+ }
992
+
993
+ if (matches_end_condition) {
994
+ container = finalize(parser, container);
995
+ assert(parser->current != NULL);
996
+ }
997
+
931
998
  } else if (parser->blank) {
932
999
 
933
1000
  // ??? do nothing
@@ -6,8 +6,15 @@
6
6
  #include "cmark.h"
7
7
  #include "buffer.h"
8
8
 
9
- const int cmark_version = CMARK_VERSION;
10
- const char cmark_version_string[] = CMARK_VERSION_STRING;
9
+ int cmark_version()
10
+ {
11
+ return CMARK_VERSION;
12
+ }
13
+
14
+ const char *cmark_version_string()
15
+ {
16
+ return CMARK_VERSION_STRING;
17
+ }
11
18
 
12
19
  char *cmark_markdown_to_html(const char *text, size_t len, int options)
13
20
  {
@@ -479,13 +479,18 @@ char *cmark_render_html(cmark_node *root, int options);
479
479
  /** Render a 'node' tree as a groff man page, without the header.
480
480
  */
481
481
  CMARK_EXPORT
482
- char *cmark_render_man(cmark_node *root, int options);
482
+ char *cmark_render_man(cmark_node *root, int options, int width);
483
483
 
484
484
  /** Render a 'node' tree as a commonmark document.
485
485
  */
486
486
  CMARK_EXPORT
487
487
  char *cmark_render_commonmark(cmark_node *root, int options, int width);
488
488
 
489
+ /** Render a 'node' tree as a LaTeX document.
490
+ */
491
+ CMARK_EXPORT
492
+ char *cmark_render_latex(cmark_node *root, int options, int width);
493
+
489
494
  /** Default writer options.
490
495
  */
491
496
  #define CMARK_OPT_DEFAULT 0
@@ -511,6 +516,14 @@ char *cmark_render_commonmark(cmark_node *root, int options, int width);
511
516
  */
512
517
  #define CMARK_OPT_VALIDATE_UTF8 16
513
518
 
519
+ /** Suppress raw HTML and unsafe links (`javascript:`, `vbscript:`,
520
+ * `file:`, and `data:`, except for `image/png`, `image/gif`,
521
+ * `image/jpeg`, or `image/webp` mime types). Raw HTML is replaced
522
+ * by a placeholder HTML comment. Unsafe links are replaced by
523
+ * empty strings.
524
+ */
525
+ #define CMARK_OPT_SAFE 32
526
+
514
527
  /**
515
528
  * ## Version information
516
529
  */
@@ -525,13 +538,13 @@ char *cmark_render_commonmark(cmark_node *root, int options, int width);
525
538
  * In hexadecimal format, the number 0x010203 represents version 1.2.3.
526
539
  */
527
540
  CMARK_EXPORT
528
- extern const int cmark_version;
541
+ int cmark_version();
529
542
 
530
543
  /** The library version string for runtime checks. Also available as
531
544
  * macro CMARK_VERSION_STRING for compile time checks.
532
545
  */
533
546
  CMARK_EXPORT
534
- extern const char cmark_version_string[];
547
+ const char *cmark_version_string();
535
548
 
536
549
  /** # AUTHORS
537
550
  *
@@ -10,192 +10,63 @@
10
10
  #include "buffer.h"
11
11
  #include "utf8.h"
12
12
  #include "scanners.h"
13
+ #include "render.h"
13
14
 
14
- // Functions to convert cmark_nodes to commonmark strings.
15
-
16
- struct render_state {
17
- int options;
18
- cmark_strbuf* buffer;
19
- cmark_strbuf* prefix;
20
- int column;
21
- int width;
22
- int need_cr;
23
- bufsize_t last_breakable;
24
- bool begin_line;
25
- bool no_wrap;
26
- bool in_tight_list_item;
27
- };
28
-
29
- static inline void cr(struct render_state *state)
30
- {
31
- if (state->need_cr < 1) {
32
- state->need_cr = 1;
33
- }
34
- }
15
+ #define safe_strlen(s) cmark_strbuf_safe_strlen(s)
16
+ #define OUT(s, wrap, escaping) renderer->out(renderer, s, wrap, escaping)
17
+ #define LIT(s) renderer->out(renderer, s, false, LITERAL)
18
+ #define CR() renderer->cr(renderer)
19
+ #define BLANKLINE() renderer->blankline(renderer)
35
20
 
36
- static inline void blankline(struct render_state *state)
37
- {
38
- if (state->need_cr < 2) {
39
- state->need_cr = 2;
40
- }
41
- }
42
-
43
- typedef enum {
44
- LITERAL,
45
- NORMAL,
46
- TITLE,
47
- URL
48
- } escaping;
49
-
50
- static inline bool
51
- needs_escaping(escaping escape,
52
- int32_t c,
53
- unsigned char next_c,
54
- struct render_state *state)
55
- {
56
- if (escape == NORMAL) {
57
- return (c == '*' || c == '_' || c == '[' || c == ']' ||
58
- c == '<' || c == '>' || c == '\\' || c == '`' ||
59
- (c == '&' && isalpha(next_c)) ||
60
- (c == '!' && next_c == '[') ||
61
- (state->begin_line &&
62
- (c == '-' || c == '+' || c == '#' || c == '=')) ||
63
- (c == '#' && (isspace(next_c) || next_c == '\0')) ||
64
- ((c == '.' || c == ')') &&
65
- isdigit(state->buffer->ptr[state->buffer->size - 1])));
66
- } else if (escape == TITLE) {
67
- return (c == '`' || c == '<' || c == '>' || c == '"' ||
68
- c == '\\');
69
- } else if (escape == URL) {
70
- return (c == '`' || c == '<' || c == '>' || isspace(c) ||
71
- c == '\\' || c == ')' || c == '(');
72
- } else {
73
- return false;
74
- }
75
- }
21
+ // Functions to convert cmark_nodes to commonmark strings.
76
22
 
77
- static inline void out(struct render_state *state,
78
- cmark_chunk str,
79
- bool wrap,
80
- escaping escape)
23
+ static inline void outc(cmark_renderer *renderer,
24
+ cmark_escaping escape,
25
+ int32_t c,
26
+ unsigned char nextc)
81
27
  {
82
- unsigned char* source = str.data;
83
- int length = str.len;
84
- unsigned char nextc;
85
- int32_t c;
86
- int i = 0;
87
- int len;
88
- cmark_chunk remainder = cmark_chunk_literal("");
89
- int k = state->buffer->size - 1;
90
-
91
- wrap = wrap && !state->no_wrap;
92
-
93
- if (state->in_tight_list_item && state->need_cr > 1) {
94
- state->need_cr = 1;
95
- }
96
- while (state->need_cr) {
97
- if (k < 0 || state->buffer->ptr[k] == '\n') {
98
- k -= 1;
28
+ bool needs_escaping = false;
29
+ needs_escaping =
30
+ escape != LITERAL &&
31
+ ((escape == NORMAL &&
32
+ (c == '*' || c == '_' || c == '[' || c == ']' || c == '#' ||
33
+ c == '<' || c == '>' || c == '\\' || c == '`' || c == '!' ||
34
+ (c == '&' && isalpha(nextc)) ||
35
+ (c == '!' && nextc == '[') ||
36
+ (renderer->begin_line &&
37
+ (c == '-' || c == '+' || c == '=')) ||
38
+ ((c == '.' || c == ')') &&
39
+ isdigit(renderer->buffer->ptr[renderer->buffer->size - 1])))) ||
40
+ (escape == URL &&
41
+ (c == '`' || c == '<' || c == '>' || isspace(c) ||
42
+ c == '\\' || c == ')' || c == '(')) ||
43
+ (escape == TITLE &&
44
+ (c == '`' || c == '<' || c == '>' || c == '"' ||
45
+ c == '\\')));
46
+
47
+ if (needs_escaping) {
48
+ if (isspace(c)) {
49
+ // use percent encoding for spaces
50
+ cmark_strbuf_printf(renderer->buffer, "%%%2x", c);
51
+ renderer->column += 3;
99
52
  } else {
100
- cmark_strbuf_putc(state->buffer, '\n');
101
- if (state->need_cr > 1) {
102
- cmark_strbuf_put(state->buffer, state->prefix->ptr,
103
- state->prefix->size);
104
- }
105
- }
106
- state->column = 0;
107
- state->begin_line = true;
108
- state->need_cr -= 1;
109
- }
110
-
111
- while (i < length) {
112
- if (state->begin_line) {
113
- cmark_strbuf_put(state->buffer, state->prefix->ptr,
114
- state->prefix->size);
115
- // note: this assumes prefix is ascii:
116
- state->column = state->prefix->size;
117
- }
118
-
119
- len = utf8proc_iterate(source + i, length - i, &c);
120
- if (len == -1) { // error condition
121
- return; // return without rendering rest of string
53
+ cmark_render_ascii(renderer, "\\");
54
+ cmark_render_code_point(renderer, c);
122
55
  }
123
- nextc = source[i + len];
124
- if (c == 32 && wrap) {
125
- if (!state->begin_line) {
126
- cmark_strbuf_putc(state->buffer, ' ');
127
- state->column += 1;
128
- state->begin_line = false;
129
- state->last_breakable = state->buffer->size -
130
- 1;
131
- // skip following spaces
132
- while (source[i + 1] == ' ') {
133
- i++;
134
- }
135
- }
136
-
137
- } else if (c == 10) {
138
- cmark_strbuf_putc(state->buffer, '\n');
139
- state->column = 0;
140
- state->begin_line = true;
141
- state->last_breakable = 0;
142
- } else if (needs_escaping(escape, c, nextc, state)) {
143
- if (isspace(c)) {
144
- // use percent encoding for spaces
145
- cmark_strbuf_printf(state->buffer, "%%%2x", c);
146
- state->column += 3;
147
- } else {
148
- cmark_strbuf_putc(state->buffer, '\\');
149
- utf8proc_encode_char(c, state->buffer);
150
- state->column += 2;
151
- }
152
- state->begin_line = false;
153
- } else {
154
- utf8proc_encode_char(c, state->buffer);
155
- state->column += 1;
156
- state->begin_line = false;
157
- }
158
-
159
- // If adding the character went beyond width, look for an
160
- // earlier place where the line could be broken:
161
- if (state->width > 0 &&
162
- state->column > state->width &&
163
- !state->begin_line &&
164
- state->last_breakable > 0) {
165
-
166
- // copy from last_breakable to remainder
167
- cmark_chunk_set_cstr(&remainder, (char *) state->buffer->ptr + state->last_breakable + 1);
168
- // truncate at last_breakable
169
- cmark_strbuf_truncate(state->buffer, state->last_breakable);
170
- // add newline, prefix, and remainder
171
- cmark_strbuf_putc(state->buffer, '\n');
172
- cmark_strbuf_put(state->buffer, state->prefix->ptr,
173
- state->prefix->size);
174
- cmark_strbuf_put(state->buffer, remainder.data, remainder.len);
175
- state->column = state->prefix->size + remainder.len;
176
- cmark_chunk_free(&remainder);
177
- state->last_breakable = 0;
178
- state->begin_line = false;
179
- }
180
-
181
- i += len;
56
+ } else {
57
+ cmark_render_code_point(renderer, c);
182
58
  }
183
59
  }
184
60
 
185
- static void lit(struct render_state *state, char *s, bool wrap)
186
- {
187
- cmark_chunk str = cmark_chunk_literal(s);
188
- out(state, str, wrap, LITERAL);
189
- }
190
-
191
61
  static int
192
- longest_backtick_sequence(cmark_chunk *code)
62
+ longest_backtick_sequence(const char *code)
193
63
  {
194
64
  int longest = 0;
195
65
  int current = 0;
196
- int i = 0;
197
- while (i <= code->len) {
198
- if (code->data[i] == '`') {
66
+ size_t i = 0;
67
+ size_t code_len = safe_strlen(code);
68
+ while (i <= code_len) {
69
+ if (code[i] == '`') {
199
70
  current++;
200
71
  } else {
201
72
  if (current > longest) {
@@ -209,13 +80,14 @@ longest_backtick_sequence(cmark_chunk *code)
209
80
  }
210
81
 
211
82
  static int
212
- shortest_unused_backtick_sequence(cmark_chunk *code)
83
+ shortest_unused_backtick_sequence(const char *code)
213
84
  {
214
85
  int32_t used = 1;
215
86
  int current = 0;
216
- int i = 0;
217
- while (i <= code->len) {
218
- if (code->data[i] == '`') {
87
+ size_t i = 0;
88
+ size_t code_len = safe_strlen(code);
89
+ while (i <= code_len) {
90
+ if (code[i] == '`') {
219
91
  current++;
220
92
  } else {
221
93
  if (current) {
@@ -240,6 +112,8 @@ is_autolink(cmark_node *node)
240
112
  cmark_chunk *title;
241
113
  cmark_chunk *url;
242
114
  cmark_node *link_text;
115
+ char *realurl;
116
+ int realurllen;
243
117
 
244
118
  if (node->type != CMARK_NODE_LINK) {
245
119
  return false;
@@ -258,8 +132,14 @@ is_autolink(cmark_node *node)
258
132
 
259
133
  link_text = node->first_child;
260
134
  cmark_consolidate_text_nodes(link_text);
261
- return (url->len == link_text->as.literal.len &&
262
- strncmp((char*)url->data,
135
+ realurl = (char*)url->data;
136
+ realurllen = url->len;
137
+ if (strncmp(realurl, "mailto:", 7) == 0) {
138
+ realurl += 7;
139
+ realurllen -= 7;
140
+ }
141
+ return (realurllen == link_text->as.literal.len &&
142
+ strncmp(realurl,
263
143
  (char*)link_text->as.literal.data,
264
144
  link_text->as.literal.len) == 0);
265
145
  }
@@ -278,18 +158,19 @@ get_containing_block(cmark_node *node)
278
158
  }
279
159
 
280
160
  static int
281
- S_render_node(cmark_node *node, cmark_event_type ev_type,
282
- struct render_state *state)
161
+ S_render_node(cmark_renderer *renderer,
162
+ cmark_node *node,
163
+ cmark_event_type ev_type,
164
+ int options)
283
165
  {
284
166
  cmark_node *tmp;
285
- cmark_chunk *code;
286
167
  int list_number;
287
168
  cmark_delim_type list_delim;
288
169
  int numticks;
289
170
  int i;
290
171
  bool entering = (ev_type == CMARK_EVENT_ENTER);
291
- cmark_chunk *info;
292
- cmark_chunk *title;
172
+ const char *info, *code, *title;
173
+ size_t info_len, code_len;
293
174
  cmark_strbuf listmarker = GH_BUF_INIT;
294
175
  char *emph_delim;
295
176
  bufsize_t marker_width;
@@ -300,7 +181,7 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
300
181
  if (!(node->type == CMARK_NODE_ITEM && node->prev == NULL &&
301
182
  entering)) {
302
183
  tmp = get_containing_block(node);
303
- state->in_tight_list_item =
184
+ renderer->in_tight_list_item =
304
185
  (tmp->type == CMARK_NODE_ITEM &&
305
186
  cmark_node_get_list_tight(tmp->parent)) ||
306
187
  (tmp &&
@@ -311,19 +192,16 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
311
192
 
312
193
  switch (node->type) {
313
194
  case CMARK_NODE_DOCUMENT:
314
- if (!entering) {
315
- cmark_strbuf_putc(state->buffer, '\n');
316
- }
317
195
  break;
318
196
 
319
197
  case CMARK_NODE_BLOCK_QUOTE:
320
198
  if (entering) {
321
- lit(state, "> ", false);
322
- cmark_strbuf_puts(state->prefix, "> ");
199
+ LIT("> ");
200
+ cmark_strbuf_puts(renderer->prefix, "> ");
323
201
  } else {
324
- cmark_strbuf_truncate(state->prefix,
325
- state->prefix->size - 2);
326
- blankline(state);
202
+ cmark_strbuf_truncate(renderer->prefix,
203
+ renderer->prefix->size - 2);
204
+ BLANKLINE();
327
205
  }
328
206
  break;
329
207
 
@@ -333,8 +211,7 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
333
211
  node->next->type == CMARK_NODE_LIST)) {
334
212
  // this ensures 2 blank lines after list,
335
213
  // if before code block or list:
336
- lit(state, "\n", false);
337
- state->need_cr = 0;
214
+ LIT("\n");
338
215
  }
339
216
  break;
340
217
 
@@ -363,19 +240,19 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
363
240
  if (entering) {
364
241
  if (cmark_node_get_list_type(node->parent) ==
365
242
  CMARK_BULLET_LIST) {
366
- lit(state, "* ", false);
367
- cmark_strbuf_puts(state->prefix, " ");
243
+ LIT("* ");
244
+ cmark_strbuf_puts(renderer->prefix, " ");
368
245
  } else {
369
- lit(state, (char *)listmarker.ptr, false);
246
+ LIT((char *)listmarker.ptr);
370
247
  for (i = marker_width; i--;) {
371
- cmark_strbuf_putc(state->prefix, ' ');
248
+ cmark_strbuf_putc(renderer->prefix, ' ');
372
249
  }
373
250
  }
374
251
  } else {
375
- cmark_strbuf_truncate(state->prefix,
376
- state->prefix->size -
252
+ cmark_strbuf_truncate(renderer->prefix,
253
+ renderer->prefix->size -
377
254
  marker_width);
378
- cr(state);
255
+ CR();
379
256
  }
380
257
  cmark_strbuf_free(&listmarker);
381
258
  break;
@@ -383,119 +260,123 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
383
260
  case CMARK_NODE_HEADER:
384
261
  if (entering) {
385
262
  for (int i = cmark_node_get_header_level(node); i > 0; i--) {
386
- lit(state, "#", false);
263
+ LIT("#");
387
264
  }
388
- lit(state, " ", false);
389
- state->no_wrap = true;
265
+ LIT(" ");
266
+ renderer->no_wrap = true;
390
267
  } else {
391
- state->no_wrap = false;
392
- blankline(state);
268
+ renderer->no_wrap = false;
269
+ BLANKLINE();
393
270
  }
394
271
  break;
395
272
 
396
273
  case CMARK_NODE_CODE_BLOCK:
397
- blankline(state);
398
- info = &node->as.code.info;
399
- code = &node->as.code.literal;
274
+ BLANKLINE();
275
+ info = cmark_node_get_fence_info(node);
276
+ info_len = safe_strlen(info);
277
+ code = cmark_node_get_literal(node);
278
+ code_len = safe_strlen(code);
400
279
  // use indented form if no info, and code doesn't
401
280
  // begin or end with a blank line, and code isn't
402
281
  // first thing in a list item
403
- if (info->len == 0 &&
404
- (code->len > 2 &&
405
- !isspace(code->data[0]) &&
406
- !(isspace(code->data[code->len - 1]) &&
407
- isspace(code->data[code->len - 2]))) &&
282
+ if (info_len == 0 &&
283
+ (code_len > 2 &&
284
+ !isspace(code[0]) &&
285
+ !(isspace(code[code_len - 1]) &&
286
+ isspace(code[code_len - 2]))) &&
408
287
  !(node->prev == NULL && node->parent &&
409
288
  node->parent->type == CMARK_NODE_ITEM)) {
410
- lit(state, " ", false);
411
- cmark_strbuf_puts(state->prefix, " ");
412
- out(state, node->as.code.literal, false, LITERAL);
413
- cmark_strbuf_truncate(state->prefix,
414
- state->prefix->size - 4);
289
+ LIT(" ");
290
+ cmark_strbuf_puts(renderer->prefix, " ");
291
+ OUT(cmark_node_get_literal(node), false, LITERAL);
292
+ cmark_strbuf_truncate(renderer->prefix,
293
+ renderer->prefix->size - 4);
415
294
  } else {
416
295
  numticks = longest_backtick_sequence(code) + 1;
417
296
  if (numticks < 3) {
418
297
  numticks = 3;
419
298
  }
420
299
  for (i = 0; i < numticks; i++) {
421
- lit(state, "`", false);
300
+ LIT("`");
422
301
  }
423
- lit(state, " ", false);
424
- out(state, *info, false, LITERAL);
425
- cr(state);
426
- out(state, node->as.code.literal, false, LITERAL);
427
- cr(state);
302
+ LIT(" ");
303
+ OUT(info, false, LITERAL);
304
+ CR();
305
+ OUT(cmark_node_get_literal(node), false, LITERAL);
306
+ CR();
428
307
  for (i = 0; i < numticks; i++) {
429
- lit(state, "`", false);
308
+ LIT("`");
430
309
  }
431
310
  }
432
- blankline(state);
311
+ BLANKLINE();
433
312
  break;
434
313
 
435
314
  case CMARK_NODE_HTML:
436
- blankline(state);
437
- out(state, node->as.literal, false, LITERAL);
438
- blankline(state);
315
+ BLANKLINE();
316
+ OUT(cmark_node_get_literal(node), false, LITERAL);
317
+ BLANKLINE();
439
318
  break;
440
319
 
441
320
  case CMARK_NODE_HRULE:
442
- blankline(state);
443
- lit(state, "-----", false);
444
- blankline(state);
321
+ BLANKLINE();
322
+ LIT("-----");
323
+ BLANKLINE();
445
324
  break;
446
325
 
447
326
  case CMARK_NODE_PARAGRAPH:
448
327
  if (!entering) {
449
- blankline(state);
328
+ BLANKLINE();
450
329
  }
451
330
  break;
452
331
 
453
332
  case CMARK_NODE_TEXT:
454
- out(state, node->as.literal, true, NORMAL);
333
+ OUT(cmark_node_get_literal(node), true, NORMAL);
455
334
  break;
456
335
 
457
336
  case CMARK_NODE_LINEBREAK:
458
- if (!(CMARK_OPT_HARDBREAKS & state->options)) {
459
- lit(state, "\\", false);
337
+ if (!(CMARK_OPT_HARDBREAKS & options)) {
338
+ LIT("\\");
460
339
  }
461
- cr(state);
340
+ CR();
462
341
  break;
463
342
 
464
343
  case CMARK_NODE_SOFTBREAK:
465
- if (state->width == 0) {
466
- cr(state);
344
+ if (renderer->width == 0 &&
345
+ !(CMARK_OPT_HARDBREAKS & options)) {
346
+ CR();
467
347
  } else {
468
- lit(state, " ", true);
348
+ OUT(" ", true, LITERAL);
469
349
  }
470
350
  break;
471
351
 
472
352
  case CMARK_NODE_CODE:
473
- code = &node->as.literal;
353
+ code = cmark_node_get_literal(node);
354
+ code_len = safe_strlen(code);
474
355
  numticks = shortest_unused_backtick_sequence(code);
475
356
  for (i = 0; i < numticks; i++) {
476
- lit(state, "`", false);
357
+ LIT("`");
477
358
  }
478
- if (code->len == 0 || code->data[0] == '`') {
479
- lit(state, " ", false);
359
+ if (code_len == 0 || code[0] == '`') {
360
+ LIT(" ");
480
361
  }
481
- out(state, node->as.literal, true, LITERAL);
482
- if (code->len == 0 || code->data[code->len - 1] == '`') {
483
- lit(state, " ", false);
362
+ OUT(cmark_node_get_literal(node), true, LITERAL);
363
+ if (code_len == 0 || code[code_len - 1] == '`') {
364
+ LIT(" ");
484
365
  }
485
366
  for (i = 0; i < numticks; i++) {
486
- lit(state, "`", false);
367
+ LIT("`");
487
368
  }
488
369
  break;
489
370
 
490
371
  case CMARK_NODE_INLINE_HTML:
491
- out(state, node->as.literal, false, LITERAL);
372
+ OUT(cmark_node_get_literal(node), false, LITERAL);
492
373
  break;
493
374
 
494
375
  case CMARK_NODE_STRONG:
495
376
  if (entering) {
496
- lit(state, "**", false);
377
+ LIT("**");
497
378
  } else {
498
- lit(state, "**", false);
379
+ LIT("**");
499
380
  }
500
381
  break;
501
382
 
@@ -509,62 +390,56 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
509
390
  emph_delim = "*";
510
391
  }
511
392
  if (entering) {
512
- lit(state, emph_delim, false);
393
+ LIT(emph_delim);
513
394
  } else {
514
- lit(state, emph_delim, false);
395
+ LIT(emph_delim);
515
396
  }
516
397
  break;
517
398
 
518
399
  case CMARK_NODE_LINK:
519
400
  if (is_autolink(node)) {
520
401
  if (entering) {
521
- lit(state, "<", false);
402
+ LIT("<");
522
403
  if (strncmp(cmark_node_get_url(node),
523
404
  "mailto:", 7) == 0) {
524
- lit(state,
525
- (char *)cmark_node_get_url(node) + 7,
526
- false);
405
+ LIT((char *)cmark_node_get_url(node) + 7);
527
406
  } else {
528
- lit(state,
529
- (char *)cmark_node_get_url(node),
530
- false);
407
+ LIT((char *)cmark_node_get_url(node));
531
408
  }
532
- lit(state, ">", false);
409
+ LIT(">");
533
410
  // return signal to skip contents of node...
534
411
  return 0;
535
412
  }
536
413
  } else {
537
414
  if (entering) {
538
- lit(state, "[", false);
415
+ LIT("[");
539
416
  } else {
540
- lit(state, "](", false);
541
- out(state,
542
- cmark_chunk_literal(cmark_node_get_url(node)),
543
- false, URL);
544
- title = &node->as.link.title;
545
- if (title->len > 0) {
546
- lit(state, " \"", true);
547
- out(state, *title, false, TITLE);
548
- lit(state, "\"", false);
417
+ LIT("](");
418
+ OUT(cmark_node_get_url(node), false, URL);
419
+ title = cmark_node_get_title(node);
420
+ if (safe_strlen(title) > 0) {
421
+ LIT(" \"");
422
+ OUT(title, false, TITLE);
423
+ LIT("\"");
549
424
  }
550
- lit(state, ")", false);
425
+ LIT(")");
551
426
  }
552
427
  }
553
428
  break;
554
429
 
555
430
  case CMARK_NODE_IMAGE:
556
431
  if (entering) {
557
- lit(state, "![", false);
432
+ LIT("![");
558
433
  } else {
559
- lit(state, "](", false);
560
- out(state, cmark_chunk_literal(cmark_node_get_url(node)), false, URL);
561
- title = &node->as.link.title;
562
- if (title->len > 0) {
563
- lit(state, " \"", true);
564
- out(state, *title, false, TITLE);
565
- lit(state, "\"", false);
434
+ LIT("](");
435
+ OUT(cmark_node_get_url(node), false, URL);
436
+ title = cmark_node_get_title(node);
437
+ if (safe_strlen(title) > 0) {
438
+ OUT(" \"", true, LITERAL);
439
+ OUT(title, false, TITLE);
440
+ LIT("\"");
566
441
  }
567
- lit(state, ")", false);
442
+ LIT(")");
568
443
  }
569
444
  break;
570
445
 
@@ -578,32 +453,10 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
578
453
 
579
454
  char *cmark_render_commonmark(cmark_node *root, int options, int width)
580
455
  {
581
- char *result;
582
- cmark_strbuf commonmark = GH_BUF_INIT;
583
- cmark_strbuf prefix = GH_BUF_INIT;
584
- if (CMARK_OPT_HARDBREAKS & options) {
456
+ if (options & CMARK_OPT_HARDBREAKS) {
457
+ // disable breaking on width, since it has
458
+ // a different meaning with OPT_HARDBREAKS
585
459
  width = 0;
586
460
  }
587
- struct render_state state = {
588
- options, &commonmark, &prefix, 0, width,
589
- 0, 0, true, false, false
590
- };
591
- cmark_node *cur;
592
- cmark_event_type ev_type;
593
- cmark_iter *iter = cmark_iter_new(root);
594
-
595
- while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
596
- cur = cmark_iter_get_node(iter);
597
- if (!S_render_node(cur, ev_type, &state)) {
598
- // a false value causes us to skip processing
599
- // the node's contents. this is used for
600
- // autolinks.
601
- cmark_iter_reset(iter, cur, CMARK_EVENT_EXIT);
602
- }
603
- }
604
- result = (char *)cmark_strbuf_detach(&commonmark);
605
-
606
- cmark_strbuf_free(&prefix);
607
- cmark_iter_free(iter);
608
- return result;
461
+ return cmark_render(root, options, width, outc, S_render_node);
609
462
  }