redcarpet 1.0.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/COPYING +14 -0
- data/README.markdown +38 -0
- data/Rakefile +135 -0
- data/bin/redcarpet +13 -0
- data/ext/array.c +300 -0
- data/ext/array.h +148 -0
- data/ext/buffer.c +318 -0
- data/ext/buffer.h +147 -0
- data/ext/extconf.rb +4 -0
- data/ext/markdown.c +1590 -0
- data/ext/markdown.h +124 -0
- data/ext/redcarpet.c +86 -0
- data/ext/render.c +499 -0
- data/lib/markdown.rb +1 -0
- data/lib/redcarpet.rb +72 -0
- data/redcarpet.gemspec +39 -0
- data/test/benchmark.rb +56 -0
- data/test/benchmark.txt +306 -0
- data/test/markdown_test.rb +176 -0
- data/test/redcarpet_test.rb +106 -0
- metadata +88 -0
data/ext/markdown.h
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
/* markdown.h - generic markdown parser */
|
2
|
+
|
3
|
+
/*
|
4
|
+
* Copyright (c) 2009, Natacha Porté
|
5
|
+
*
|
6
|
+
* Permission to use, copy, modify, and distribute this software for any
|
7
|
+
* purpose with or without fee is hereby granted, provided that the above
|
8
|
+
* copyright notice and this permission notice appear in all copies.
|
9
|
+
*
|
10
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
11
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
12
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
13
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
14
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
15
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
16
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
17
|
+
*/
|
18
|
+
|
19
|
+
#ifndef LITHIUM_MARKDOWN_H
|
20
|
+
#define LITHIUM_MARKDOWN_H
|
21
|
+
|
22
|
+
#include "buffer.h"
|
23
|
+
|
24
|
+
/* Require a blank newline after HTML tags */
|
25
|
+
#define UPSKIRT_NEWLINE_AFTER_TAGS
|
26
|
+
|
27
|
+
/********************
|
28
|
+
* TYPE DEFINITIONS *
|
29
|
+
********************/
|
30
|
+
|
31
|
+
/* mkd_autolink • type of autolink */
|
32
|
+
enum mkd_autolink {
|
33
|
+
MKDA_NOT_AUTOLINK, /* used internally when it is not an autolink*/
|
34
|
+
MKDA_NORMAL, /* normal http/http/ftp/etc link */
|
35
|
+
MKDA_EXPLICIT_EMAIL, /* e-mail link with explit mailto: */
|
36
|
+
MKDA_IMPLICIT_EMAIL /* e-mail link without mailto: */
|
37
|
+
};
|
38
|
+
|
39
|
+
struct mkd_renderopt {
|
40
|
+
void *opaque;
|
41
|
+
unsigned int flags;
|
42
|
+
};
|
43
|
+
|
44
|
+
struct mkd_parseropt {
|
45
|
+
unsigned int flags;
|
46
|
+
int recursion_depth;
|
47
|
+
};
|
48
|
+
|
49
|
+
/* mkd_renderer • functions for rendering parsed data */
|
50
|
+
struct mkd_renderer {
|
51
|
+
/* block level callbacks - NULL skips the block */
|
52
|
+
void (*blockcode)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
|
53
|
+
void (*blockquote)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
|
54
|
+
void (*blockhtml)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
|
55
|
+
void (*header)(struct buf *ob, struct buf *text, int level, struct mkd_renderopt *opaque);
|
56
|
+
void (*hrule)(struct buf *ob, struct mkd_renderopt *opaque);
|
57
|
+
void (*list)(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *opaque);
|
58
|
+
void (*listitem)(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *opaque);
|
59
|
+
void (*paragraph)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
|
60
|
+
|
61
|
+
/* span level callbacks - NULL or return 0 prints the span verbatim */
|
62
|
+
int (*autolink)(struct buf *ob, struct buf *link, enum mkd_autolink type, struct mkd_renderopt *opaque);
|
63
|
+
int (*codespan)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
|
64
|
+
int (*double_emphasis)(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *opaque);
|
65
|
+
int (*emphasis)(struct buf *ob, struct buf *text, char c,struct mkd_renderopt *opaque);
|
66
|
+
int (*image)(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, struct mkd_renderopt *opaque);
|
67
|
+
int (*linebreak)(struct buf *ob, struct mkd_renderopt *opaque);
|
68
|
+
int (*link)(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, struct mkd_renderopt *opaque);
|
69
|
+
int (*raw_html_tag)(struct buf *ob, struct buf *tag, struct mkd_renderopt *opaque);
|
70
|
+
int (*triple_emphasis)(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *opaque);
|
71
|
+
|
72
|
+
/* low level callbacks - NULL copies input directly into the output */
|
73
|
+
void (*entity)(struct buf *ob, struct buf *entity, struct mkd_renderopt *opaque);
|
74
|
+
void (*normal_text)(struct buf *ob, struct buf *text, struct mkd_renderopt *opaque);
|
75
|
+
|
76
|
+
/* renderer data */
|
77
|
+
const char *emph_chars; /* chars that trigger emphasis rendering */
|
78
|
+
|
79
|
+
struct mkd_renderopt render_options;
|
80
|
+
struct mkd_parseropt parser_options;
|
81
|
+
};
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
/*********
|
86
|
+
* FLAGS *
|
87
|
+
*********/
|
88
|
+
|
89
|
+
/* list/listitem flags */
|
90
|
+
#define MKD_LIST_ORDERED 1
|
91
|
+
#define MKD_LI_BLOCK 2 /* <li> containing block data */
|
92
|
+
|
93
|
+
typedef enum {
|
94
|
+
RENDER_SKIP_HTML = (1 << 0),
|
95
|
+
RENDER_SKIP_STYLE = (1 << 1),
|
96
|
+
RENDER_SKIP_IMAGES = (1 << 2),
|
97
|
+
RENDER_SKIP_LINKS = (1 << 3),
|
98
|
+
RENDER_SMARTYPANTS = (1 << 4),
|
99
|
+
RENDER_EXPAND_TABS = (1 << 5),
|
100
|
+
RENDER_AUTOLINK = (1 << 6),
|
101
|
+
RENDER_SAFELINK = (1 << 7),
|
102
|
+
} render_mode;
|
103
|
+
|
104
|
+
typedef enum {
|
105
|
+
PARSER_STRICT = (1 << 0),
|
106
|
+
} parser_mode;
|
107
|
+
|
108
|
+
/**********************
|
109
|
+
* EXPORTED FUNCTIONS *
|
110
|
+
**********************/
|
111
|
+
|
112
|
+
/* markdown • parses the input buffer and renders it into the output buffer */
|
113
|
+
void
|
114
|
+
markdown(struct buf *ob, struct buf *ib, const struct mkd_renderer *rndr);
|
115
|
+
|
116
|
+
void
|
117
|
+
init_renderer(struct mkd_renderer *renderer,
|
118
|
+
unsigned int render_flags, void *opaque,
|
119
|
+
unsigned int parser_flags, int recursion_depth);
|
120
|
+
|
121
|
+
|
122
|
+
#endif /* ndef LITHIUM_MARKDOWN_H */
|
123
|
+
|
124
|
+
/* vim: set filetype=c: */
|
data/ext/redcarpet.c
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include "ruby.h"
|
3
|
+
|
4
|
+
#include "markdown.h"
|
5
|
+
|
6
|
+
static VALUE rb_cRedcarpet;
|
7
|
+
|
8
|
+
static void rb_redcarpet__setup_render(VALUE ruby_obj, struct mkd_renderer *rnd)
|
9
|
+
{
|
10
|
+
unsigned int render_flags = RENDER_EXPAND_TABS;
|
11
|
+
unsigned int parser_flags = 0;
|
12
|
+
|
13
|
+
/* smart */
|
14
|
+
if (rb_funcall(ruby_obj, rb_intern("smart"), 0) == Qtrue)
|
15
|
+
render_flags |= RENDER_SMARTYPANTS;
|
16
|
+
|
17
|
+
/* filter_html */
|
18
|
+
if (rb_funcall(ruby_obj, rb_intern("filter_html"), 0) == Qtrue)
|
19
|
+
render_flags |= RENDER_SKIP_HTML;
|
20
|
+
|
21
|
+
/* no_image */
|
22
|
+
if (rb_funcall(ruby_obj, rb_intern("no_image"), 0) == Qtrue)
|
23
|
+
render_flags |= RENDER_SKIP_IMAGES;
|
24
|
+
|
25
|
+
/* no_links */
|
26
|
+
if (rb_funcall(ruby_obj, rb_intern("no_links"), 0) == Qtrue)
|
27
|
+
render_flags |= RENDER_SKIP_LINKS;
|
28
|
+
|
29
|
+
/* filter_style */
|
30
|
+
if (rb_funcall(ruby_obj, rb_intern("filter_styles"), 0) == Qtrue)
|
31
|
+
render_flags |= RENDER_SKIP_STYLE;
|
32
|
+
|
33
|
+
/* autolink */
|
34
|
+
if (rb_funcall(ruby_obj, rb_intern("autolink"), 0) == Qtrue)
|
35
|
+
render_flags |= RENDER_AUTOLINK;
|
36
|
+
|
37
|
+
/* safelink */
|
38
|
+
if (rb_funcall(ruby_obj, rb_intern("safelink"), 0) == Qtrue)
|
39
|
+
render_flags |= RENDER_SAFELINK;
|
40
|
+
|
41
|
+
|
42
|
+
/* parser - strict */
|
43
|
+
if (rb_funcall(ruby_obj, rb_intern("strict"), 0) == Qtrue)
|
44
|
+
parser_flags |= PARSER_STRICT;
|
45
|
+
|
46
|
+
|
47
|
+
init_renderer(rnd, render_flags, NULL, parser_flags, 16);
|
48
|
+
}
|
49
|
+
|
50
|
+
static VALUE rb_redcarpet_to_html(int argc, VALUE *argv, VALUE self)
|
51
|
+
{
|
52
|
+
VALUE text = rb_funcall(self, rb_intern("text"), 0);
|
53
|
+
VALUE result;
|
54
|
+
|
55
|
+
struct buf input_buf, *output_buf;
|
56
|
+
struct mkd_renderer redcarpet_render;
|
57
|
+
|
58
|
+
Check_Type(text, T_STRING);
|
59
|
+
|
60
|
+
memset(&input_buf, 0x0, sizeof(struct buf));
|
61
|
+
input_buf.data = RSTRING_PTR(text);
|
62
|
+
input_buf.size = RSTRING_LEN(text);
|
63
|
+
|
64
|
+
output_buf = bufnew(64);
|
65
|
+
|
66
|
+
rb_redcarpet__setup_render(self, &redcarpet_render);
|
67
|
+
markdown(output_buf, &input_buf, &redcarpet_render);
|
68
|
+
|
69
|
+
result = rb_str_new(output_buf->data, output_buf->size);
|
70
|
+
bufrelease(output_buf);
|
71
|
+
|
72
|
+
/* force the input encoding */
|
73
|
+
if (rb_respond_to(text, rb_intern("encoding"))) {
|
74
|
+
VALUE encoding = rb_funcall(text, rb_intern("encoding"), 0);
|
75
|
+
rb_funcall(result, rb_intern("force_encoding"), 1, encoding);
|
76
|
+
}
|
77
|
+
|
78
|
+
return result;
|
79
|
+
}
|
80
|
+
|
81
|
+
void Init_redcarpet()
|
82
|
+
{
|
83
|
+
rb_cRedcarpet = rb_define_class("Redcarpet", rb_cObject);
|
84
|
+
rb_define_method(rb_cRedcarpet, "to_html", rb_redcarpet_to_html, -1);
|
85
|
+
}
|
86
|
+
|
data/ext/render.c
ADDED
@@ -0,0 +1,499 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (c) 2011, Vicent Marti
|
3
|
+
* Copyright (c) 2009, Natacha Porté
|
4
|
+
*
|
5
|
+
* Permission to use, copy, modify, and distribute this software for any
|
6
|
+
* purpose with or without fee is hereby granted, provided that the above
|
7
|
+
* copyright notice and this permission notice appear in all copies.
|
8
|
+
*
|
9
|
+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
10
|
+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
11
|
+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
12
|
+
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
13
|
+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
14
|
+
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
15
|
+
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
16
|
+
*/
|
17
|
+
|
18
|
+
#include "markdown.h"
|
19
|
+
|
20
|
+
#include <strings.h>
|
21
|
+
#include <stdlib.h>
|
22
|
+
|
23
|
+
|
24
|
+
static int is_safe_link(const char *link, size_t link_len)
|
25
|
+
{
|
26
|
+
static const size_t valid_uris_count = 4;
|
27
|
+
static const char *valid_uris[] = {
|
28
|
+
"http:", "https:", "ftp:", "mailto:"
|
29
|
+
};
|
30
|
+
|
31
|
+
size_t i;
|
32
|
+
|
33
|
+
for (i = 0; i < valid_uris_count; ++i) {
|
34
|
+
size_t len = strlen(valid_uris[i]);
|
35
|
+
|
36
|
+
if (link_len > len && memcmp(link, valid_uris[i], len) == 0)
|
37
|
+
return 1;
|
38
|
+
}
|
39
|
+
|
40
|
+
return 0;
|
41
|
+
}
|
42
|
+
|
43
|
+
static int
|
44
|
+
put_scaped_char(struct buf *ob, char c)
|
45
|
+
{
|
46
|
+
switch (c) {
|
47
|
+
case '<': BUFPUTSL(ob, "<"); return 1;
|
48
|
+
case '>': BUFPUTSL(ob, ">"); return 1;
|
49
|
+
case '&': BUFPUTSL(ob, "&"); return 1;
|
50
|
+
case '"': BUFPUTSL(ob, """); return 1;
|
51
|
+
default: return 0;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
/* lus_attr_escape • copy the buffer entity-escaping '<', '>', '&' and '"' */
|
57
|
+
static void
|
58
|
+
lus_attr_escape(struct buf *ob, const char *src, size_t size) {
|
59
|
+
size_t i = 0, org;
|
60
|
+
while (i < size) {
|
61
|
+
/* copying directly unescaped characters */
|
62
|
+
org = i;
|
63
|
+
while (i < size && src[i] != '<' && src[i] != '>'
|
64
|
+
&& src[i] != '&' && src[i] != '"')
|
65
|
+
i += 1;
|
66
|
+
if (i > org) bufput(ob, src + org, i - org);
|
67
|
+
|
68
|
+
/* escaping */
|
69
|
+
if (i >= size) break;
|
70
|
+
|
71
|
+
put_scaped_char(ob, src[i]);
|
72
|
+
i++;
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
static int
|
77
|
+
is_html_tag(struct buf *tag, const char *tagname)
|
78
|
+
{
|
79
|
+
size_t i;
|
80
|
+
|
81
|
+
for (i = 0; i < tag->size; ++i) {
|
82
|
+
if (tagname[i] == '>')
|
83
|
+
break;
|
84
|
+
|
85
|
+
if (tag->data[i] != tagname[i])
|
86
|
+
return 0;
|
87
|
+
}
|
88
|
+
|
89
|
+
return (i == tag->size || isspace(tag->data[i]) || tag->data[i] == '>');
|
90
|
+
}
|
91
|
+
|
92
|
+
/********************
|
93
|
+
* GENERIC RENDERER *
|
94
|
+
********************/
|
95
|
+
|
96
|
+
static void
|
97
|
+
rndr_autolink2(struct buf *ob, const char *link, size_t link_size, enum mkd_autolink type)
|
98
|
+
{
|
99
|
+
BUFPUTSL(ob, "<a href=\"");
|
100
|
+
if (type == MKDA_IMPLICIT_EMAIL) BUFPUTSL(ob, "mailto:");
|
101
|
+
lus_attr_escape(ob, link, link_size);
|
102
|
+
BUFPUTSL(ob, "\">");
|
103
|
+
if (type == MKDA_EXPLICIT_EMAIL && link_size > 7)
|
104
|
+
lus_attr_escape(ob, link + 7, link_size - 7);
|
105
|
+
else lus_attr_escape(ob, link, link_size);
|
106
|
+
BUFPUTSL(ob, "</a>");
|
107
|
+
}
|
108
|
+
|
109
|
+
static int
|
110
|
+
rndr_autolink(struct buf *ob, struct buf *link, enum mkd_autolink type, struct mkd_renderopt *options)
|
111
|
+
{
|
112
|
+
if (!link || !link->size)
|
113
|
+
return 0;
|
114
|
+
|
115
|
+
if ((options->flags & RENDER_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
|
116
|
+
return 0;
|
117
|
+
|
118
|
+
rndr_autolink2(ob, link->data, link->size, type);
|
119
|
+
return 1;
|
120
|
+
}
|
121
|
+
|
122
|
+
static void
|
123
|
+
rndr_blockcode(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
|
124
|
+
if (ob->size) bufputc(ob, '\n');
|
125
|
+
BUFPUTSL(ob, "<pre><code>");
|
126
|
+
if (text) lus_attr_escape(ob, text->data, text->size);
|
127
|
+
BUFPUTSL(ob, "</code></pre>\n");
|
128
|
+
}
|
129
|
+
|
130
|
+
static void
|
131
|
+
rndr_blockquote(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
|
132
|
+
BUFPUTSL(ob, "<blockquote>\n");
|
133
|
+
if (text) bufput(ob, text->data, text->size);
|
134
|
+
BUFPUTSL(ob, "</blockquote>");
|
135
|
+
}
|
136
|
+
|
137
|
+
static int
|
138
|
+
rndr_codespan(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
|
139
|
+
BUFPUTSL(ob, "<code>");
|
140
|
+
if (text) lus_attr_escape(ob, text->data, text->size);
|
141
|
+
BUFPUTSL(ob, "</code>");
|
142
|
+
return 1;
|
143
|
+
}
|
144
|
+
|
145
|
+
static int
|
146
|
+
rndr_double_emphasis(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *options) {
|
147
|
+
if (!text || !text->size) return 0;
|
148
|
+
BUFPUTSL(ob, "<strong>");
|
149
|
+
bufput(ob, text->data, text->size);
|
150
|
+
BUFPUTSL(ob, "</strong>");
|
151
|
+
return 1;
|
152
|
+
}
|
153
|
+
|
154
|
+
static int
|
155
|
+
rndr_emphasis(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *options) {
|
156
|
+
if (!text || !text->size) return 0;
|
157
|
+
BUFPUTSL(ob, "<em>");
|
158
|
+
if (text) bufput(ob, text->data, text->size);
|
159
|
+
BUFPUTSL(ob, "</em>");
|
160
|
+
return 1;
|
161
|
+
}
|
162
|
+
|
163
|
+
static void
|
164
|
+
rndr_header(struct buf *ob, struct buf *text, int level, struct mkd_renderopt *options) {
|
165
|
+
if (ob->size) bufputc(ob, '\n');
|
166
|
+
bufprintf(ob, "<h%d>", level);
|
167
|
+
if (text) bufput(ob, text->data, text->size);
|
168
|
+
bufprintf(ob, "</h%d>\n", level);
|
169
|
+
}
|
170
|
+
|
171
|
+
static int
|
172
|
+
rndr_link(struct buf *ob, struct buf *link, struct buf *title, struct buf *content, struct mkd_renderopt *options)
|
173
|
+
{
|
174
|
+
if ((options->flags & RENDER_SAFELINK) != 0 && !is_safe_link(link->data, link->size))
|
175
|
+
return 0;
|
176
|
+
|
177
|
+
BUFPUTSL(ob, "<a href=\"");
|
178
|
+
if (link && link->size) lus_attr_escape(ob, link->data, link->size);
|
179
|
+
if (title && title->size) {
|
180
|
+
BUFPUTSL(ob, "\" title=\"");
|
181
|
+
lus_attr_escape(ob, title->data, title->size); }
|
182
|
+
BUFPUTSL(ob, "\">");
|
183
|
+
if (content && content->size) bufput(ob, content->data, content->size);
|
184
|
+
BUFPUTSL(ob, "</a>");
|
185
|
+
return 1;
|
186
|
+
}
|
187
|
+
|
188
|
+
static void
|
189
|
+
rndr_list(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *options) {
|
190
|
+
if (ob->size) bufputc(ob, '\n');
|
191
|
+
bufput(ob, flags & MKD_LIST_ORDERED ? "<ol>\n" : "<ul>\n", 5);
|
192
|
+
if (text) bufput(ob, text->data, text->size);
|
193
|
+
bufput(ob, flags & MKD_LIST_ORDERED ? "</ol>\n" : "</ul>\n", 6);
|
194
|
+
}
|
195
|
+
|
196
|
+
static void
|
197
|
+
rndr_listitem(struct buf *ob, struct buf *text, int flags, struct mkd_renderopt *options) {
|
198
|
+
if (ob->size) bufputc(ob, '\n');
|
199
|
+
BUFPUTSL(ob, "<li>\n");
|
200
|
+
if (text) {
|
201
|
+
while (text->size && text->data[text->size - 1] == '\n')
|
202
|
+
text->size -= 1;
|
203
|
+
bufput(ob, text->data, text->size); }
|
204
|
+
BUFPUTSL(ob, "</li>\n");
|
205
|
+
}
|
206
|
+
|
207
|
+
static void
|
208
|
+
rndr_paragraph(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
|
209
|
+
size_t i = 0;
|
210
|
+
|
211
|
+
if (ob->size) bufputc(ob, '\n');
|
212
|
+
|
213
|
+
if (!text || !text->size)
|
214
|
+
return;
|
215
|
+
|
216
|
+
while (i < text->size && isspace(text->data[i])) i++;
|
217
|
+
|
218
|
+
if (i < text->size) {
|
219
|
+
BUFPUTSL(ob, "<p>");
|
220
|
+
bufput(ob, &text->data[i], text->size - i);
|
221
|
+
BUFPUTSL(ob, "</p>\n");
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
static void
|
226
|
+
rndr_raw_block(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
|
227
|
+
size_t org, sz;
|
228
|
+
if (!text) return;
|
229
|
+
sz = text->size;
|
230
|
+
while (sz > 0 && text->data[sz - 1] == '\n') sz -= 1;
|
231
|
+
org = 0;
|
232
|
+
while (org < sz && text->data[org] == '\n') org += 1;
|
233
|
+
if (org >= sz) return;
|
234
|
+
if (ob->size) bufputc(ob, '\n');
|
235
|
+
bufput(ob, text->data + org, sz - org);
|
236
|
+
bufputc(ob, '\n');
|
237
|
+
}
|
238
|
+
|
239
|
+
static int
|
240
|
+
rndr_triple_emphasis(struct buf *ob, struct buf *text, char c, struct mkd_renderopt *options) {
|
241
|
+
if (!text || !text->size) return 0;
|
242
|
+
BUFPUTSL(ob, "<strong><em>");
|
243
|
+
bufput(ob, text->data, text->size);
|
244
|
+
BUFPUTSL(ob, "</em></strong>");
|
245
|
+
return 1;
|
246
|
+
}
|
247
|
+
|
248
|
+
|
249
|
+
/**********************
|
250
|
+
* XHTML 1.0 RENDERER *
|
251
|
+
**********************/
|
252
|
+
|
253
|
+
static void
|
254
|
+
rndr_hrule(struct buf *ob, struct mkd_renderopt *options) {
|
255
|
+
if (ob->size) bufputc(ob, '\n');
|
256
|
+
BUFPUTSL(ob, "<hr />\n");
|
257
|
+
}
|
258
|
+
|
259
|
+
static int
|
260
|
+
rndr_image(struct buf *ob, struct buf *link, struct buf *title, struct buf *alt, struct mkd_renderopt *options) {
|
261
|
+
if (!link || !link->size) return 0;
|
262
|
+
BUFPUTSL(ob, "<img src=\"");
|
263
|
+
lus_attr_escape(ob, link->data, link->size);
|
264
|
+
BUFPUTSL(ob, "\" alt=\"");
|
265
|
+
if (alt && alt->size)
|
266
|
+
lus_attr_escape(ob, alt->data, alt->size);
|
267
|
+
if (title && title->size) {
|
268
|
+
BUFPUTSL(ob, "\" title=\"");
|
269
|
+
lus_attr_escape(ob, title->data, title->size); }
|
270
|
+
BUFPUTSL(ob, "\" />");
|
271
|
+
return 1;
|
272
|
+
}
|
273
|
+
|
274
|
+
static int
|
275
|
+
rndr_linebreak(struct buf *ob, struct mkd_renderopt *options) {
|
276
|
+
BUFPUTSL(ob, "<br />\n");
|
277
|
+
return 1;
|
278
|
+
}
|
279
|
+
|
280
|
+
static int
|
281
|
+
rndr_raw_html(struct buf *ob, struct buf *text, struct mkd_renderopt *options) {
|
282
|
+
int escape_html = 0;
|
283
|
+
|
284
|
+
if (options->flags & RENDER_SKIP_HTML)
|
285
|
+
escape_html = 1;
|
286
|
+
|
287
|
+
else if ((options->flags & RENDER_SKIP_STYLE) != 0 && is_html_tag(text, "<style>"))
|
288
|
+
escape_html = 1;
|
289
|
+
|
290
|
+
else if ((options->flags & RENDER_SKIP_LINKS) != 0 && is_html_tag(text, "<a>"))
|
291
|
+
escape_html = 1;
|
292
|
+
|
293
|
+
else if ((options->flags & RENDER_SKIP_IMAGES) != 0 && is_html_tag(text, "<img>"))
|
294
|
+
escape_html = 1;
|
295
|
+
|
296
|
+
|
297
|
+
if (escape_html)
|
298
|
+
lus_attr_escape(ob, text->data, text->size);
|
299
|
+
else
|
300
|
+
bufput(ob, text->data, text->size);
|
301
|
+
|
302
|
+
return 1;
|
303
|
+
}
|
304
|
+
|
305
|
+
|
306
|
+
static struct {
|
307
|
+
char c0;
|
308
|
+
const char *pattern;
|
309
|
+
const char *entity;
|
310
|
+
int skip;
|
311
|
+
} smartypants_subs[] = {
|
312
|
+
{ '\'', "'s>", "’", 0 },
|
313
|
+
{ '\'', "'t>", "’", 0 },
|
314
|
+
{ '\'', "'re>", "’", 0 },
|
315
|
+
{ '\'', "'ll>", "’", 0 },
|
316
|
+
{ '\'', "'ve>", "’", 0 },
|
317
|
+
{ '\'', "'m>", "’", 0 },
|
318
|
+
{ '\'', "'d>", "’", 0 },
|
319
|
+
{ '-', "--", "—", 1 },
|
320
|
+
{ '-', "<->", "–", 0 },
|
321
|
+
{ '.', "...", "…", 2 },
|
322
|
+
{ '.', ". . .", "…", 4 },
|
323
|
+
{ '(', "(c)", "©", 2 },
|
324
|
+
{ '(', "(r)", "®", 2 },
|
325
|
+
{ '(', "(tm)", "™", 3 },
|
326
|
+
{ '3', "<3/4>", "¾", 2 },
|
327
|
+
{ '3', "<3/4ths>", "¾", 2 },
|
328
|
+
{ '1', "<1/2>", "½", 2 },
|
329
|
+
{ '1', "<1/4>", "¼", 2 },
|
330
|
+
{ '1', "<1/4th>", "¼", 2 },
|
331
|
+
{ '&', "�", 0, 3 },
|
332
|
+
};
|
333
|
+
|
334
|
+
static const char *smartypants_squotes[] = {"‘", "’"};
|
335
|
+
static const char *smartypants_dquotes[] = {"“", "”"};
|
336
|
+
|
337
|
+
#define SUBS_COUNT (sizeof(smartypants_subs) / sizeof(smartypants_subs[0]))
|
338
|
+
|
339
|
+
static int
|
340
|
+
word_boundary(char c)
|
341
|
+
{
|
342
|
+
return isspace(c) || ispunct(c);
|
343
|
+
}
|
344
|
+
|
345
|
+
static int
|
346
|
+
smartypants_cmpsub(const struct buf *buf, size_t start, const char *prefix)
|
347
|
+
{
|
348
|
+
size_t i;
|
349
|
+
|
350
|
+
if (prefix[0] == '<') {
|
351
|
+
if (start == 0 || !word_boundary(buf->data[start - 1]))
|
352
|
+
return 0;
|
353
|
+
|
354
|
+
prefix++;
|
355
|
+
}
|
356
|
+
|
357
|
+
for (i = start; i < buf->size; ++i) {
|
358
|
+
char c, p;
|
359
|
+
|
360
|
+
c = tolower(buf->data[i]);
|
361
|
+
p = *prefix++;
|
362
|
+
|
363
|
+
if (p == 0)
|
364
|
+
return 1;
|
365
|
+
|
366
|
+
if (p == '>')
|
367
|
+
return word_boundary(c);
|
368
|
+
|
369
|
+
if (c != p)
|
370
|
+
return 0;
|
371
|
+
}
|
372
|
+
|
373
|
+
return (*prefix == '>');
|
374
|
+
}
|
375
|
+
|
376
|
+
/* Smarty-pants-style chrome for quotes, -, ellipses, and (r)(c)(tm)
|
377
|
+
*/
|
378
|
+
static void
|
379
|
+
smartypants_and_autolink(struct buf *ob, struct buf *text, unsigned int flags)
|
380
|
+
{
|
381
|
+
size_t i;
|
382
|
+
int open_single = 0, open_double = 0;
|
383
|
+
|
384
|
+
int autolink = (flags & RENDER_AUTOLINK);
|
385
|
+
int smartypants = (flags & RENDER_SMARTYPANTS);
|
386
|
+
|
387
|
+
for (i = 0; i < text->size; ++i) {
|
388
|
+
size_t sub;
|
389
|
+
char c = text->data[i];
|
390
|
+
|
391
|
+
if (autolink && is_safe_link(text->data + i, text->size - i)) {
|
392
|
+
size_t j = i;
|
393
|
+
|
394
|
+
while (j < text->size && !isspace(text->data[j])) j++;
|
395
|
+
|
396
|
+
rndr_autolink2(ob, &text->data[i], j - i, MKDA_NORMAL);
|
397
|
+
i = j;
|
398
|
+
continue;
|
399
|
+
}
|
400
|
+
|
401
|
+
if (smartypants) {
|
402
|
+
for (sub = 0; sub < SUBS_COUNT; ++sub) {
|
403
|
+
if (c == smartypants_subs[sub].c0 &&
|
404
|
+
smartypants_cmpsub(text, i, smartypants_subs[sub].pattern)) {
|
405
|
+
|
406
|
+
if (smartypants_subs[sub].entity)
|
407
|
+
bufputs(ob, smartypants_subs[sub].entity);
|
408
|
+
|
409
|
+
i += smartypants_subs[sub].skip;
|
410
|
+
break;
|
411
|
+
}
|
412
|
+
}
|
413
|
+
|
414
|
+
if (sub < SUBS_COUNT)
|
415
|
+
continue;
|
416
|
+
|
417
|
+
switch (c) {
|
418
|
+
case '\"':
|
419
|
+
bufputs(ob, smartypants_dquotes[open_double]);
|
420
|
+
open_double = !open_double;
|
421
|
+
continue;
|
422
|
+
|
423
|
+
case '\'':
|
424
|
+
bufputs(ob, smartypants_squotes[open_single]);
|
425
|
+
open_single = !open_single;
|
426
|
+
continue;
|
427
|
+
|
428
|
+
/* TODO: advanced quotes like `` and '' */
|
429
|
+
}
|
430
|
+
}
|
431
|
+
|
432
|
+
if (!put_scaped_char(ob, c))
|
433
|
+
bufputc(ob, c);
|
434
|
+
}
|
435
|
+
}
|
436
|
+
|
437
|
+
static void
|
438
|
+
rndr_normal_text(struct buf *ob, struct buf *text, struct mkd_renderopt *options)
|
439
|
+
{
|
440
|
+
if (!text)
|
441
|
+
return;
|
442
|
+
|
443
|
+
if (options->flags & RENDER_SMARTYPANTS || options->flags & RENDER_AUTOLINK)
|
444
|
+
smartypants_and_autolink(ob, text, options->flags);
|
445
|
+
else
|
446
|
+
lus_attr_escape(ob, text->data, text->size);
|
447
|
+
}
|
448
|
+
|
449
|
+
void
|
450
|
+
init_renderer(struct mkd_renderer *renderer,
|
451
|
+
unsigned int render_flags, void *opaque,
|
452
|
+
unsigned int parser_flags, int recursion_depth)
|
453
|
+
{
|
454
|
+
static const struct mkd_renderer renderer_default = {
|
455
|
+
rndr_blockcode,
|
456
|
+
rndr_blockquote,
|
457
|
+
rndr_raw_block,
|
458
|
+
rndr_header,
|
459
|
+
rndr_hrule,
|
460
|
+
rndr_list,
|
461
|
+
rndr_listitem,
|
462
|
+
rndr_paragraph,
|
463
|
+
|
464
|
+
rndr_autolink,
|
465
|
+
rndr_codespan,
|
466
|
+
rndr_double_emphasis,
|
467
|
+
rndr_emphasis,
|
468
|
+
rndr_image,
|
469
|
+
rndr_linebreak,
|
470
|
+
rndr_link,
|
471
|
+
rndr_raw_html,
|
472
|
+
rndr_triple_emphasis,
|
473
|
+
|
474
|
+
NULL,
|
475
|
+
rndr_normal_text,
|
476
|
+
|
477
|
+
"*_",
|
478
|
+
|
479
|
+
{ NULL, 0 },
|
480
|
+
{ 0, 0 },
|
481
|
+
};
|
482
|
+
|
483
|
+
memcpy(renderer, &renderer_default, sizeof(struct mkd_renderer));
|
484
|
+
|
485
|
+
if (render_flags & RENDER_SKIP_IMAGES)
|
486
|
+
renderer->image = NULL;
|
487
|
+
|
488
|
+
if (render_flags & RENDER_SKIP_LINKS) {
|
489
|
+
renderer->link = NULL;
|
490
|
+
renderer->autolink = NULL;
|
491
|
+
}
|
492
|
+
|
493
|
+
renderer->parser_options.recursion_depth = recursion_depth;
|
494
|
+
renderer->parser_options.flags = parser_flags;
|
495
|
+
|
496
|
+
renderer->render_options.opaque = opaque;
|
497
|
+
renderer->render_options.flags = render_flags;
|
498
|
+
}
|
499
|
+
|