redcarpet 1.17.2 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +294 -37
- data/Rakefile +13 -15
- data/bin/redcarpet +1 -1
- data/ext/redcarpet/autolink.c +18 -19
- data/ext/redcarpet/autolink.h +7 -4
- data/ext/redcarpet/buffer.c +135 -229
- data/ext/redcarpet/buffer.h +47 -113
- data/ext/redcarpet/houdini.h +29 -0
- data/ext/redcarpet/houdini_href_e.c +108 -0
- data/ext/redcarpet/houdini_html_e.c +84 -0
- data/ext/redcarpet/html.c +181 -237
- data/ext/redcarpet/html.h +28 -14
- data/ext/redcarpet/html_blocks.h +206 -0
- data/ext/redcarpet/html_smartypants.c +99 -49
- data/ext/redcarpet/markdown.c +712 -493
- data/ext/redcarpet/markdown.h +48 -41
- data/ext/redcarpet/rc_markdown.c +137 -0
- data/ext/redcarpet/rc_render.c +434 -0
- data/ext/redcarpet/redcarpet.h +33 -0
- data/ext/redcarpet/stack.c +81 -0
- data/ext/redcarpet/stack.h +21 -0
- data/lib/redcarpet/render_man.rb +65 -0
- data/lib/redcarpet.rb +62 -101
- data/redcarpet.gemspec +18 -13
- data/test/redcarpet_test.rb +209 -122
- metadata +33 -20
- data/ext/redcarpet/array.c +0 -300
- data/ext/redcarpet/array.h +0 -147
- data/ext/redcarpet/redcarpet.c +0 -161
- data/lib/markdown.rb +0 -1
- data/test/benchmark.rb +0 -56
- data/test/benchmark.txt +0 -306
- data/test/markdown_test.rb +0 -186
data/README.markdown
CHANGED
|
@@ -1,61 +1,318 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
Redcarpet 2 is written with sugar, spice and everything nice
|
|
2
|
+
============================================================
|
|
3
3
|
|
|
4
|
-
Redcarpet is
|
|
5
|
-
|
|
4
|
+
Redcarpet is Ruby library for Markdown processing that smells like
|
|
5
|
+
butterflies and popcorn.
|
|
6
6
|
|
|
7
|
-
Redcarpet
|
|
7
|
+
Redcarpet used to be a drop-in replacement for Redcloth. This is no longer the
|
|
8
|
+
case since version 2 -- it now has its own API, but retains the old name. Yes,
|
|
9
|
+
that does mean that Redcarpet 2 is not backwards-compatible with the 1.X
|
|
10
|
+
versions.
|
|
8
11
|
|
|
9
|
-
|
|
12
|
+
Redcarpet is powered by the Sundown library, which can be found at
|
|
10
13
|
|
|
11
|
-
|
|
12
|
-
bindings so awesome.
|
|
14
|
+
https://www.github.com/tanoku/sundown
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
You might want to find out more about Sundown to see what makes this Ruby
|
|
17
|
+
library so awesome.
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
* With special thanks to Ryan Tomayko
|
|
19
|
+
This library is written by people
|
|
20
|
+
-------------------------------------------------------
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
Redcarpet 2 has been rewritten from scratch by Vicent Martí (@tanoku). Why
|
|
23
|
+
are you not following me on Twitter?
|
|
23
24
|
|
|
24
|
-
Redcarpet
|
|
25
|
+
Redcarpet would not be possible without the Sundown library and its authors
|
|
26
|
+
(Natacha Porté, Vicent Martí, and its many awesome contributors).
|
|
27
|
+
|
|
28
|
+
You can totally install it as a Gem
|
|
29
|
+
-----------------------------------
|
|
30
|
+
|
|
31
|
+
Redcarpet is readily available as a Ruby gem. It will build some native
|
|
32
|
+
extensions, but the parser is standalone and requires no installed libraries.
|
|
25
33
|
|
|
26
34
|
$ [sudo] gem install redcarpet
|
|
27
35
|
|
|
28
|
-
The Redcarpet source (including
|
|
36
|
+
The Redcarpet source (including Sundown as a submodule) is available at GitHub:
|
|
29
37
|
|
|
30
38
|
$ git clone git://github.com/tanoku/redcarpet.git
|
|
31
39
|
|
|
32
|
-
|
|
33
|
-
|
|
40
|
+
And it's like *really* simple to use
|
|
41
|
+
------------------------------------
|
|
42
|
+
|
|
43
|
+
The core of the Redcarpet library is the `Redcarpet::Markdown` class. Each
|
|
44
|
+
instance of the class is attached to a `Renderer` object; the Markdown class
|
|
45
|
+
performs parsing of a document and uses the attached renderer to generate
|
|
46
|
+
output.
|
|
47
|
+
|
|
48
|
+
The `Markdown` object is encouraged to be instantiated once with the required
|
|
49
|
+
settings, and reused between parses.
|
|
50
|
+
|
|
51
|
+
Markdown.new(renderer, extensions={})
|
|
52
|
+
|
|
53
|
+
Initializes a Markdown parser
|
|
54
|
+
|
|
55
|
+
renderer - a renderer object, inheriting from Redcarpet::Render::Base.
|
|
56
|
+
If the given object has not been instantiated, the library
|
|
57
|
+
will do it with default arguments.
|
|
58
|
+
|
|
59
|
+
extensions - a hash containing the Markdown extensions which the parser
|
|
60
|
+
will identify. The following extensions are accepted:
|
|
61
|
+
|
|
62
|
+
:no_intra_emphasis - do not parse emphasis inside of words.
|
|
63
|
+
Strings such as `foo_bar_baz` will not generate `<em>`
|
|
64
|
+
tags.
|
|
65
|
+
|
|
66
|
+
:tables - parse tables, PHP-Markdown style
|
|
67
|
+
|
|
68
|
+
:fenced_code_blocks - parse fenced code blocks, PHP-Markdown
|
|
69
|
+
style .Blocks delimited with 3 or more `~` or backticks
|
|
70
|
+
will be considered as code, without the need to be
|
|
71
|
+
indented. An optional language name may be added at the
|
|
72
|
+
end of the opening fence for the code block
|
|
73
|
+
|
|
74
|
+
:autolink - parse links even when they are not enclosed in
|
|
75
|
+
`<>` characters. Autolinks for the http, https and ftp
|
|
76
|
+
protocols will be automatically detected. Email addresses
|
|
77
|
+
are also handled, and http links without protocol, but
|
|
78
|
+
starting with `www.`
|
|
79
|
+
|
|
80
|
+
:strikethrough - parse strikethrough, PHP-Markdown style
|
|
81
|
+
Two `~` characters mark the start of a strikethrough,
|
|
82
|
+
e.g. `this is ~~good~~ bad`
|
|
83
|
+
|
|
84
|
+
:lax_html_blocks - HTML blocks do not require to be surrounded
|
|
85
|
+
by an empty line as in the Markdown standard.
|
|
86
|
+
|
|
87
|
+
:space_after_headers - A space is always required between the
|
|
88
|
+
hash at the beginning of a header and its name, e.g.
|
|
89
|
+
`#this is my header` would not be a valid header.
|
|
90
|
+
|
|
91
|
+
:superscript - parse superscripts after the `^` character;
|
|
92
|
+
contiguous superscripts are nested together, and complex
|
|
93
|
+
values can be enclosed in parenthesis,
|
|
94
|
+
e.g. `this is the 2^(nd) time`
|
|
95
|
+
|
|
96
|
+
Example:
|
|
97
|
+
|
|
98
|
+
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML,
|
|
99
|
+
:autolink => true, :space_after_headers => true)
|
|
100
|
+
|
|
101
|
+
Rendering with the `Markdown` object is done through `Markdown#render`.
|
|
102
|
+
Unlike in the RedCloth API, the text to render is passed as an argument
|
|
103
|
+
and not stored inside the `Markdown` instance, to encourage reusability.
|
|
104
|
+
|
|
105
|
+
Markdown.render(text)
|
|
106
|
+
|
|
107
|
+
Render a Markdown document with the attached renderer
|
|
108
|
+
|
|
109
|
+
text - a Markdown document
|
|
110
|
+
|
|
111
|
+
Example:
|
|
112
|
+
|
|
113
|
+
markdown.render("This is *bongos*, indeed.")
|
|
114
|
+
#=> "<p>This is <em>bongos</em>, indeed</p>"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
Darling, I packed you a couple renderers for lunch
|
|
118
|
+
--------------------------------------------------
|
|
119
|
+
|
|
120
|
+
Redcarpet comes with two built-in renderers, `Redcarpet::Render::HTML` and
|
|
121
|
+
`Redcarpet::Render::XHTML`, which output HTML and XHTML, respectively. These
|
|
122
|
+
renderers are actually implemented in C, and hence offer a brilliant
|
|
123
|
+
performance, several degrees of magnitude faster than other Ruby Markdown
|
|
124
|
+
solutions.
|
|
125
|
+
|
|
126
|
+
All the rendering flags that previously applied only to HTML output have
|
|
127
|
+
now been moved to the `Render::HTML` class, and may be enabled when
|
|
128
|
+
instantiating the renderer:
|
|
129
|
+
|
|
130
|
+
Render::HTML.new(render_options={})
|
|
131
|
+
|
|
132
|
+
Initializes an HTML renderer. The following flags are available:
|
|
133
|
+
|
|
134
|
+
:filter_html - do not allow any user-inputted HTML in the output
|
|
135
|
+
|
|
136
|
+
:no_images - do not generate any `<img>` tags
|
|
137
|
+
|
|
138
|
+
:no_links - do not generate any `<a>` tags
|
|
139
|
+
|
|
140
|
+
:no_styles - do not generate any `<style>` tags
|
|
141
|
+
|
|
142
|
+
:safe_links_only - only generate links for protocols which are considered safe
|
|
143
|
+
|
|
144
|
+
:with_toc_data - add HTML anchors to each header in the output HTML,
|
|
145
|
+
to allow linking to each section.
|
|
146
|
+
|
|
147
|
+
:hard_wrap - insert HTML `<br>` tags inside on paragraphs where the origin
|
|
148
|
+
Markdown document had newlines (by default, Markdown ignores these
|
|
149
|
+
newlines).
|
|
150
|
+
|
|
151
|
+
:xhtml - output XHTML-conformant tags. This option is always enabled in the
|
|
152
|
+
`Render::XHTML` renderer.
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
Example:
|
|
156
|
+
|
|
157
|
+
rndr = Redcarpet::Render::HTML.new(:no_links => true, :hard_wrap => true)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
The `HTML` renderer has an alternate version, `Redcarpet::Render::HTML_TOC`,
|
|
161
|
+
which will output a table of contents in HTML based on the headers of the
|
|
162
|
+
Markdown document.
|
|
163
|
+
|
|
164
|
+
Furthermore, the abstract base class `Redcarpet::Render::Base` can be used
|
|
165
|
+
to write a custom renderer purely in Ruby, or extending an existing renderer.
|
|
166
|
+
See the following section for more information.
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
And you can even cook your own
|
|
170
|
+
------------------------------
|
|
171
|
+
|
|
172
|
+
Custom renderers are created by inheriting from an existing renderer. The
|
|
173
|
+
built-in renderers, `HTML` and `XHTML` may be extended as such:
|
|
174
|
+
|
|
175
|
+
~~~~~ ruby
|
|
176
|
+
# create a custom renderer that allows highlighting of code blocks
|
|
177
|
+
class HTMLwithAlbino < Redcarpet::Render::HTML
|
|
178
|
+
def block_code(code, language)
|
|
179
|
+
Albino.safe_colorize(code, language)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
markdown = Redcarpet::Markdown.new(HTMLwithAlbino, :fenced_code_blocks => true)
|
|
184
|
+
~~~~~
|
|
185
|
+
|
|
186
|
+
But new renderers can also be created from scratch (see `lib/render_man.rb` for
|
|
187
|
+
an example implementation of a Manpage renderer)
|
|
188
|
+
|
|
189
|
+
~~~~~~ ruby
|
|
190
|
+
class ManPage < Redcarpet::Render::Base
|
|
191
|
+
# you get the drill -- keep going from here
|
|
192
|
+
end
|
|
193
|
+
~~~~~
|
|
194
|
+
|
|
195
|
+
The following instance methods may be implemented by the renderer:
|
|
196
|
+
|
|
197
|
+
# Block-level calls
|
|
198
|
+
# If the return value of the method is `nil`, the block
|
|
199
|
+
# will be skipped.
|
|
200
|
+
# If the method for a document element is not implemented,
|
|
201
|
+
# the block will be skipped.
|
|
202
|
+
#
|
|
203
|
+
# Example:
|
|
204
|
+
#
|
|
205
|
+
# class RenderWithoutCode < Redcarpet::Render::HTML
|
|
206
|
+
# def block_code(code, language)
|
|
207
|
+
# nil
|
|
208
|
+
# end
|
|
209
|
+
# end
|
|
210
|
+
#
|
|
211
|
+
block_code(code, language)
|
|
212
|
+
block_quote(quote)
|
|
213
|
+
block_html(raw_html)
|
|
214
|
+
header(text, header_level)
|
|
215
|
+
hrule()
|
|
216
|
+
list(contents, list_type)
|
|
217
|
+
list_item(text, list_type)
|
|
218
|
+
paragraph(text)
|
|
219
|
+
table(header, body)
|
|
220
|
+
table_row(content)
|
|
221
|
+
table_cell(content, alignment)
|
|
222
|
+
|
|
223
|
+
# Span-level calls
|
|
224
|
+
# A return value of `nil` will not output any data
|
|
225
|
+
# If the method for a document element is not implemented,
|
|
226
|
+
# the contents of the span will be copied verbatim
|
|
227
|
+
autolink(link, link_type)
|
|
228
|
+
codespan(code)
|
|
229
|
+
double_emphasis(text)
|
|
230
|
+
emphasis(text)
|
|
231
|
+
image(link, title, alt_text)
|
|
232
|
+
linebreak()
|
|
233
|
+
link(link, title, content)
|
|
234
|
+
raw_html(raw_html)
|
|
235
|
+
triple_emphasis(text)
|
|
236
|
+
strikethrough(text)
|
|
237
|
+
superscript(text)
|
|
238
|
+
|
|
239
|
+
# Low level rendering
|
|
240
|
+
entity(text)
|
|
241
|
+
normal_text(text)
|
|
242
|
+
|
|
243
|
+
# Header of the document
|
|
244
|
+
# Rendered before any another elements
|
|
245
|
+
doc_header()
|
|
246
|
+
|
|
247
|
+
# Footer of the document
|
|
248
|
+
# Rendered after all the other elements
|
|
249
|
+
doc_footer()
|
|
250
|
+
|
|
251
|
+
# Pre/post-process
|
|
252
|
+
# Special callback: preprocess or postprocess the whole
|
|
253
|
+
# document before or after the rendering process begins
|
|
254
|
+
preprocess(full_document)
|
|
255
|
+
postprocess(full_document)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
Also, now our Pants are much smarter
|
|
259
|
+
------------------------------------
|
|
260
|
+
|
|
261
|
+
Redcarpet 2 comes with a standalone [SmartyPants](
|
|
262
|
+
http://daringfireball.net/projects/smartypants/) implementation. It is fully
|
|
263
|
+
compliant with the original implementation. It is the fastest SmartyPants
|
|
264
|
+
parser there is, with a difference of several orders of magnitude.
|
|
265
|
+
|
|
266
|
+
The SmartyPants parser can be found in `Redcarpet::Render::SmartyPants`. It has
|
|
267
|
+
been implemented as a module, so it can be used standalone or as a mixin.
|
|
268
|
+
|
|
269
|
+
When mixed with a Renderer class, it will override the `postprocess` method
|
|
270
|
+
to perform SmartyPants replacements once the rendering is complete
|
|
271
|
+
|
|
272
|
+
~~~~ ruby
|
|
273
|
+
# Mixin
|
|
274
|
+
class HTMLWithPants < Redcarpet::Render::HTML
|
|
275
|
+
include Redcarpet::Render::SmartyPants
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
# Standalone
|
|
279
|
+
Redcarpet::Render::SmartyPants.render("<p>Oh SmartyPants, you're so crazy...</p>")
|
|
280
|
+
~~~~~
|
|
281
|
+
|
|
282
|
+
SmartyPants works on top of already-rendered HTML, and will ignore replacements
|
|
283
|
+
inside the content of HTML tags and inside specific HTML blocks such as
|
|
284
|
+
`<code>` or `<pre>`.
|
|
285
|
+
|
|
286
|
+
What? You really want to mix Markdown renderers?
|
|
287
|
+
------------------------------------------------
|
|
288
|
+
|
|
289
|
+
What a terrible idea! Markdown is already ill-specified enough; if you create
|
|
290
|
+
software that is renderer-independent, the results will be completely unreliable!
|
|
291
|
+
|
|
292
|
+
Each renderer has its own API and its own set of extensions: you should choose one
|
|
293
|
+
(it doesn't have to be Redcarpet, though that would be great!), write your
|
|
294
|
+
software accordingly, and force your users to install it. That's the
|
|
295
|
+
only way to have reliable and predictable Markdown output on your program.
|
|
34
296
|
|
|
35
|
-
|
|
297
|
+
Still, if major forces (let's say, tornadoes or other natural disasters) force you
|
|
298
|
+
to keep a Markdown-compatibility later, Redcarpet also supports this:
|
|
36
299
|
|
|
37
|
-
|
|
38
|
-
require 'redcarpet'
|
|
39
|
-
markdown = Redcarpet.new("Hello World!")
|
|
40
|
-
puts markdown.to_html
|
|
41
|
-
~~~~~~
|
|
300
|
+
require 'redcarpet/compat'
|
|
42
301
|
|
|
43
|
-
|
|
44
|
-
|
|
302
|
+
Requiring the compatibility library will declare a `Markdown` class with the
|
|
303
|
+
classical RedCloth API, e.g.
|
|
45
304
|
|
|
46
|
-
|
|
47
|
-
markdown = Redcarpet.new("Hello World!", :smart, :filter_html)
|
|
48
|
-
~~~~~~
|
|
305
|
+
Markdown.new('this is my text').to_html
|
|
49
306
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
307
|
+
This class renders 100% standards compliant Markdown with 0 extensions. Nada.
|
|
308
|
+
Don't even try to enable extensions with a compatibility layer, because
|
|
309
|
+
that's a maintance nightmare and won't work.
|
|
53
310
|
|
|
54
|
-
|
|
55
|
-
|
|
311
|
+
On a related topic: if your Markdown gem has a `lib/markdown.rb` file that
|
|
312
|
+
monkeypatches the Markdown class, you're a terrible human being. Just saying.
|
|
56
313
|
|
|
57
|
-
|
|
58
|
-
|
|
314
|
+
Boring legal stuff
|
|
315
|
+
------------------
|
|
59
316
|
|
|
60
317
|
Copyright (c) 2011, Vicent Martí
|
|
61
318
|
|
data/Rakefile
CHANGED
|
@@ -70,9 +70,6 @@ task :install => package('.gem') do
|
|
|
70
70
|
sh "gem install #{package('.gem')}"
|
|
71
71
|
end
|
|
72
72
|
|
|
73
|
-
desc 'Update the gemspec'
|
|
74
|
-
task :update_gem => file('redcarpet.gemspec')
|
|
75
|
-
|
|
76
73
|
directory 'pkg/'
|
|
77
74
|
|
|
78
75
|
file package('.gem') => %w[pkg/ redcarpet.gemspec] + $spec.files do |f|
|
|
@@ -91,9 +88,10 @@ def source_version
|
|
|
91
88
|
line.match(/.*VERSION = '(.*)'/)[1]
|
|
92
89
|
end
|
|
93
90
|
|
|
94
|
-
|
|
91
|
+
task :update_gem do
|
|
95
92
|
# read spec file and split out manifest section
|
|
96
|
-
|
|
93
|
+
GEMFILE = 'redcarpet.gemspec'
|
|
94
|
+
spec = File.read(GEMFILE)
|
|
97
95
|
head, manifest, tail = spec.split(" # = MANIFEST =\n")
|
|
98
96
|
head.sub!(/\.version = '.*'/, ".version = '#{source_version}'")
|
|
99
97
|
head.sub!(/\.date = '.*'/, ".date = '#{Date.today.to_s}'")
|
|
@@ -107,26 +105,26 @@ file 'redcarpet.gemspec' => FileList['Rakefile','lib/redcarpet.rb'] do |f|
|
|
|
107
105
|
# piece file back together and write...
|
|
108
106
|
manifest = " s.files = %w[\n#{files}\n ]\n"
|
|
109
107
|
spec = [head,manifest,tail].join(" # = MANIFEST =\n")
|
|
110
|
-
File.open(
|
|
111
|
-
puts "updated #{
|
|
108
|
+
File.open(GEMFILE, 'w') { |io| io.write(spec) }
|
|
109
|
+
puts "updated #{GEMFILE}"
|
|
112
110
|
end
|
|
113
111
|
|
|
114
|
-
desc 'Gather required
|
|
115
|
-
task :gather => '
|
|
112
|
+
desc 'Gather required Sundown sources into extension directory'
|
|
113
|
+
task :gather => 'sundown/src/markdown.h' do |t|
|
|
116
114
|
files =
|
|
117
115
|
FileList[
|
|
118
|
-
'
|
|
119
|
-
'
|
|
120
|
-
'
|
|
121
|
-
'
|
|
116
|
+
'sundown/src/{markdown,buffer,stack,autolink,html_blocks}.h',
|
|
117
|
+
'sundown/src/{markdown,buffer,stack,autolink}.c',
|
|
118
|
+
'sundown/html/{html,html_smartypants,houdini_html_e,houdini_href_e}.c',
|
|
119
|
+
'sundown/html/{html,houdini}.h',
|
|
122
120
|
]
|
|
123
121
|
cp files, 'ext/redcarpet/',
|
|
124
122
|
:preserve => true,
|
|
125
123
|
:verbose => true
|
|
126
124
|
end
|
|
127
125
|
|
|
128
|
-
file '
|
|
129
|
-
abort "The
|
|
126
|
+
file 'sundown/src/markdown.h' do |t|
|
|
127
|
+
abort "The Sundown submodule is required."
|
|
130
128
|
end
|
|
131
129
|
|
|
132
130
|
|
data/bin/redcarpet
CHANGED
data/ext/redcarpet/autolink.c
CHANGED
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
#include <ctype.h>
|
|
23
23
|
|
|
24
24
|
int
|
|
25
|
-
|
|
25
|
+
sd_autolink_issafe(const uint8_t *link, size_t link_len)
|
|
26
26
|
{
|
|
27
|
-
static const size_t valid_uris_count =
|
|
27
|
+
static const size_t valid_uris_count = 5;
|
|
28
28
|
static const char *valid_uris[] = {
|
|
29
|
-
"http://", "https://", "ftp://", "mailto
|
|
29
|
+
"/", "http://", "https://", "ftp://", "mailto:"
|
|
30
30
|
};
|
|
31
31
|
|
|
32
32
|
size_t i;
|
|
@@ -35,7 +35,7 @@ is_safe_link(const char *link, size_t link_len)
|
|
|
35
35
|
size_t len = strlen(valid_uris[i]);
|
|
36
36
|
|
|
37
37
|
if (link_len > len &&
|
|
38
|
-
strncasecmp(link, valid_uris[i], len) == 0 &&
|
|
38
|
+
strncasecmp((char *)link, valid_uris[i], len) == 0 &&
|
|
39
39
|
isalnum(link[len]))
|
|
40
40
|
return 1;
|
|
41
41
|
}
|
|
@@ -44,9 +44,9 @@ is_safe_link(const char *link, size_t link_len)
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
static size_t
|
|
47
|
-
autolink_delim(
|
|
47
|
+
autolink_delim(uint8_t *data, size_t link_end, size_t offset, size_t size)
|
|
48
48
|
{
|
|
49
|
-
|
|
49
|
+
uint8_t cclose, copen = 0;
|
|
50
50
|
size_t i;
|
|
51
51
|
|
|
52
52
|
for (i = 0; i < link_end; ++i)
|
|
@@ -128,7 +128,7 @@ autolink_delim(char *data, size_t link_end, size_t offset, size_t size)
|
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
static size_t
|
|
131
|
-
check_domain(
|
|
131
|
+
check_domain(uint8_t *data, size_t size)
|
|
132
132
|
{
|
|
133
133
|
size_t i, np = 0;
|
|
134
134
|
|
|
@@ -140,21 +140,20 @@ check_domain(char *data, size_t size)
|
|
|
140
140
|
else if (!isalnum(data[i]) && data[i] != '-') break;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
return i;
|
|
143
|
+
/* a valid domain needs to have at least a dot.
|
|
144
|
+
* that's as far as we get */
|
|
145
|
+
return np ? i : 0;
|
|
147
146
|
}
|
|
148
147
|
|
|
149
148
|
size_t
|
|
150
|
-
|
|
149
|
+
sd_autolink__www(size_t *rewind_p, struct buf *link, uint8_t *data, size_t offset, size_t size)
|
|
151
150
|
{
|
|
152
151
|
size_t link_end;
|
|
153
152
|
|
|
154
153
|
if (offset > 0 && !ispunct(data[-1]) && !isspace(data[-1]))
|
|
155
154
|
return 0;
|
|
156
155
|
|
|
157
|
-
if (size < 4 || memcmp(data, "www.",
|
|
156
|
+
if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)
|
|
158
157
|
return 0;
|
|
159
158
|
|
|
160
159
|
link_end = check_domain(data, size);
|
|
@@ -177,13 +176,13 @@ ups_autolink__www(size_t *rewind_p, struct buf *link, char *data, size_t offset,
|
|
|
177
176
|
}
|
|
178
177
|
|
|
179
178
|
size_t
|
|
180
|
-
|
|
179
|
+
sd_autolink__email(size_t *rewind_p, struct buf *link, uint8_t *data, size_t offset, size_t size)
|
|
181
180
|
{
|
|
182
181
|
size_t link_end, rewind;
|
|
183
182
|
int nb = 0, np = 0;
|
|
184
183
|
|
|
185
184
|
for (rewind = 0; rewind < offset; ++rewind) {
|
|
186
|
-
|
|
185
|
+
uint8_t c = data[-rewind - 1];
|
|
187
186
|
|
|
188
187
|
if (isalnum(c))
|
|
189
188
|
continue;
|
|
@@ -198,7 +197,7 @@ ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offse
|
|
|
198
197
|
return 0;
|
|
199
198
|
|
|
200
199
|
for (link_end = 0; link_end < size; ++link_end) {
|
|
201
|
-
|
|
200
|
+
uint8_t c = data[link_end];
|
|
202
201
|
|
|
203
202
|
if (isalnum(c))
|
|
204
203
|
continue;
|
|
@@ -226,7 +225,7 @@ ups_autolink__email(size_t *rewind_p, struct buf *link, char *data, size_t offse
|
|
|
226
225
|
}
|
|
227
226
|
|
|
228
227
|
size_t
|
|
229
|
-
|
|
228
|
+
sd_autolink__url(size_t *rewind_p, struct buf *link, uint8_t *data, size_t offset, size_t size)
|
|
230
229
|
{
|
|
231
230
|
size_t link_end, rewind = 0, domain_len;
|
|
232
231
|
|
|
@@ -236,9 +235,9 @@ ups_autolink__url(size_t *rewind_p, struct buf *link, char *data, size_t offset,
|
|
|
236
235
|
while (rewind < offset && isalpha(data[-rewind - 1]))
|
|
237
236
|
rewind++;
|
|
238
237
|
|
|
239
|
-
if (!
|
|
238
|
+
if (!sd_autolink_issafe(data - rewind, size + rewind))
|
|
240
239
|
return 0;
|
|
241
|
-
link_end =
|
|
240
|
+
link_end = strlen("://");
|
|
242
241
|
|
|
243
242
|
domain_len = check_domain(data + link_end, size - link_end);
|
|
244
243
|
if (domain_len == 0)
|
data/ext/redcarpet/autolink.h
CHANGED
|
@@ -15,18 +15,21 @@
|
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
17
|
#ifndef UPSKIRT_AUTOLINK_H
|
|
18
|
-
#define
|
|
18
|
+
#define UPSKIRT_AUTOLINK_H
|
|
19
19
|
|
|
20
20
|
#include "buffer.h"
|
|
21
21
|
|
|
22
|
+
extern int
|
|
23
|
+
sd_autolink_issafe(const uint8_t *link, size_t link_len);
|
|
24
|
+
|
|
22
25
|
extern size_t
|
|
23
|
-
|
|
26
|
+
sd_autolink__www(size_t *rewind_p, struct buf *link, uint8_t *data, size_t offset, size_t size);
|
|
24
27
|
|
|
25
28
|
extern size_t
|
|
26
|
-
|
|
29
|
+
sd_autolink__email(size_t *rewind_p, struct buf *link, uint8_t *data, size_t offset, size_t size);
|
|
27
30
|
|
|
28
31
|
extern size_t
|
|
29
|
-
|
|
32
|
+
sd_autolink__url(size_t *rewind_p, struct buf *link, uint8_t *data, size_t offset, size_t size);
|
|
30
33
|
|
|
31
34
|
#endif
|
|
32
35
|
|