redcarpet 1.5.3 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of redcarpet might be problematic. Click here for more details.
- data/ext/markdown.c +47 -2
- data/ext/markdown.h +7 -0
- data/ext/redcarpet.c +3 -4
- data/ext/xhtml.c +94 -139
- data/ext/xhtml.h +0 -1
- data/lib/redcarpet.rb +4 -4
- data/redcarpet.gemspec +1 -1
- data/test/redcarpet_test.rb +8 -1
- metadata +4 -4
data/ext/markdown.c
CHANGED
@@ -100,8 +100,27 @@ static struct html_tag block_tags[] = {
|
|
100
100
|
#define DEL_TAG (block_tags + 10)
|
101
101
|
|
102
102
|
/***************************
|
103
|
-
*
|
103
|
+
* HELPER FUNCTIONS *
|
104
104
|
***************************/
|
105
|
+
int
|
106
|
+
is_safe_link(const char *link, size_t link_len)
|
107
|
+
{
|
108
|
+
static const size_t valid_uris_count = 4;
|
109
|
+
static const char *valid_uris[] = {
|
110
|
+
"http://", "https://", "ftp://", "mailto://"
|
111
|
+
};
|
112
|
+
|
113
|
+
size_t i;
|
114
|
+
|
115
|
+
for (i = 0; i < valid_uris_count; ++i) {
|
116
|
+
size_t len = strlen(valid_uris[i]);
|
117
|
+
|
118
|
+
if (link_len > len && memcmp(link, valid_uris[i], len) == 0)
|
119
|
+
return 1;
|
120
|
+
}
|
121
|
+
|
122
|
+
return 0;
|
123
|
+
}
|
105
124
|
|
106
125
|
/* cmp_link_ref • comparison function for link_ref sorted arrays */
|
107
126
|
static int
|
@@ -245,8 +264,9 @@ parse_inline(struct buf *ob, struct render *rndr, char *data, size_t size)
|
|
245
264
|
|
246
265
|
while (i < size) {
|
247
266
|
/* copying inactive chars into the output */
|
248
|
-
while (end < size && (action = rndr->active_char[(unsigned char)data[end]]) == 0)
|
267
|
+
while (end < size && (action = rndr->active_char[(unsigned char)data[end]]) == 0) {
|
249
268
|
end++;
|
269
|
+
}
|
250
270
|
|
251
271
|
if (rndr->make.normal_text) {
|
252
272
|
work.data = data + i;
|
@@ -616,6 +636,25 @@ char_langle_tag(struct buf *ob, struct render *rndr, char *data, size_t offset,
|
|
616
636
|
else return end;
|
617
637
|
}
|
618
638
|
|
639
|
+
static size_t
|
640
|
+
char_autolink(struct buf *ob, struct render *rndr, char *data, size_t offset, size_t size)
|
641
|
+
{
|
642
|
+
struct buf work = { data, 0, 0, 0, 0 };
|
643
|
+
|
644
|
+
if (offset > 0 && !isspace(data[-1]))
|
645
|
+
return 0;
|
646
|
+
|
647
|
+
if (!is_safe_link(data, size))
|
648
|
+
return 0;
|
649
|
+
|
650
|
+
while (work.size < size && !isspace(data[work.size]))
|
651
|
+
work.size++;
|
652
|
+
|
653
|
+
if (rndr->make.autolink)
|
654
|
+
rndr->make.autolink(ob, &work, MKDA_NORMAL, rndr->make.opaque);
|
655
|
+
|
656
|
+
return work.size;
|
657
|
+
}
|
619
658
|
|
620
659
|
/* char_link • '[': parsing a link or an image */
|
621
660
|
static size_t
|
@@ -2029,6 +2068,12 @@ markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndrer, unsi
|
|
2029
2068
|
rndr.active_char['\\'] = char_escape;
|
2030
2069
|
rndr.active_char['&'] = char_entity;
|
2031
2070
|
|
2071
|
+
if (extensions & MKDEXT_AUTOLINK) {
|
2072
|
+
rndr.active_char['h'] = char_autolink; // http, https
|
2073
|
+
rndr.active_char['f'] = char_autolink; // ftp
|
2074
|
+
rndr.active_char['m'] = char_autolink; // mailto
|
2075
|
+
}
|
2076
|
+
|
2032
2077
|
/* Extension data */
|
2033
2078
|
rndr.ext_flags = extensions;
|
2034
2079
|
rndr.max_nesting = 16;
|
data/ext/markdown.h
CHANGED
@@ -40,6 +40,7 @@ enum mkd_extensions {
|
|
40
40
|
MKDEXT_LAX_EMPHASIS = (1 << 0),
|
41
41
|
MKDEXT_TABLES = (1 << 1),
|
42
42
|
MKDEXT_FENCED_CODE = (1 << 2),
|
43
|
+
MKDEXT_AUTOLINK = (1 << 3),
|
43
44
|
};
|
44
45
|
|
45
46
|
/* mkd_renderer • functions for rendering parsed data */
|
@@ -94,6 +95,12 @@ struct mkd_renderer {
|
|
94
95
|
#define MKD_TABLE_ALIGN_R (1 << 1)
|
95
96
|
#define MKD_TABLE_ALIGN_CENTER (MKD_TABLE_ALIGN_L | MKD_TABLE_ALIGN_R)
|
96
97
|
|
98
|
+
/*******************
|
99
|
+
* Auxiliar methods
|
100
|
+
*******************/
|
101
|
+
int
|
102
|
+
is_safe_link(const char *link, size_t link_len);
|
103
|
+
|
97
104
|
/**********************
|
98
105
|
* EXPORTED FUNCTIONS *
|
99
106
|
**********************/
|
data/ext/redcarpet.c
CHANGED
@@ -39,10 +39,6 @@ static void rb_redcarpet__get_flags(VALUE ruby_obj,
|
|
39
39
|
if (rb_funcall(ruby_obj, rb_intern("filter_styles"), 0) == Qtrue)
|
40
40
|
render_flags |= XHTML_SKIP_STYLE;
|
41
41
|
|
42
|
-
/* autolink */
|
43
|
-
if (rb_funcall(ruby_obj, rb_intern("autolink"), 0) == Qtrue)
|
44
|
-
render_flags |= XHTML_AUTOLINK;
|
45
|
-
|
46
42
|
/* safelink */
|
47
43
|
if (rb_funcall(ruby_obj, rb_intern("safelink"), 0) == Qtrue)
|
48
44
|
render_flags |= XHTML_SAFELINK;
|
@@ -62,6 +58,9 @@ static void rb_redcarpet__get_flags(VALUE ruby_obj,
|
|
62
58
|
if (rb_funcall(ruby_obj, rb_intern("fenced_code"), 0) == Qtrue)
|
63
59
|
extensions |= MKDEXT_FENCED_CODE;
|
64
60
|
|
61
|
+
if (rb_funcall(ruby_obj, rb_intern("autolink"), 0) == Qtrue)
|
62
|
+
extensions |= MKDEXT_AUTOLINK;
|
63
|
+
|
65
64
|
if (rb_funcall(ruby_obj, rb_intern("strikethrough"), 0) == Qtrue)
|
66
65
|
render_flags |= XHTML_STRIKETHROUGH;
|
67
66
|
|
data/ext/xhtml.c
CHANGED
@@ -31,26 +31,6 @@ struct xhtml_renderopt {
|
|
31
31
|
unsigned int flags;
|
32
32
|
};
|
33
33
|
|
34
|
-
static int
|
35
|
-
is_safe_link(const char *link, size_t link_len)
|
36
|
-
{
|
37
|
-
static const size_t valid_uris_count = 4;
|
38
|
-
static const char *valid_uris[] = {
|
39
|
-
"http:", "https:", "ftp:", "mailto:"
|
40
|
-
};
|
41
|
-
|
42
|
-
size_t i;
|
43
|
-
|
44
|
-
for (i = 0; i < valid_uris_count; ++i) {
|
45
|
-
size_t len = strlen(valid_uris[i]);
|
46
|
-
|
47
|
-
if (link_len > len && memcmp(link, valid_uris[i], len) == 0)
|
48
|
-
return 1;
|
49
|
-
}
|
50
|
-
|
51
|
-
return 0;
|
52
|
-
}
|
53
|
-
|
54
34
|
static inline int
|
55
35
|
put_scaped_char(struct buf *ob, char c)
|
56
36
|
{
|
@@ -103,20 +83,6 @@ is_html_tag(struct buf *tag, const char *tagname)
|
|
103
83
|
/********************
|
104
84
|
* GENERIC RENDERER *
|
105
85
|
********************/
|
106
|
-
|
107
|
-
static void
|
108
|
-
rndr_autolink2(struct buf *ob, const char *link, size_t link_size, enum mkd_autolink type)
|
109
|
-
{
|
110
|
-
BUFPUTSL(ob, "<a href=\"");
|
111
|
-
if (type == MKDA_IMPLICIT_EMAIL) BUFPUTSL(ob, "mailto:");
|
112
|
-
lus_attr_escape(ob, link, link_size);
|
113
|
-
BUFPUTSL(ob, "\">");
|
114
|
-
if (type == MKDA_EXPLICIT_EMAIL && link_size > 7)
|
115
|
-
lus_attr_escape(ob, link + 7, link_size - 7);
|
116
|
-
else lus_attr_escape(ob, link, link_size);
|
117
|
-
BUFPUTSL(ob, "</a>");
|
118
|
-
}
|
119
|
-
|
120
86
|
static int
|
121
87
|
rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *opaque)
|
122
88
|
{
|
@@ -128,7 +94,19 @@ rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, void *op
|
|
128
94
|
if ((options->flags & XHTML_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
|
129
95
|
return 0;
|
130
96
|
|
131
|
-
|
97
|
+
BUFPUTSL(ob, "<a href=\"");
|
98
|
+
if (type == MKDA_IMPLICIT_EMAIL)
|
99
|
+
BUFPUTSL(ob, "mailto:");
|
100
|
+
bufput(ob, link->data, link->size);
|
101
|
+
BUFPUTSL(ob, "\">");
|
102
|
+
|
103
|
+
if (type == MKDA_EXPLICIT_EMAIL && link->size > 7)
|
104
|
+
lus_attr_escape(ob, link->data + 7, link->size - 7);
|
105
|
+
else
|
106
|
+
lus_attr_escape(ob, link->data, link->size);
|
107
|
+
|
108
|
+
BUFPUTSL(ob, "</a>");
|
109
|
+
|
132
110
|
return 1;
|
133
111
|
}
|
134
112
|
|
@@ -492,140 +470,114 @@ smartypants_quotes(struct buf *ob, struct buf *text, size_t i, int is_open)
|
|
492
470
|
|
493
471
|
static void
|
494
472
|
rndr_normal_text(struct buf *ob, struct buf *text, void *opaque)
|
473
|
+
{
|
474
|
+
if (text)
|
475
|
+
lus_attr_escape(ob, text->data, text->size);
|
476
|
+
}
|
477
|
+
|
478
|
+
static void
|
479
|
+
rndr_smartypants(struct buf *ob, struct buf *text, void *opaque)
|
495
480
|
{
|
496
481
|
size_t i;
|
497
482
|
int open_single = 0, open_double = 0, open_tag = 0;
|
498
|
-
struct xhtml_renderopt *options = opaque;
|
499
|
-
|
500
|
-
int autolink = (options->flags & XHTML_AUTOLINK);
|
501
|
-
int smartypants = (options->flags & XHTML_SMARTYPANTS);
|
502
483
|
|
503
484
|
if (!text)
|
504
485
|
return;
|
505
486
|
|
506
|
-
if (!autolink && !smartypants) {
|
507
|
-
lus_attr_escape(ob, text->data, text->size);
|
508
|
-
return;
|
509
|
-
}
|
510
|
-
|
511
487
|
for (i = 0; i < text->size; ++i) {
|
512
488
|
size_t sub;
|
513
489
|
char c = text->data[i];
|
514
490
|
|
515
|
-
|
516
|
-
|
517
|
-
|
518
|
-
if (autolink) {
|
519
|
-
/* Autolinking is not standarized in the Markdown spec.
|
520
|
-
* We only check for links immediately after a space */
|
521
|
-
if ((i == 0 || isspace(text->data[i - 1])) &&
|
522
|
-
is_safe_link(text->data + i, text->size - i)) {
|
523
|
-
size_t j = i;
|
524
|
-
|
525
|
-
while (j < text->size && !isspace(text->data[j]))
|
526
|
-
j++;
|
527
|
-
|
528
|
-
rndr_autolink2(ob, &text->data[i], j - i, MKDA_NORMAL);
|
529
|
-
i = j - 1;
|
530
|
-
continue;
|
531
|
-
}
|
532
|
-
}
|
533
|
-
|
534
|
-
/*
|
535
|
-
* Smartypants subsitutions
|
536
|
-
*/
|
537
|
-
if (smartypants) {
|
538
|
-
for (sub = 0; sub < SUBS_COUNT; ++sub) {
|
539
|
-
if (c == smartypants_subs[sub].c0 &&
|
540
|
-
smartypants_cmpsub(text, i, smartypants_subs[sub].pattern)) {
|
491
|
+
for (sub = 0; sub < SUBS_COUNT; ++sub) {
|
492
|
+
if (c == smartypants_subs[sub].c0 &&
|
493
|
+
smartypants_cmpsub(text, i, smartypants_subs[sub].pattern)) {
|
541
494
|
|
542
|
-
|
543
|
-
|
495
|
+
if (smartypants_subs[sub].entity)
|
496
|
+
bufputs(ob, smartypants_subs[sub].entity);
|
544
497
|
|
545
|
-
|
546
|
-
|
547
|
-
}
|
498
|
+
i += smartypants_subs[sub].skip;
|
499
|
+
break;
|
548
500
|
}
|
501
|
+
}
|
549
502
|
|
550
|
-
|
551
|
-
|
503
|
+
if (sub < SUBS_COUNT)
|
504
|
+
continue;
|
552
505
|
|
553
|
-
|
554
|
-
|
555
|
-
|
556
|
-
|
506
|
+
switch (c) {
|
507
|
+
case '<':
|
508
|
+
open_tag = 1;
|
509
|
+
break;
|
557
510
|
|
558
|
-
|
559
|
-
|
560
|
-
|
511
|
+
case '>':
|
512
|
+
open_tag = 0;
|
513
|
+
break;
|
561
514
|
|
562
515
|
#if 0
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
579
|
-
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
594
|
-
|
595
|
-
|
596
|
-
|
597
|
-
|
598
|
-
}
|
516
|
+
/*
|
517
|
+
* FIXME: this is bongos.
|
518
|
+
*
|
519
|
+
* The markdown spec defines that code blocks can be delimited
|
520
|
+
* by more than one backtick, e.g.
|
521
|
+
*
|
522
|
+
* ``There is a literal backtick (`) here.``
|
523
|
+
* <p><code>There is a literal backtick (`) here.</code></p>
|
524
|
+
*
|
525
|
+
* Obviously, there's no way to differentiate between the start
|
526
|
+
* of a code block and the start of a quoted string for smartypants
|
527
|
+
*
|
528
|
+
* Look at this piece of Python code:
|
529
|
+
*
|
530
|
+
* ``result = ''.join(['this', 'is', 'bongos'])``
|
531
|
+
*
|
532
|
+
* This MD expression is clearly ambiguous since it can be parsed as:
|
533
|
+
*
|
534
|
+
* <p>“result = ”.join ...</p>
|
535
|
+
*
|
536
|
+
* Or also as:
|
537
|
+
*
|
538
|
+
* <p><code>result = ''.join(['this', 'is', 'bongos'])</code></p>
|
539
|
+
*
|
540
|
+
* Fuck everything about this. This is temporarily disabled, because at GitHub
|
541
|
+
* it's probably smarter to prioritize code blocks than pretty cutesy punctuation.
|
542
|
+
*
|
543
|
+
* The equivalent closing tag for the (``), ('') has also been disabled, because
|
544
|
+
* it makes no sense to have closing tags without opening tags.
|
545
|
+
*/
|
546
|
+
case '`':
|
547
|
+
if (open_tag == 0) {
|
548
|
+
if (i + 1 < text->size && text->data[i + 1] == '`') {
|
549
|
+
BUFPUTSL(ob, "“"); i++;
|
550
|
+
continue;
|
599
551
|
}
|
600
|
-
|
552
|
+
}
|
553
|
+
break;
|
601
554
|
#endif
|
602
555
|
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
607
|
-
|
608
|
-
}
|
556
|
+
case '\"':
|
557
|
+
if (open_tag == 0) {
|
558
|
+
if (smartypants_quotes(ob, text, i, open_double)) {
|
559
|
+
open_double = !open_double;
|
560
|
+
continue;
|
609
561
|
}
|
610
|
-
|
562
|
+
}
|
563
|
+
break;
|
611
564
|
|
612
|
-
|
613
|
-
|
565
|
+
case '\'':
|
566
|
+
if (open_tag == 0) {
|
614
567
|
|
615
568
|
#if 0 /* temporarily disabled, see previous comment */
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
569
|
+
if (i + 1 < text->size && text->data[i + 1] == '\'') {
|
570
|
+
BUFPUTSL(ob, "”"); i++;
|
571
|
+
continue;
|
572
|
+
}
|
620
573
|
#endif
|
621
574
|
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
}
|
575
|
+
if (smartypants_quotes(ob, text, i, open_single)) {
|
576
|
+
open_single = !open_single;
|
577
|
+
continue;
|
626
578
|
}
|
627
|
-
break;
|
628
579
|
}
|
580
|
+
break;
|
629
581
|
}
|
630
582
|
|
631
583
|
/*
|
@@ -769,6 +721,9 @@ init_xhtml_renderer(struct mkd_renderer *renderer, unsigned int render_flags)
|
|
769
721
|
renderer->link = NULL;
|
770
722
|
renderer->autolink = NULL;
|
771
723
|
}
|
724
|
+
|
725
|
+
if (render_flags & XHTML_SMARTYPANTS)
|
726
|
+
renderer->normal_text = rndr_smartypants;
|
772
727
|
}
|
773
728
|
|
774
729
|
void
|
data/ext/xhtml.h
CHANGED
data/lib/redcarpet.rb
CHANGED
@@ -26,7 +26,7 @@
|
|
26
26
|
# end
|
27
27
|
#
|
28
28
|
class Redcarpet
|
29
|
-
VERSION = '1.
|
29
|
+
VERSION = '1.6.0'
|
30
30
|
|
31
31
|
# Original Markdown formatted text.
|
32
32
|
attr_reader :text
|
@@ -49,15 +49,15 @@ class Redcarpet
|
|
49
49
|
# Disable superscript and relaxed emphasis processing.
|
50
50
|
attr_accessor :strict
|
51
51
|
|
52
|
-
# Convert URL in links, even if they aren't encased in <tt><></tt>
|
53
|
-
attr_accessor :autolink
|
54
|
-
|
55
52
|
# Don't make hyperlinks from <tt>[][]</tt> links that have unknown URL types.
|
56
53
|
attr_accessor :safelink
|
57
54
|
|
58
55
|
# Add TOC anchors to every header
|
59
56
|
attr_accessor :generate_toc
|
60
57
|
|
58
|
+
# Enable the Autolinking extension
|
59
|
+
attr_accessor :autolink
|
60
|
+
|
61
61
|
# Enable PHP-Markdown tables extension
|
62
62
|
attr_accessor :tables
|
63
63
|
|
data/redcarpet.gemspec
CHANGED
data/test/redcarpet_test.rb
CHANGED
@@ -199,8 +199,15 @@ EOS
|
|
199
199
|
end
|
200
200
|
|
201
201
|
def test_that_headers_are_linkable
|
202
|
-
markdown =
|
202
|
+
markdown = Redcarpet.new('### Hello [GitHub](http://github.com)')
|
203
203
|
assert_equal "<h3>Hello <a href=\"http://github.com\">GitHub</a></h3>", markdown.to_html.strip
|
204
204
|
end
|
205
|
+
|
206
|
+
def test_autolinking_with_ent_chars
|
207
|
+
markdown = Redcarpet.new(<<text, :autolink)
|
208
|
+
This a stupid link: https://github.com/rtomayko/tilt/issues?milestone=1&state=open
|
209
|
+
text
|
210
|
+
assert_equal "<p>This a stupid link: <a href=\"https://github.com/rtomayko/tilt/issues?milestone=1&state=open\">https://github.com/rtomayko/tilt/issues?milestone=1&state=open</a></p>\n", markdown.to_html
|
211
|
+
end
|
205
212
|
|
206
213
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redcarpet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 15
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 6
|
9
|
+
- 0
|
10
|
+
version: 1.6.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- "Natacha Port\xC3\xA9"
|