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.
- checksums.yaml +4 -4
- data/README.md +4 -2
- data/Rakefile +0 -5
- data/ext/commonmarker/cmark/CMakeLists.txt +1 -1
- data/ext/commonmarker/cmark/Makefile +12 -7
- data/ext/commonmarker/cmark/README.md +12 -8
- data/ext/commonmarker/cmark/api_test/main.c +18 -2
- data/ext/commonmarker/cmark/benchmarks.md +4 -4
- data/ext/commonmarker/cmark/build/CMakeFiles/CMakeError.log +12 -12
- data/ext/commonmarker/cmark/build/CMakeFiles/CMakeOutput.log +106 -106
- data/ext/commonmarker/cmark/build/CMakeFiles/Makefile2 +7 -7
- data/ext/commonmarker/cmark/build/CMakeFiles/progress.marks +1 -1
- data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/build.make +1 -1
- data/ext/commonmarker/cmark/build/api_test/CMakeFiles/api_test.dir/link.txt +1 -1
- data/ext/commonmarker/cmark/build/api_test/CMakeFiles/progress.marks +1 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/DependInfo.cmake +2 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/build.make +61 -9
- data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/cmake_clean.cmake +2 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/link.txt +1 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/cmark.dir/progress.make +2 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/DependInfo.cmake +3 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/build.make +81 -29
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/cmake_clean.cmake +3 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/link.txt +1 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark.dir/progress.make +19 -17
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/C.includecache +56 -10
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/DependInfo.cmake +2 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/blocks.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/build.make +60 -8
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/cmake_clean.cmake +2 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/cmark.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/commonmark.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/depend.internal +27 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/depend.make +27 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/flags.make +1 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/html.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/inlines.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/latex.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/link.txt +1 -1
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/man.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/progress.make +19 -17
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/render.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/libcmark_static.dir/scanners.c.o +0 -0
- data/ext/commonmarker/cmark/build/src/CMakeFiles/progress.marks +1 -1
- data/ext/commonmarker/cmark/build/src/Makefile +66 -0
- data/ext/commonmarker/cmark/build/src/cmake_install.cmake +3 -3
- data/ext/commonmarker/cmark/build/src/cmark_version.h +2 -2
- data/ext/commonmarker/cmark/build/src/libcmark.a +0 -0
- data/ext/commonmarker/cmark/build/src/libcmark.pc +1 -1
- data/ext/commonmarker/cmark/changelog.txt +144 -0
- data/ext/commonmarker/cmark/man/make_man_page.py +3 -3
- data/ext/commonmarker/cmark/man/man1/cmark.1 +10 -2
- data/ext/commonmarker/cmark/man/man3/cmark.3 +106 -85
- data/ext/commonmarker/cmark/src/CMakeLists.txt +5 -2
- data/ext/commonmarker/cmark/src/blocks.c +76 -9
- data/ext/commonmarker/cmark/src/cmark.c +9 -2
- data/ext/commonmarker/cmark/src/cmark.h +16 -3
- data/ext/commonmarker/cmark/src/commonmark.c +162 -309
- data/ext/commonmarker/cmark/src/html.c +30 -10
- data/ext/commonmarker/cmark/src/inlines.c +80 -72
- data/ext/commonmarker/cmark/src/latex.c +430 -0
- data/ext/commonmarker/cmark/src/main.c +12 -4
- data/ext/commonmarker/cmark/src/man.c +118 -156
- data/ext/commonmarker/cmark/src/node.h +1 -0
- data/ext/commonmarker/cmark/src/render.c +186 -0
- data/ext/commonmarker/cmark/src/render.h +66 -0
- data/ext/commonmarker/cmark/src/scanners.c +14586 -8944
- data/ext/commonmarker/cmark/src/scanners.h +16 -2
- data/ext/commonmarker/cmark/src/scanners.re +93 -9
- data/ext/commonmarker/cmark/test/__pycache__/cmark.cpython-34.pyc +0 -0
- data/ext/commonmarker/cmark/test/__pycache__/normalize.cpython-34.pyc +0 -0
- data/ext/commonmarker/cmark/test/smart_punct.txt +74 -10
- data/ext/commonmarker/cmark/test/spec.txt +726 -92
- data/ext/commonmarker/cmark/test/spec_tests.py +16 -13
- data/lib/commonmarker/config.rb +2 -0
- data/lib/commonmarker/version.rb +1 -1
- data/test/test_helper.rb +1 -1
- data/test/test_spec.rb +11 -10
- metadata +9 -6
- data/ext/commonmarker/cmark/algorithm.md +0 -116
- data/ext/commonmarker/cmark/src/debug.h +0 -36
- 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
|
-
|
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
|
-
|
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
|
-
|
254
|
-
|
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,
|
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
|
-
|
272
|
-
|
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
|
-
|
442
|
-
|
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 ?
|
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 ?
|
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
|
+
}
|