greenmat 3.2.2.2 → 3.5.1.2
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.
- checksums.yaml +5 -5
- data/.travis.yml +21 -8
- data/CHANGELOG.md +24 -0
- data/COPYING +17 -11
- data/Gemfile +2 -2
- data/README.md +19 -13
- data/Rakefile +1 -0
- data/bin/greenmat +4 -40
- data/ext/greenmat/autolink.c +24 -12
- data/ext/greenmat/buffer.c +24 -17
- data/ext/greenmat/buffer.h +18 -13
- data/ext/greenmat/gm_markdown.c +41 -14
- data/ext/greenmat/gm_render.c +68 -21
- data/ext/greenmat/greenmat.h +22 -0
- data/ext/greenmat/houdini.h +22 -0
- data/ext/greenmat/houdini_href_e.c +27 -11
- data/ext/greenmat/houdini_html_e.c +22 -1
- data/ext/greenmat/html.c +177 -47
- data/ext/greenmat/html.h +19 -14
- data/ext/greenmat/html_smartypants.c +47 -20
- data/ext/greenmat/markdown.c +181 -30
- data/ext/greenmat/markdown.h +20 -16
- data/ext/greenmat/stack.c +22 -0
- data/ext/greenmat/stack.h +22 -0
- data/greenmat.gemspec +4 -3
- data/lib/greenmat.rb +1 -1
- data/lib/greenmat/cli.rb +86 -0
- data/lib/greenmat/compat.rb +0 -5
- data/lib/greenmat/render_strip.rb +13 -1
- data/lib/greenmat/version.rb +1 -1
- data/spec/greenmat/markdown_spec.rb +166 -0
- data/test/custom_render_test.rb +41 -2
- data/test/greenmat_bin_test.rb +80 -0
- data/test/greenmat_compat_test.rb +6 -6
- data/test/html5_test.rb +60 -38
- data/test/html_render_test.rb +162 -128
- data/test/html_toc_render_test.rb +74 -11
- data/test/markdown_test.rb +258 -182
- data/test/safe_render_test.rb +5 -6
- data/test/smarty_html_test.rb +19 -13
- data/test/smarty_pants_test.rb +10 -0
- data/test/stripdown_render_test.rb +38 -9
- data/test/test_helper.rb +30 -9
- metadata +29 -13
data/ext/greenmat/gm_render.c
CHANGED
@@ -1,17 +1,23 @@
|
|
1
1
|
/*
|
2
|
-
* Copyright (c)
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
3
|
*
|
4
|
-
* Permission
|
5
|
-
*
|
6
|
-
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
7
10
|
*
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
14
|
-
* OR
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
15
21
|
*/
|
16
22
|
|
17
23
|
#include "greenmat.h"
|
@@ -34,10 +40,10 @@
|
|
34
40
|
}
|
35
41
|
|
36
42
|
extern VALUE rb_mGreenmat;
|
43
|
+
extern VALUE rb_cRenderHTML_TOC;
|
37
44
|
VALUE rb_mRender;
|
38
45
|
VALUE rb_cRenderBase;
|
39
46
|
VALUE rb_cRenderHTML;
|
40
|
-
VALUE rb_cRenderHTML_TOC;
|
41
47
|
VALUE rb_mSmartyPants;
|
42
48
|
|
43
49
|
#define buf2str(t) ((t) ? rb_enc_str_new((const char*)(t)->data, (t)->size, opt->active_enc) : Qnil)
|
@@ -48,6 +54,12 @@ rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, v
|
|
48
54
|
BLOCK_CALLBACK("block_code", 2, buf2str(text), buf2str(lang));
|
49
55
|
}
|
50
56
|
|
57
|
+
static void
|
58
|
+
rndr_blockcustom(struct buf *ob, const struct buf *text, const struct buf *type, void *opaque)
|
59
|
+
{
|
60
|
+
BLOCK_CALLBACK("block_custom", 2, buf2str(text), buf2str(type));
|
61
|
+
}
|
62
|
+
|
51
63
|
static void
|
52
64
|
rndr_blockquote(struct buf *ob, const struct buf *text, void *opaque)
|
53
65
|
{
|
@@ -287,6 +299,7 @@ rndr_link_attributes(struct buf *ob, const struct buf *url, void *opaque)
|
|
287
299
|
|
288
300
|
static struct sd_callbacks rb_greenmat_callbacks = {
|
289
301
|
rndr_blockcode,
|
302
|
+
rndr_blockcustom,
|
290
303
|
rndr_blockquote,
|
291
304
|
rndr_raw_block,
|
292
305
|
rndr_header,
|
@@ -325,6 +338,7 @@ static struct sd_callbacks rb_greenmat_callbacks = {
|
|
325
338
|
|
326
339
|
static const char *rb_greenmat_method_names[] = {
|
327
340
|
"block_code",
|
341
|
+
"block_custom",
|
328
342
|
"block_quote",
|
329
343
|
"block_html",
|
330
344
|
"header",
|
@@ -369,16 +383,22 @@ static void rb_greenmat_rbase_mark(struct rb_greenmat_rndr *rndr)
|
|
369
383
|
rb_gc_mark(rndr->options.link_attributes);
|
370
384
|
}
|
371
385
|
|
386
|
+
static void rndr_deallocate(void *rndr)
|
387
|
+
{
|
388
|
+
xfree(rndr);
|
389
|
+
}
|
390
|
+
|
372
391
|
static VALUE rb_greenmat_rbase_alloc(VALUE klass)
|
373
392
|
{
|
374
393
|
struct rb_greenmat_rndr *rndr = ALLOC(struct rb_greenmat_rndr);
|
375
394
|
memset(rndr, 0x0, sizeof(struct rb_greenmat_rndr));
|
376
|
-
return Data_Wrap_Struct(klass, rb_greenmat_rbase_mark,
|
395
|
+
return Data_Wrap_Struct(klass, rb_greenmat_rbase_mark, rndr_deallocate, rndr);
|
377
396
|
}
|
378
397
|
|
379
398
|
static void rb_greenmat__overload(VALUE self, VALUE base_class)
|
380
399
|
{
|
381
400
|
struct rb_greenmat_rndr *rndr;
|
401
|
+
VALUE options_ivar;
|
382
402
|
|
383
403
|
Data_Get_Struct(self, struct rb_greenmat_rndr, rndr);
|
384
404
|
rndr->options.self = self;
|
@@ -399,6 +419,10 @@ static void rb_greenmat__overload(VALUE self, VALUE base_class)
|
|
399
419
|
dest[i] = source[i];
|
400
420
|
}
|
401
421
|
}
|
422
|
+
|
423
|
+
options_ivar = rb_attr_get(self, rb_intern("@options"));
|
424
|
+
if (options_ivar == Qundef || options_ivar == Qnil)
|
425
|
+
rb_iv_set(self, "@options", rb_hash_new());
|
402
426
|
}
|
403
427
|
|
404
428
|
static VALUE rb_greenmat_rbase_init(VALUE self)
|
@@ -418,6 +442,9 @@ static VALUE rb_greenmat_html_init(int argc, VALUE *argv, VALUE self)
|
|
418
442
|
if (rb_scan_args(argc, argv, "01", &hash) == 1) {
|
419
443
|
Check_Type(hash, T_HASH);
|
420
444
|
|
445
|
+
/* Give access to the passed options through `@options` */
|
446
|
+
rb_iv_set(self, "@options", hash);
|
447
|
+
|
421
448
|
/* escape_html */
|
422
449
|
if (rb_hash_aref(hash, CSTR2SYM("escape_html")) == Qtrue)
|
423
450
|
render_flags |= HTML_ESCAPE;
|
@@ -472,25 +499,45 @@ static VALUE rb_greenmat_html_init(int argc, VALUE *argv, VALUE self)
|
|
472
499
|
static VALUE rb_greenmat_htmltoc_init(int argc, VALUE *argv, VALUE self)
|
473
500
|
{
|
474
501
|
struct rb_greenmat_rndr *rndr;
|
475
|
-
int
|
476
|
-
VALUE hash,
|
502
|
+
unsigned int render_flags = HTML_TOC;
|
503
|
+
VALUE hash, nesting_level = Qnil;
|
477
504
|
|
478
505
|
Data_Get_Struct(self, struct rb_greenmat_rndr, rndr);
|
479
506
|
|
480
507
|
if (rb_scan_args(argc, argv, "01", &hash) == 1) {
|
481
508
|
Check_Type(hash, T_HASH);
|
482
509
|
|
483
|
-
|
510
|
+
/* Give access to the passed options through `@options` */
|
511
|
+
rb_iv_set(self, "@options", hash);
|
484
512
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
513
|
+
/* escape_html */
|
514
|
+
if (rb_hash_aref(hash, CSTR2SYM("escape_html")) == Qtrue)
|
515
|
+
render_flags |= HTML_ESCAPE;
|
516
|
+
|
517
|
+
/* Nesting level */
|
518
|
+
nesting_level = rb_hash_aref(hash, CSTR2SYM("nesting_level"));
|
489
519
|
}
|
490
520
|
|
491
|
-
sdhtml_toc_renderer(&rndr->callbacks, (struct html_renderopt *)&rndr->options.html,
|
521
|
+
sdhtml_toc_renderer(&rndr->callbacks, (struct html_renderopt *)&rndr->options.html, render_flags);
|
492
522
|
rb_greenmat__overload(self, rb_cRenderHTML_TOC);
|
493
523
|
|
524
|
+
/* Check whether we are dealing with a Range object by
|
525
|
+
checking whether the object responds to min and max */
|
526
|
+
if (rb_respond_to(nesting_level, rb_intern("min")) &&
|
527
|
+
rb_respond_to(nesting_level, rb_intern("max"))) {
|
528
|
+
int min = NUM2INT(rb_funcall(nesting_level, rb_intern("min"), 0));
|
529
|
+
int max = NUM2INT(rb_funcall(nesting_level, rb_intern("max"), 0));
|
530
|
+
|
531
|
+
rndr->options.html.toc_data.nesting_bounds[0] = min;
|
532
|
+
rndr->options.html.toc_data.nesting_bounds[1] = max;
|
533
|
+
} else if (FIXNUM_P(nesting_level)) {
|
534
|
+
rndr->options.html.toc_data.nesting_bounds[0] = 1;
|
535
|
+
rndr->options.html.toc_data.nesting_bounds[1] = NUM2INT(nesting_level);
|
536
|
+
} else {
|
537
|
+
rndr->options.html.toc_data.nesting_bounds[0] = 1;
|
538
|
+
rndr->options.html.toc_data.nesting_bounds[1] = 6;
|
539
|
+
}
|
540
|
+
|
494
541
|
return Qnil;
|
495
542
|
}
|
496
543
|
|
data/ext/greenmat/greenmat.h
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
10
|
+
*
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
21
|
+
*/
|
22
|
+
|
1
23
|
#ifndef GREENMAT_H__
|
2
24
|
#define GREENMAT_H__
|
3
25
|
|
data/ext/greenmat/houdini.h
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
10
|
+
*
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
21
|
+
*/
|
22
|
+
|
1
23
|
#ifndef HOUDINI_H__
|
2
24
|
#define HOUDINI_H__
|
3
25
|
|
@@ -1,3 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
10
|
+
*
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
21
|
+
*/
|
22
|
+
|
1
23
|
#include <assert.h>
|
2
24
|
#include <stdio.h>
|
3
25
|
#include <string.h>
|
@@ -22,10 +44,10 @@
|
|
22
44
|
* have its native function (i.e. as an URL
|
23
45
|
* component/separator) and hence needs no escaping.
|
24
46
|
*
|
25
|
-
* There
|
26
|
-
*
|
27
|
-
*
|
28
|
-
*
|
47
|
+
* There is one exception: the ' (single quote)
|
48
|
+
* character does not appear in the table.
|
49
|
+
* It is meant to appear in the URL as components,
|
50
|
+
* however it require special HTML-entity escaping
|
29
51
|
* to generate valid HTML markup.
|
30
52
|
*
|
31
53
|
* All other characters will be escaped to %XX.
|
@@ -34,7 +56,7 @@
|
|
34
56
|
static const char HREF_SAFE[] = {
|
35
57
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
36
58
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
37
|
-
0, 1, 0, 1, 1, 1,
|
59
|
+
0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
38
60
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1,
|
39
61
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
40
62
|
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,
|
@@ -73,12 +95,6 @@ houdini_escape_href(struct buf *ob, const uint8_t *src, size_t size)
|
|
73
95
|
break;
|
74
96
|
|
75
97
|
switch (src[i]) {
|
76
|
-
/* amp appears all the time in URLs, but needs
|
77
|
-
* HTML-entity escaping to be inside an href */
|
78
|
-
case '&':
|
79
|
-
BUFPUTSL(ob, "&");
|
80
|
-
break;
|
81
|
-
|
82
98
|
/* the single quote is a valid URL character
|
83
99
|
* according to the standard; it needs HTML
|
84
100
|
* entity escaping too */
|
@@ -1,3 +1,25 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2015, Vicent Marti
|
3
|
+
*
|
4
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
|
+
* of this software and associated documentation files (the "Software"), to deal
|
6
|
+
* in the Software without restriction, including without limitation the rights
|
7
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
* copies of the Software, and to permit persons to whom the Software is
|
9
|
+
* furnished to do so, subject to the following conditions:
|
10
|
+
*
|
11
|
+
* The above copyright notice and this permission notice shall be included in
|
12
|
+
* all copies or substantial portions of the Software.
|
13
|
+
*
|
14
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
15
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
16
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
17
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
18
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
19
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
20
|
+
* THE SOFTWARE.
|
21
|
+
*/
|
22
|
+
|
1
23
|
#include <assert.h>
|
2
24
|
#include <stdio.h>
|
3
25
|
#include <string.h>
|
@@ -80,4 +102,3 @@ houdini_escape_html(struct buf *ob, const uint8_t *src, size_t size)
|
|
80
102
|
{
|
81
103
|
houdini_escape_html0(ob, src, size, 1);
|
82
104
|
}
|
83
|
-
|
data/ext/greenmat/html.c
CHANGED
@@ -1,23 +1,28 @@
|
|
1
1
|
/*
|
2
2
|
* Copyright (c) 2009, Natacha Porté
|
3
|
-
* Copyright (c)
|
3
|
+
* Copyright (c) 2015, Vicent Marti
|
4
4
|
*
|
5
|
-
* Permission
|
6
|
-
*
|
7
|
-
*
|
5
|
+
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
* of this software and associated documentation files (the "Software"), to deal
|
7
|
+
* in the Software without restriction, including without limitation the rights
|
8
|
+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
* copies of the Software, and to permit persons to whom the Software is
|
10
|
+
* furnished to do so, subject to the following conditions:
|
8
11
|
*
|
9
|
-
*
|
10
|
-
*
|
11
|
-
*
|
12
|
-
*
|
13
|
-
*
|
14
|
-
*
|
15
|
-
* OR
|
12
|
+
* The above copyright notice and this permission notice shall be included in
|
13
|
+
* all copies or substantial portions of the Software.
|
14
|
+
*
|
15
|
+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
* THE SOFTWARE.
|
16
22
|
*/
|
17
23
|
|
18
24
|
#include "markdown.h"
|
19
25
|
#include "html.h"
|
20
|
-
#include "ruby.h"
|
21
26
|
#include <string.h>
|
22
27
|
#include <stdlib.h>
|
23
28
|
#include <stdio.h>
|
@@ -115,6 +120,52 @@ rndr_autolink(struct buf *ob, const struct buf *link, enum mkd_autolink type, vo
|
|
115
120
|
return 1;
|
116
121
|
}
|
117
122
|
|
123
|
+
static void
|
124
|
+
rndr_blockcustom(struct buf *ob, const struct buf *text, const struct buf *type, void *opaque)
|
125
|
+
{
|
126
|
+
struct html_renderopt *options = opaque;
|
127
|
+
|
128
|
+
if (ob->size) bufputc(ob, '\n');
|
129
|
+
|
130
|
+
if (type && type->size) {
|
131
|
+
size_t i, cls;
|
132
|
+
if (options->flags & HTML_PRETTIFY) {
|
133
|
+
BUFPUTSL(ob, "<div data-type=\"customblock prettyprint\" data-metadata=\"");
|
134
|
+
cls++;
|
135
|
+
} else {
|
136
|
+
BUFPUTSL(ob, "<div data-type=\"customblock\" data-metadata=\"");
|
137
|
+
}
|
138
|
+
|
139
|
+
for (i = 0, cls = 0; i < type->size; ++i, ++cls) {
|
140
|
+
while (i < type->size && isspace(type->data[i]))
|
141
|
+
i++;
|
142
|
+
|
143
|
+
if (i < type->size) {
|
144
|
+
size_t org = i;
|
145
|
+
while (i < type->size && is_non_space(type->data[i]))
|
146
|
+
i++;
|
147
|
+
|
148
|
+
if (type->data[org] == '.')
|
149
|
+
org++;
|
150
|
+
|
151
|
+
if (cls) bufputc(ob, ' ');
|
152
|
+
escape_html(ob, type->data + org, i - org);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
BUFPUTSL(ob, "\">");
|
157
|
+
} else if (options->flags & HTML_PRETTIFY) {
|
158
|
+
BUFPUTSL(ob, "<div data-type=\"customblock prettyprint\">");
|
159
|
+
} else {
|
160
|
+
BUFPUTSL(ob, "<div data-type=\"customblock\">");
|
161
|
+
}
|
162
|
+
|
163
|
+
if (text)
|
164
|
+
escape_html(ob, text->data, text->size);
|
165
|
+
|
166
|
+
BUFPUTSL(ob, "</div>\n");
|
167
|
+
}
|
168
|
+
|
118
169
|
static void
|
119
170
|
rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, void *opaque)
|
120
171
|
{
|
@@ -125,10 +176,10 @@ rndr_blockcode(struct buf *ob, const struct buf *text, const struct buf *lang, v
|
|
125
176
|
if (lang && lang->size) {
|
126
177
|
size_t i, cls;
|
127
178
|
if (options->flags & HTML_PRETTIFY) {
|
128
|
-
BUFPUTSL(ob, "<pre><code class=\"prettyprint ");
|
179
|
+
BUFPUTSL(ob, "<pre><code class=\"prettyprint\" data-metadata=\"");
|
129
180
|
cls++;
|
130
181
|
} else {
|
131
|
-
BUFPUTSL(ob, "<pre><code
|
182
|
+
BUFPUTSL(ob, "<pre><code data-metadata=\"");
|
132
183
|
}
|
133
184
|
|
134
185
|
for (i = 0, cls = 0; i < lang->size; ++i, ++cls) {
|
@@ -250,8 +301,15 @@ rndr_quote(struct buf *ob, const struct buf *text, void *opaque)
|
|
250
301
|
if (!text || !text->size)
|
251
302
|
return 0;
|
252
303
|
|
304
|
+
struct html_renderopt *options = opaque;
|
305
|
+
|
253
306
|
BUFPUTSL(ob, "<q>");
|
254
|
-
|
307
|
+
|
308
|
+
if (options->flags & HTML_ESCAPE)
|
309
|
+
escape_html(ob, text->data, text->size);
|
310
|
+
else
|
311
|
+
bufput(ob, text->data, text->size);
|
312
|
+
|
255
313
|
BUFPUTSL(ob, "</q>");
|
256
314
|
|
257
315
|
return 1;
|
@@ -265,17 +323,52 @@ rndr_linebreak(struct buf *ob, void *opaque)
|
|
265
323
|
return 1;
|
266
324
|
}
|
267
325
|
|
268
|
-
|
326
|
+
static void
|
327
|
+
rndr_header_anchor(struct buf *out, const struct buf *anchor)
|
269
328
|
{
|
270
|
-
|
271
|
-
VALUE space_regex = rb_reg_new(" +", 2 /* length */, 0);
|
272
|
-
VALUE tags_regex = rb_reg_new("<\\/?[^>]*>", 10, 0);
|
329
|
+
static const char *STRIPPED = " -&+$,/:;=?@\"#{}|^~[]`\\*()%.!'";
|
273
330
|
|
274
|
-
|
275
|
-
|
276
|
-
|
331
|
+
const uint8_t *a = anchor->data;
|
332
|
+
const size_t size = anchor->size;
|
333
|
+
size_t i = 0;
|
334
|
+
int stripped = 0, inserted = 0;
|
277
335
|
|
278
|
-
|
336
|
+
for (; i < size; ++i) {
|
337
|
+
// skip html tags
|
338
|
+
if (a[i] == '<') {
|
339
|
+
while (i < size && a[i] != '>')
|
340
|
+
i++;
|
341
|
+
// skip html entities
|
342
|
+
} else if (a[i] == '&') {
|
343
|
+
while (i < size && a[i] != ';')
|
344
|
+
i++;
|
345
|
+
}
|
346
|
+
// replace non-ascii or invalid characters with dashes
|
347
|
+
else if (!isascii(a[i]) || strchr(STRIPPED, a[i])) {
|
348
|
+
if (inserted && !stripped)
|
349
|
+
bufputc(out, '-');
|
350
|
+
// and do it only once
|
351
|
+
stripped = 1;
|
352
|
+
}
|
353
|
+
else {
|
354
|
+
bufputc(out, tolower(a[i]));
|
355
|
+
stripped = 0;
|
356
|
+
inserted++;
|
357
|
+
}
|
358
|
+
}
|
359
|
+
|
360
|
+
// replace the last dash if there was anything added
|
361
|
+
if (stripped && inserted)
|
362
|
+
out->size--;
|
363
|
+
|
364
|
+
// if anchor found empty, use djb2 hash for it
|
365
|
+
if (!inserted && anchor->size) {
|
366
|
+
unsigned long hash = 5381;
|
367
|
+
for (i = 0; i < size; ++i) {
|
368
|
+
hash = ((hash << 5) + hash) + a[i]; /* h * 33 + c */
|
369
|
+
}
|
370
|
+
bufprintf(out, "part-%lx", hash);
|
371
|
+
}
|
279
372
|
}
|
280
373
|
|
281
374
|
static void
|
@@ -286,8 +379,12 @@ rndr_header(struct buf *ob, const struct buf *text, int level, void *opaque)
|
|
286
379
|
if (ob->size)
|
287
380
|
bufputc(ob, '\n');
|
288
381
|
|
289
|
-
if ((options->flags & HTML_TOC) &&
|
290
|
-
|
382
|
+
if ((options->flags & HTML_TOC) && level >= options->toc_data.nesting_bounds[0] &&
|
383
|
+
level <= options->toc_data.nesting_bounds[1]) {
|
384
|
+
bufprintf(ob, "<h%d id=\"", level);
|
385
|
+
rndr_header_anchor(ob, text);
|
386
|
+
BUFPUTSL(ob, "\">");
|
387
|
+
}
|
291
388
|
else
|
292
389
|
bufprintf(ob, "<h%d>", level);
|
293
390
|
|
@@ -395,15 +492,30 @@ rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)
|
|
395
492
|
static void
|
396
493
|
rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)
|
397
494
|
{
|
398
|
-
size_t org,
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
495
|
+
size_t org, size;
|
496
|
+
struct html_renderopt *options = opaque;
|
497
|
+
|
498
|
+
if (!text)
|
499
|
+
return;
|
500
|
+
|
501
|
+
size = text->size;
|
502
|
+
while (size > 0 && text->data[size - 1] == '\n')
|
503
|
+
size--;
|
504
|
+
|
505
|
+
for (org = 0; org < size && text->data[org] == '\n'; ++org)
|
506
|
+
|
507
|
+
if (org >= size)
|
508
|
+
return;
|
509
|
+
|
510
|
+
/* Remove style tags if the `:no_styles` option is enabled */
|
511
|
+
if ((options->flags & HTML_SKIP_STYLE) != 0 &&
|
512
|
+
sdhtml_is_tag(text->data, size, "style"))
|
513
|
+
return;
|
514
|
+
|
515
|
+
if (ob->size)
|
516
|
+
bufputc(ob, '\n');
|
517
|
+
|
518
|
+
bufput(ob, text->data + org, size - org);
|
407
519
|
bufputc(ob, '\n');
|
408
520
|
}
|
409
521
|
|
@@ -429,10 +541,15 @@ static int
|
|
429
541
|
rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, const struct buf *alt, void *opaque)
|
430
542
|
{
|
431
543
|
struct html_renderopt *options = opaque;
|
432
|
-
|
544
|
+
|
545
|
+
if (link != NULL && (options->flags & HTML_SAFELINK) != 0 && !sd_autolink_issafe(link->data, link->size))
|
546
|
+
return 0;
|
433
547
|
|
434
548
|
BUFPUTSL(ob, "<img src=\"");
|
435
|
-
|
549
|
+
|
550
|
+
if (link && link->size)
|
551
|
+
escape_href(ob, link->data, link->size);
|
552
|
+
|
436
553
|
BUFPUTSL(ob, "\" alt=\"");
|
437
554
|
|
438
555
|
if (alt && alt->size)
|
@@ -440,7 +557,8 @@ rndr_image(struct buf *ob, const struct buf *link, const struct buf *title, cons
|
|
440
557
|
|
441
558
|
if (title && title->size) {
|
442
559
|
BUFPUTSL(ob, "\" title=\"");
|
443
|
-
escape_html(ob, title->data, title->size);
|
560
|
+
escape_html(ob, title->data, title->size);
|
561
|
+
}
|
444
562
|
|
445
563
|
bufputs(ob, USE_XHTML(options) ? "\"/>" : "\">");
|
446
564
|
return 1;
|
@@ -452,7 +570,7 @@ rndr_raw_html(struct buf *ob, const struct buf *text, void *opaque)
|
|
452
570
|
struct html_renderopt *options = opaque;
|
453
571
|
|
454
572
|
/* HTML_ESCAPE overrides SKIP_HTML, SKIP_STYLE, SKIP_LINKS and SKIP_IMAGES
|
455
|
-
|
573
|
+
It doesn't see if there are any valid tags, just escape all of them. */
|
456
574
|
if((options->flags & HTML_ESCAPE) != 0) {
|
457
575
|
escape_html(ob, text->data, text->size);
|
458
576
|
return 1;
|
@@ -591,7 +709,7 @@ rndr_footnote_def(struct buf *ob, const struct buf *text, unsigned int num, void
|
|
591
709
|
bufprintf(ob, "\n<li id=\"fn%d\">\n", num);
|
592
710
|
if (pfound) {
|
593
711
|
bufput(ob, text->data, i);
|
594
|
-
bufprintf(ob, " <a href=\"#fnref%d\"
|
712
|
+
bufprintf(ob, " <a href=\"#fnref%d\">↩</a>", num);
|
595
713
|
bufput(ob, text->data + i, text->size - i);
|
596
714
|
} else if (text) {
|
597
715
|
bufput(ob, text->data, text->size);
|
@@ -602,7 +720,7 @@ rndr_footnote_def(struct buf *ob, const struct buf *text, unsigned int num, void
|
|
602
720
|
static int
|
603
721
|
rndr_footnote_ref(struct buf *ob, unsigned int num, void *opaque)
|
604
722
|
{
|
605
|
-
bufprintf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\"
|
723
|
+
bufprintf(ob, "<sup id=\"fnref%d\"><a href=\"#fn%d\">%d</a></sup>", num, num, num);
|
606
724
|
return 1;
|
607
725
|
}
|
608
726
|
|
@@ -611,7 +729,8 @@ toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
|
|
611
729
|
{
|
612
730
|
struct html_renderopt *options = opaque;
|
613
731
|
|
614
|
-
if (level
|
732
|
+
if (level >= options->toc_data.nesting_bounds[0] &&
|
733
|
+
level <= options->toc_data.nesting_bounds[1]) {
|
615
734
|
/* set the level offset if this is the first header
|
616
735
|
* we're parsing for the document */
|
617
736
|
if (options->toc_data.current_level == 0)
|
@@ -635,8 +754,17 @@ toc_header(struct buf *ob, const struct buf *text, int level, void *opaque)
|
|
635
754
|
BUFPUTSL(ob,"</li>\n<li>\n");
|
636
755
|
}
|
637
756
|
|
638
|
-
bufprintf(ob, "<a href=\"
|
639
|
-
|
757
|
+
bufprintf(ob, "<a href=\"#");
|
758
|
+
rndr_header_anchor(ob, text);
|
759
|
+
BUFPUTSL(ob, "\">");
|
760
|
+
|
761
|
+
if (text) {
|
762
|
+
if (options->flags & HTML_ESCAPE)
|
763
|
+
escape_html(ob, text->data, text->size);
|
764
|
+
else
|
765
|
+
bufput(ob, text->data, text->size);
|
766
|
+
}
|
767
|
+
|
640
768
|
BUFPUTSL(ob, "</a>\n");
|
641
769
|
}
|
642
770
|
}
|
@@ -661,12 +789,13 @@ toc_finalize(struct buf *ob, void *opaque)
|
|
661
789
|
}
|
662
790
|
|
663
791
|
void
|
664
|
-
sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, int
|
792
|
+
sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options, unsigned int render_flags)
|
665
793
|
{
|
666
794
|
static const struct sd_callbacks cb_default = {
|
667
795
|
NULL,
|
668
796
|
NULL,
|
669
797
|
NULL,
|
798
|
+
NULL,
|
670
799
|
toc_header,
|
671
800
|
NULL,
|
672
801
|
NULL,
|
@@ -702,8 +831,7 @@ sdhtml_toc_renderer(struct sd_callbacks *callbacks, struct html_renderopt *optio
|
|
702
831
|
};
|
703
832
|
|
704
833
|
memset(options, 0x0, sizeof(struct html_renderopt));
|
705
|
-
options->flags =
|
706
|
-
options->toc_data.nesting_level = nesting_level;
|
834
|
+
options->flags = render_flags;
|
707
835
|
|
708
836
|
memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
|
709
837
|
}
|
@@ -713,6 +841,7 @@ sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options,
|
|
713
841
|
{
|
714
842
|
static const struct sd_callbacks cb_default = {
|
715
843
|
rndr_blockcode,
|
844
|
+
rndr_blockcustom,
|
716
845
|
rndr_blockquote,
|
717
846
|
rndr_raw_block,
|
718
847
|
rndr_header,
|
@@ -752,7 +881,8 @@ sdhtml_renderer(struct sd_callbacks *callbacks, struct html_renderopt *options,
|
|
752
881
|
/* Prepare the options pointer */
|
753
882
|
memset(options, 0x0, sizeof(struct html_renderopt));
|
754
883
|
options->flags = render_flags;
|
755
|
-
options->toc_data.
|
884
|
+
options->toc_data.nesting_bounds[0] = 1;
|
885
|
+
options->toc_data.nesting_bounds[1] = 6;
|
756
886
|
|
757
887
|
/* Prepare the callbacks */
|
758
888
|
memcpy(callbacks, &cb_default, sizeof(struct sd_callbacks));
|