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
@@ -2,12 +2,13 @@
2
2
  #include <stdio.h>
3
3
  #include <string.h>
4
4
  #include <assert.h>
5
-
5
+ #include "cmark_ctype.h"
6
6
  #include "config.h"
7
7
  #include "cmark.h"
8
8
  #include "node.h"
9
9
  #include "buffer.h"
10
10
  #include "houdini.h"
11
+ #include "scanners.h"
11
12
 
12
13
  // Functions to convert cmark_nodes to HTML strings.
13
14
 
@@ -156,7 +157,7 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
156
157
  } else {
157
158
  bufsize_t first_tag = 0;
158
159
  while (first_tag < node->as.code.info.len &&
159
- node->as.code.info.data[first_tag] != ' ') {
160
+ !cmark_isspace(node->as.code.info.data[first_tag])) {
160
161
  first_tag += 1;
161
162
  }
162
163
 
@@ -174,7 +175,13 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
174
175
 
175
176
  case CMARK_NODE_HTML:
176
177
  cr(html);
177
- cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
178
+ if (options & CMARK_OPT_SAFE) {
179
+ cmark_strbuf_puts(html, "<!-- raw HTML omitted -->");
180
+ } else {
181
+ cmark_strbuf_put(html, node->as.literal.data,
182
+ node->as.literal.len);
183
+ }
184
+ cr(html);
178
185
  break;
179
186
 
180
187
  case CMARK_NODE_HRULE:
@@ -228,7 +235,12 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
228
235
  break;
229
236
 
230
237
  case CMARK_NODE_INLINE_HTML:
231
- cmark_strbuf_put(html, node->as.literal.data, node->as.literal.len);
238
+ if (options & CMARK_OPT_SAFE) {
239
+ cmark_strbuf_puts(html, "<!-- raw HTML omitted -->");
240
+ } else {
241
+ cmark_strbuf_put(html, node->as.literal.data,
242
+ node->as.literal.len);
243
+ }
232
244
  break;
233
245
 
234
246
  case CMARK_NODE_STRONG:
@@ -250,15 +262,19 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
250
262
  case CMARK_NODE_LINK:
251
263
  if (entering) {
252
264
  cmark_strbuf_puts(html, "<a href=\"");
253
- houdini_escape_href(html, node->as.link.url.data,
254
- node->as.link.url.len);
265
+ if (!((options & CMARK_OPT_SAFE) &&
266
+ scan_dangerous_url(&node->as.link.url, 0))) {
267
+ houdini_escape_href(html,
268
+ node->as.link.url.data,
269
+ node->as.link.url.len);
255
270
 
271
+ }
256
272
  if (node->as.link.title.len) {
257
273
  cmark_strbuf_puts(html, "\" title=\"");
258
- escape_html(html, node->as.link.title.data,
274
+ escape_html(html,
275
+ node->as.link.title.data,
259
276
  node->as.link.title.len);
260
277
  }
261
-
262
278
  cmark_strbuf_puts(html, "\">");
263
279
  } else {
264
280
  cmark_strbuf_puts(html, "</a>");
@@ -268,9 +284,13 @@ S_render_node(cmark_node *node, cmark_event_type ev_type,
268
284
  case CMARK_NODE_IMAGE:
269
285
  if (entering) {
270
286
  cmark_strbuf_puts(html, "<img src=\"");
271
- houdini_escape_href(html, node->as.link.url.data,
272
- node->as.link.url.len);
287
+ if (!((options & CMARK_OPT_SAFE) &&
288
+ scan_dangerous_url(&node->as.link.url, 0))) {
289
+ houdini_escape_href(html,
290
+ node->as.link.url.data,
291
+ node->as.link.url.len);
273
292
 
293
+ }
274
294
  cmark_strbuf_puts(html, "\" alt=\"");
275
295
  state->plain = node;
276
296
  } else {
@@ -65,46 +65,6 @@ static void subject_from_buf(subject *e, cmark_strbuf *buffer,
65
65
  cmark_reference_map *refmap);
66
66
  static bufsize_t subject_find_special_char(subject *subj, int options);
67
67
 
68
- static cmark_chunk cmark_clean_autolink(cmark_chunk *url, int is_email)
69
- {
70
- cmark_strbuf buf = GH_BUF_INIT;
71
-
72
- cmark_chunk_trim(url);
73
-
74
- if (url->len == 0) {
75
- cmark_chunk result = CMARK_CHUNK_EMPTY;
76
- return result;
77
- }
78
-
79
- if (is_email)
80
- cmark_strbuf_puts(&buf, "mailto:");
81
-
82
- houdini_unescape_html_f(&buf, url->data, url->len);
83
- return cmark_chunk_buf_detach(&buf);
84
- }
85
-
86
- static inline cmark_node *make_link(cmark_node *label, cmark_chunk *url, cmark_chunk *title)
87
- {
88
- cmark_node* e = (cmark_node *)calloc(1, sizeof(*e));
89
- if(e != NULL) {
90
- e->type = CMARK_NODE_LINK;
91
- e->first_child = label;
92
- e->last_child = label;
93
- e->as.link.url = *url;
94
- e->as.link.title = *title;
95
- e->next = NULL;
96
- label->parent = e;
97
- }
98
- return e;
99
- }
100
-
101
- static inline cmark_node* make_autolink(cmark_node* label, cmark_chunk url, int is_email)
102
- {
103
- cmark_chunk clean_url = cmark_clean_autolink(&url, is_email);
104
- cmark_chunk title = CMARK_CHUNK_EMPTY;
105
- return make_link(label, &clean_url, &title);
106
- }
107
-
108
68
  // Create an inline with a literal string value.
109
69
  static inline cmark_node* make_literal(cmark_node_type t, cmark_chunk s)
110
70
  {
@@ -144,6 +104,18 @@ static inline cmark_node* make_simple(cmark_node_type t)
144
104
  return e;
145
105
  }
146
106
 
107
+ // Like make_str, but parses entities.
108
+ static cmark_node *make_str_with_entities(cmark_chunk *content)
109
+ {
110
+ cmark_strbuf unescaped = GH_BUF_INIT;
111
+
112
+ if (houdini_unescape_html(&unescaped, content->data, content->len)) {
113
+ return make_str(cmark_chunk_buf_detach(&unescaped));
114
+ } else {
115
+ return make_str(*content);
116
+ }
117
+ }
118
+
147
119
  // Duplicate a chunk by creating a copy of the buffer not by reusing the
148
120
  // buffer like cmark_chunk_dup does.
149
121
  static cmark_chunk chunk_clone(cmark_chunk *src)
@@ -160,6 +132,33 @@ static cmark_chunk chunk_clone(cmark_chunk *src)
160
132
  return c;
161
133
  }
162
134
 
135
+ static cmark_chunk cmark_clean_autolink(cmark_chunk *url, int is_email)
136
+ {
137
+ cmark_strbuf buf = GH_BUF_INIT;
138
+
139
+ cmark_chunk_trim(url);
140
+
141
+ if (url->len == 0) {
142
+ cmark_chunk result = CMARK_CHUNK_EMPTY;
143
+ return result;
144
+ }
145
+
146
+ if (is_email)
147
+ cmark_strbuf_puts(&buf, "mailto:");
148
+
149
+ houdini_unescape_html_f(&buf, url->data, url->len);
150
+ return cmark_chunk_buf_detach(&buf);
151
+ }
152
+
153
+ static inline cmark_node* make_autolink(cmark_chunk url, int is_email)
154
+ {
155
+ cmark_node *link = make_simple(CMARK_NODE_LINK);
156
+ link->as.link.url = cmark_clean_autolink(&url, is_email);
157
+ link->as.link.title = cmark_chunk_literal("");
158
+ cmark_node_append_child(link, make_str_with_entities(&url));
159
+ return link;
160
+ }
161
+
163
162
  static void subject_from_buf(subject *e, cmark_strbuf *buffer,
164
163
  cmark_reference_map *refmap)
165
164
  {
@@ -437,18 +436,45 @@ static cmark_node* handle_delim(subject* subj, unsigned char c, bool smart)
437
436
  // Assumes we have a hyphen at the current position.
438
437
  static cmark_node* handle_hyphen(subject* subj, bool smart)
439
438
  {
439
+ int startpos = subj->pos;
440
+
440
441
  advance(subj);
441
- if (smart && peek_char(subj) == '-') {
442
- advance(subj);
443
- if (peek_char(subj) == '-') {
444
- advance(subj);
445
- return make_str(cmark_chunk_literal(EMDASH));
446
- } else {
447
- return make_str(cmark_chunk_literal(ENDASH));
448
- }
449
- } else {
442
+
443
+ if (!smart || peek_char(subj) != '-') {
450
444
  return make_str(cmark_chunk_literal("-"));
451
445
  }
446
+
447
+ while (smart && peek_char(subj) == '-') {
448
+ advance(subj);
449
+ }
450
+
451
+ int numhyphens = subj->pos - startpos;
452
+ int en_count = 0;
453
+ int em_count = 0;
454
+ int i;
455
+ cmark_strbuf buf = GH_BUF_INIT;
456
+
457
+ if (numhyphens % 3 == 0) { // if divisible by 3, use all em dashes
458
+ em_count = numhyphens / 3;
459
+ } else if (numhyphens % 2 == 0) { // if divisible by 2, use all en dashes
460
+ en_count = numhyphens / 2;
461
+ } else if (numhyphens % 3 == 2) { // use one en dash at end
462
+ en_count = 1;
463
+ em_count = (numhyphens - 2) / 3;
464
+ } else { // use two en dashes at the end
465
+ en_count = 2;
466
+ em_count = (numhyphens - 4) / 3;
467
+ }
468
+
469
+ for (i = em_count; i > 0; i--) {
470
+ cmark_strbuf_puts(&buf, EMDASH);
471
+ }
472
+
473
+ for (i = en_count; i > 0; i--) {
474
+ cmark_strbuf_puts(&buf, ENDASH);
475
+ }
476
+
477
+ return make_str(cmark_chunk_buf_detach(&buf));
452
478
  }
453
479
 
454
480
  // Assumes we have a period at the current position.
@@ -593,7 +619,8 @@ S_insert_emph(subject *subj, delimiter *opener, delimiter *closer)
593
619
  // replace empty opener inline with emph
594
620
  cmark_chunk_free(&(opener_inl->as.literal));
595
621
  emph = opener_inl;
596
- emph->type = use_delims == 1 ? NODE_EMPH : NODE_STRONG;
622
+ emph->type = use_delims == 1 ?
623
+ CMARK_NODE_EMPH : CMARK_NODE_STRONG;
597
624
  // remove opener from list
598
625
  remove_delimiter(subj, opener);
599
626
  } else {
@@ -667,19 +694,6 @@ static cmark_node* handle_entity(subject* subj)
667
694
  return make_str(cmark_chunk_buf_detach(&ent));
668
695
  }
669
696
 
670
- // Like make_str, but parses entities.
671
- // Returns an inline sequence consisting of str and entity elements.
672
- static cmark_node *make_str_with_entities(cmark_chunk *content)
673
- {
674
- cmark_strbuf unescaped = GH_BUF_INIT;
675
-
676
- if (houdini_unescape_html(&unescaped, content->data, content->len)) {
677
- return make_str(cmark_chunk_buf_detach(&unescaped));
678
- } else {
679
- return make_str(*content);
680
- }
681
- }
682
-
683
697
  // Clean a URL: remove surrounding whitespace and surrounding <>,
684
698
  // and remove \ that escape punctuation.
685
699
  cmark_chunk cmark_clean_url(cmark_chunk *url)
@@ -744,10 +758,7 @@ static cmark_node* handle_pointy_brace(subject* subj)
744
758
  contents = cmark_chunk_dup(&subj->input, subj->pos, matchlen - 1);
745
759
  subj->pos += matchlen;
746
760
 
747
- return make_autolink(
748
- make_str_with_entities(&contents),
749
- contents, 0
750
- );
761
+ return make_autolink(contents, 0);
751
762
  }
752
763
 
753
764
  // next try to match an email autolink
@@ -756,10 +767,7 @@ static cmark_node* handle_pointy_brace(subject* subj)
756
767
  contents = cmark_chunk_dup(&subj->input, subj->pos, matchlen - 1);
757
768
  subj->pos += matchlen;
758
769
 
759
- return make_autolink(
760
- make_str_with_entities(&contents),
761
- contents, 1
762
- );
770
+ return make_autolink(contents, 1);
763
771
  }
764
772
 
765
773
  // finally, try to match an html tag
@@ -934,7 +942,7 @@ noMatch:
934
942
 
935
943
  match:
936
944
  inl = opener->inl_text;
937
- inl->type = is_image ? NODE_IMAGE : NODE_LINK;
945
+ inl->type = is_image ? CMARK_NODE_IMAGE : CMARK_NODE_LINK;
938
946
  cmark_chunk_free(&inl->as.literal);
939
947
  inl->first_child = link_text;
940
948
  process_emphasis(subj, opener);
@@ -0,0 +1,430 @@
1
+ #include <stdlib.h>
2
+ #include <stdio.h>
3
+ #include <string.h>
4
+ #include <assert.h>
5
+ #include <ctype.h>
6
+
7
+ #include "config.h"
8
+ #include "cmark.h"
9
+ #include "node.h"
10
+ #include "buffer.h"
11
+ #include "utf8.h"
12
+ #include "scanners.h"
13
+ #include "render.h"
14
+
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)
20
+
21
+ static inline void outc(cmark_renderer *renderer,
22
+ cmark_escaping escape,
23
+ int32_t c,
24
+ unsigned char nextc)
25
+ {
26
+ if (escape == LITERAL) {
27
+ cmark_render_code_point(renderer, c);
28
+ return;
29
+ }
30
+
31
+ switch(c) {
32
+ case 123: // '{'
33
+ case 125: // '}'
34
+ case 35: // '#'
35
+ case 37: // '%'
36
+ case 38: // '&'
37
+ cmark_render_ascii(renderer, "\\");
38
+ cmark_render_code_point(renderer, c);
39
+ break;
40
+ case 36: // '$'
41
+ case 95: // '_'
42
+ if (escape == NORMAL) {
43
+ cmark_render_ascii(renderer, "\\");
44
+ }
45
+ cmark_render_code_point(renderer, c);
46
+ break;
47
+ case 45 : // '-'
48
+ if (nextc == 45) { // prevent ligature
49
+ cmark_render_ascii(renderer, "\\-");
50
+ } else {
51
+ cmark_render_ascii(renderer, "-");
52
+ }
53
+ break;
54
+ case 126: // '~'
55
+ if (escape == NORMAL) {
56
+ cmark_render_ascii(renderer, "\\textasciitilde{}");
57
+ } else {
58
+ cmark_render_code_point(renderer, c);
59
+ }
60
+ break;
61
+ case 94: // '^'
62
+ cmark_render_ascii(renderer, "\\^{}");
63
+ break;
64
+ case 92: // '\\'
65
+ if (escape == URL) {
66
+ // / acts as path sep even on windows:
67
+ cmark_render_ascii(renderer, "/");
68
+ } else {
69
+ cmark_render_ascii(renderer, "\\textbackslash{}");
70
+ }
71
+ break;
72
+ case 124: // '|'
73
+ cmark_render_ascii(renderer, "\\textbar{}");
74
+ break;
75
+ case 60: // '<'
76
+ cmark_render_ascii(renderer, "\\textless{}");
77
+ break;
78
+ case 62: // '>'
79
+ cmark_render_ascii(renderer, "\\textgreater{}");
80
+ break;
81
+ case 91: // '['
82
+ case 93: // ']'
83
+ cmark_render_ascii(renderer, "{");
84
+ cmark_render_code_point(renderer, c);
85
+ cmark_render_ascii(renderer, "}");
86
+ break;
87
+ case 34: // '"'
88
+ cmark_render_ascii(renderer, "\\textquotedbl{}");
89
+ // requires \usepackage[T1]{fontenc}
90
+ break;
91
+ case 39: // '\''
92
+ cmark_render_ascii(renderer, "\\textquotesingle{}");
93
+ // requires \usepackage{textcomp}
94
+ break;
95
+ case 160: // nbsp
96
+ cmark_render_ascii(renderer, "~");
97
+ break;
98
+ case 8230: // hellip
99
+ cmark_render_ascii(renderer, "\\ldots{}");
100
+ break;
101
+ case 8216: // lsquo
102
+ if (escape == NORMAL) {
103
+ cmark_render_ascii(renderer, "`");
104
+ } else {
105
+ cmark_render_code_point(renderer, c);
106
+ }
107
+ break;
108
+ case 8217: // rsquo
109
+ if (escape == NORMAL) {
110
+ cmark_render_ascii(renderer, "\'");
111
+ } else {
112
+ cmark_render_code_point(renderer, c);
113
+ }
114
+ break;
115
+ case 8220: // ldquo
116
+ if (escape == NORMAL) {
117
+ cmark_render_ascii(renderer, "``");
118
+ } else {
119
+ cmark_render_code_point(renderer, c);
120
+ }
121
+ break;
122
+ case 8221: // rdquo
123
+ if (escape == NORMAL) {
124
+ cmark_render_ascii(renderer, "''");
125
+ } else {
126
+ cmark_render_code_point(renderer, c);
127
+ }
128
+ break;
129
+ case 8212: // emdash
130
+ if (escape == NORMAL) {
131
+ cmark_render_ascii(renderer, "---");
132
+ } else {
133
+ cmark_render_code_point(renderer, c);
134
+ }
135
+ break;
136
+ case 8211: // endash
137
+ if (escape == NORMAL) {
138
+ cmark_render_ascii(renderer, "--");
139
+ } else {
140
+ cmark_render_code_point(renderer, c);
141
+ }
142
+ break;
143
+ default:
144
+ cmark_render_code_point(renderer, c);
145
+ }
146
+ }
147
+
148
+ typedef enum {
149
+ NO_LINK,
150
+ URL_AUTOLINK,
151
+ EMAIL_AUTOLINK,
152
+ NORMAL_LINK
153
+ } link_type;
154
+
155
+ static link_type
156
+ get_link_type(cmark_node *node)
157
+ {
158
+ size_t title_len, url_len;
159
+ cmark_node *link_text;
160
+ char *realurl;
161
+ int realurllen;
162
+ bool isemail = false;
163
+
164
+ if (node->type != CMARK_NODE_LINK) {
165
+ return NO_LINK;
166
+ }
167
+
168
+ const char* url = cmark_node_get_url(node);
169
+ cmark_chunk url_chunk = cmark_chunk_literal(url);
170
+
171
+ url_len = safe_strlen(url);
172
+ if (url_len == 0 || scan_scheme(&url_chunk, 0) == 0) {
173
+ return NO_LINK;
174
+ }
175
+
176
+ const char* title = cmark_node_get_title(node);
177
+ title_len = safe_strlen(title);
178
+ // if it has a title, we can't treat it as an autolink:
179
+ if (title_len > 0) {
180
+ return NORMAL_LINK;
181
+ }
182
+
183
+ link_text = node->first_child;
184
+ cmark_consolidate_text_nodes(link_text);
185
+ realurl = (char*)url;
186
+ realurllen = url_len;
187
+ if (strncmp(realurl, "mailto:", 7) == 0) {
188
+ realurl += 7;
189
+ realurllen -= 7;
190
+ isemail = true;
191
+ }
192
+ if (realurllen == link_text->as.literal.len &&
193
+ strncmp(realurl,
194
+ (char*)link_text->as.literal.data,
195
+ link_text->as.literal.len) == 0) {
196
+ if (isemail) {
197
+ return EMAIL_AUTOLINK;
198
+ } else {
199
+ return URL_AUTOLINK;
200
+ }
201
+ } else {
202
+ return NORMAL_LINK;
203
+ }
204
+ }
205
+
206
+ static int
207
+ S_get_enumlevel(cmark_node *node)
208
+ {
209
+ int enumlevel = 0;
210
+ cmark_node *tmp = node;
211
+ while (tmp) {
212
+ if (tmp->type == CMARK_NODE_LIST &&
213
+ cmark_node_get_list_type(node) == CMARK_ORDERED_LIST) {
214
+ enumlevel++;
215
+ }
216
+ tmp = tmp->parent;
217
+ }
218
+ return enumlevel;
219
+ }
220
+
221
+ static int
222
+ S_render_node(cmark_renderer *renderer,
223
+ cmark_node *node,
224
+ cmark_event_type ev_type,
225
+ int options)
226
+ {
227
+ int list_number;
228
+ char list_number_string[20];
229
+ bool entering = (ev_type == CMARK_EVENT_ENTER);
230
+ cmark_list_type list_type;
231
+ const char* roman_numerals[] = { "", "i", "ii", "iii", "iv", "v",
232
+ "vi", "vii", "viii", "ix", "x"
233
+ };
234
+
235
+ // avoid warning about unused parameter:
236
+ (void)(options);
237
+
238
+ switch (node->type) {
239
+ case CMARK_NODE_DOCUMENT:
240
+ break;
241
+
242
+ case CMARK_NODE_BLOCK_QUOTE:
243
+ if (entering) {
244
+ LIT("\\begin{quote}");
245
+ CR();
246
+ } else {
247
+ LIT("\\end{quote}");
248
+ BLANKLINE();
249
+ }
250
+ break;
251
+
252
+ case CMARK_NODE_LIST:
253
+ list_type = cmark_node_get_list_type(node);
254
+ if (entering) {
255
+ LIT("\\begin{");
256
+ LIT(list_type == CMARK_ORDERED_LIST ?
257
+ "enumerate" : "itemize");
258
+ LIT("}");
259
+ CR();
260
+ list_number = cmark_node_get_list_start(node);
261
+ if (list_number > 1) {
262
+ sprintf(list_number_string,
263
+ "%d", list_number);
264
+ LIT("\\setcounter{enum");
265
+ LIT((char *)roman_numerals[S_get_enumlevel(node)]);
266
+ LIT("}{");
267
+ OUT(list_number_string, false, NORMAL);
268
+ LIT("}");
269
+ CR();
270
+ }
271
+ } else {
272
+ LIT("\\end{");
273
+ LIT(list_type == CMARK_ORDERED_LIST ?
274
+ "enumerate" : "itemize");
275
+ LIT("}");
276
+ BLANKLINE();
277
+ }
278
+ break;
279
+
280
+ case CMARK_NODE_ITEM:
281
+ if (entering) {
282
+ LIT("\\item ");
283
+ } else {
284
+ CR();
285
+ }
286
+ break;
287
+
288
+ case CMARK_NODE_HEADER:
289
+ if (entering) {
290
+ switch (cmark_node_get_header_level(node)) {
291
+ case 1:
292
+ LIT("\\section");
293
+ break;
294
+ case 2:
295
+ LIT("\\subsection");
296
+ break;
297
+ case 3:
298
+ LIT("\\subsubsection");
299
+ break;
300
+ case 4:
301
+ LIT("\\paragraph");
302
+ break;
303
+ case 5:
304
+ LIT("\\subparagraph");
305
+ break;
306
+ }
307
+ LIT("{");
308
+ } else {
309
+ LIT("}");
310
+ BLANKLINE();
311
+ }
312
+ break;
313
+
314
+ case CMARK_NODE_CODE_BLOCK:
315
+ CR();
316
+ LIT("\\begin{verbatim}");
317
+ CR();
318
+ OUT(cmark_node_get_literal(node), false, LITERAL);
319
+ CR();
320
+ LIT("\\end{verbatim}");
321
+ BLANKLINE();
322
+ break;
323
+
324
+ case CMARK_NODE_HTML:
325
+ break;
326
+
327
+ case CMARK_NODE_HRULE:
328
+ BLANKLINE();
329
+ LIT("\\begin{center}\\rule{0.5\\linewidth}{\\linethickness}\\end{center}");
330
+ BLANKLINE();
331
+ break;
332
+
333
+ case CMARK_NODE_PARAGRAPH:
334
+ if (!entering) {
335
+ BLANKLINE();
336
+ }
337
+ break;
338
+
339
+ case CMARK_NODE_TEXT:
340
+ OUT(cmark_node_get_literal(node), true, NORMAL);
341
+ break;
342
+
343
+ case CMARK_NODE_LINEBREAK:
344
+ LIT("\\\\");
345
+ CR();
346
+ break;
347
+
348
+ case CMARK_NODE_SOFTBREAK:
349
+ if (renderer->width == 0) {
350
+ CR();
351
+ } else {
352
+ OUT(" ", true, NORMAL);
353
+ }
354
+ break;
355
+
356
+ case CMARK_NODE_CODE:
357
+ LIT("\\texttt{");
358
+ OUT(cmark_node_get_literal(node), false, NORMAL);
359
+ LIT("}");
360
+ break;
361
+
362
+ case CMARK_NODE_INLINE_HTML:
363
+ break;
364
+
365
+ case CMARK_NODE_STRONG:
366
+ if (entering) {
367
+ LIT("\\textbf{");
368
+ } else {
369
+ LIT("}");
370
+ }
371
+ break;
372
+
373
+ case CMARK_NODE_EMPH:
374
+ if (entering) {
375
+ LIT("\\emph{");
376
+ } else {
377
+ LIT("}");
378
+ }
379
+ break;
380
+
381
+ case CMARK_NODE_LINK:
382
+ if (entering) {
383
+ const char* url = cmark_node_get_url(node);
384
+ // requires \usepackage{hyperref}
385
+ switch(get_link_type(node)) {
386
+ case URL_AUTOLINK:
387
+ LIT("\\url{");
388
+ OUT(url, false, URL);
389
+ break;
390
+ case EMAIL_AUTOLINK:
391
+ LIT("\\href{");
392
+ OUT(url, false, URL);
393
+ LIT("}\\nolinkurl{");
394
+ break;
395
+ case NORMAL_LINK:
396
+ LIT("\\href{");
397
+ OUT(url, false, URL);
398
+ LIT("}{");
399
+ break;
400
+ case NO_LINK:
401
+ LIT("{"); // error?
402
+ }
403
+ } else {
404
+ LIT("}");
405
+ }
406
+
407
+ break;
408
+
409
+ case CMARK_NODE_IMAGE:
410
+ if (entering) {
411
+ LIT("\\protect\\includegraphics{");
412
+ // requires \include{graphicx}
413
+ OUT(cmark_node_get_url(node), false, URL);
414
+ LIT("}");
415
+ return 0;
416
+ }
417
+ break;
418
+
419
+ default:
420
+ assert(false);
421
+ break;
422
+ }
423
+
424
+ return 1;
425
+ }
426
+
427
+ char *cmark_render_latex(cmark_node *root, int options, int width)
428
+ {
429
+ return cmark_render(root, options, width, outc, S_render_node);
430
+ }